Dependency properties in Oxygene

How can we create dependency properties in oxygene?

Hi,

something like following:

interface

uses
  System.Collections.Generic,
  System.Linq,
  System.Windows,
  System.Text;

type
  MyStateControl = public class (System.Windows.Controls.Primitives.ButtonBase)
  private
    method GetState: Boolean;
    method SetState(value: Boolean);
  public
    class constructor;
    class var StateProperty: System.Windows.DependencyProperty;
    property State: Boolean read GetState write SetState;
  end;

implementation

class constructor MyStateControl;
begin
  StateProperty := System.Windows.DependencyProperty.Register('State', typeOf(Boolean), typeOf(MyStateControl), new System.Windows.PropertyMetadata(false));
end;

method MyStateControl.SetState(value: Boolean); 
begin
  SetValue(StateProperty, value);
end;

method MyStateControl.GetState: Boolean;
begin
  exit Boolean(GetValue(StateProperty))
end;

end.

Alex,
I would just add some comments:

  • the StateProperty should be defined as readonly.
  • in the last parameter of the Register method, you can specify the address of a method that would be called each time the dependency property value changes.
    Patrick

Hi guys,

- the StateProperty should be defined as readonly.

It was just a general sample of configuring DependencyProperty in Oxygene. It could be State, Text or DataSource property :wink:

- you can specify the address of a method that would be called each time the dependency property value changes.

Right, there is a tricky part (which there are quite many in WPF ;))
Thanks for reminding about it!

If you have some additional logic in the property setter for given DependencyProperty, then under certain circumstances (namely when you trying to set property value in XAML) such setter will not be called. It looks like WPF just assign property value directly, bypassing the setter. For these cases during registering DependencyProperty you can specify additional method which will be called any time when property value being changed:

class constructor MyStateControl;
begin
  StateProperty := System.Windows.DependencyProperty.Register('State', typeOf(Boolean), typeOf(MyStateControl), 
new FrameworkPropertyMetadata(@StatePropertyChanged, BindsTwoWayByDefault := true));
end;

class method MyStateControl.StatePropertyChanged(sender: DependencyObject; e: DependencyPropertyChangedEventArgs);
begin
  var oldVal := Boolean(e.OldValue);
  var newVal := Boolean(e.NewValue);
  if newVal = oldVal then 
    exit;

  // your implementation goes here...
end;

Oh and here is another tricky part, which at one time in the past cost me an hour of investigation -
By default, data binding Mode for dependency properties is the OneWay.
In order to make it TwoWay you need to specify appropriate property of the FrameworkPropertyMetadata instance as shown above

Hope it helps!

@alexk: if I say readonly, it’s not the name, but the field should be defined as read-only.

class var StateProperty: System.Windows.DependencyProperty; readonly;

Hi Patrick,

yeah, I just mean, that my aim was to show a general case (rather than a special case) for using DependencyProperty.
By the way I just pulled out DependencyProperty C# example from here:
http://msdn.microsoft.com/en-us/library/system.windows.dependencyproperty.aspx
and ported it to Oxygene :wink:

Thanx for the help, it works splendid over here :slight_smile:

Gert,
if you define many dependency properties, you could define an aspect to do the work for you.
I have one for dependency properties and another one for attached properties, but they are not documented. If you’re interested, I can post them.
Patrick

Patrick, I can give the dependency aspects to Gert, the aspect lib containing those is probably in the solution he is working on… (we are working on the same project)

Patrick,

If he has the aspects, can you give a little explanation on how to use them?

Gert and Jeroen,
here is the library.

Simple dependency property:

      [Aspect: DependencyPropertyAspect]
      Property MyProperty : PropertyType;

Dependency property with manual initialization (if you want handler for change or special initialization value):

      [Aspect: DependencyPropertyAspect (InitializeField := False)]
      Property MyProperty : PropertyType;

For dependency property, the aspect will:

  • Create the static field, if it does not exist.
  • Define the getter and setter methods.
  • Remove the compiler-generated property field.
  • If InitializeField is True, add code to the class constructor to register the dependency property.

Attached property:

      [Aspect: AttachedPropertyAspect ('Target full type name')]
      Property MyProperty : PropertyType;

The Target full type name must be defined if it is not ‘System.Windows.DependencyObject’.
You can also use InitializeField := False, as for dependency properties.

The aspect does the following:

  • Create the static field, if it does not exist.
  • Define the getter and setter methods.
  • Remove the compiler-generated property field.
  • Remove the compiler-generated property.
  • If InitializeField is True, add code to the class constructor to register the attached property.

I hope I don’t forget something. If that’s the case, or if you find a bug, just tell me.

Patrick

Thanx

This seems helpful, but as a newbie to oxygene and VS, I’m struggling to figure out how you actually get this DLL into your VS project so it can be used. Any help?

Hello,
you should add the assembly to your project using Add Cirrus reference.
After that, you should reference the OrdinaSoft.Aspects assembly in the uses clause.
Then, you can use the aspects defined in the assembly.
Patrick

Thanks, Patrick. ran into more problems, though. I added the cirrus reference and put OrdinaSoft.Aspects in my uses. Then I tried to use the aspect for an attached property as:

    [Aspect: AttachedPropertyAspect]
    property CanLock: Boolean;

I get the compiler error:

Error	1	(E238) Exception while applying aspect
"OrdinaSoft.Aspects.AttachedPropertyAspect": 
Attribute does not implement IBaseAspect

And once you tell me what I’m screwing up, the next question will be how to preserve my set_CanLock function so I can take some action in it. I understand I can use InitializeField := False, but that would skip registering the field, right?

Hello,
what is the version of the compiler you are currently using?
I will post a version of the assembly compiled with the right version.

@ck: Do you think we can have some support on the compiler, so that I don’t need to compile the library for each compiler version, or publish the source code?

Patrick

Hello,
for the modification on the setter: never do this!
A dependency property is a special beast: its value can come locally (setting the value), but also from a style, from a parent, from an animation, … In some cases, the run-time bypass your setter and your code will never execute.

If you want to run code when the value is changed, you must register a method that will be called when the value has changed.
The signature of the method is:

  classmethod xxx (Obj : Object; Args : DependencyPropertyChangedEventArgs);

In your code, you must cast Obj into the right type to access the members of the class.

Then, you must use the InitializeField := False parameter of the aspect and register your dependency property with:

  xxxField := DependencyProperty.Register ('Name of property', TypeOf (The property type), TypeOf (The defining class type), new PropertyMetadata (@ xxx));

Patrick

Patrick: Thanks for the offer, and sorry I didn’t see it until now. This forum often forgets notification.

Honestly, I think this is a bit beyond me at the moment and I found I didn’t really need to use a DP anyway. So probably best to hold off on recompiling.

Do you plan on keeping this closed source? Otherwise, you could post the source to github and people could compile it whenever their compiler changed.

Hello,
I can’t, for the moment, publish the source of these aspects, because there are more aspects in the assembly and the others are used to link to classes defined in my own assemblies. This means that I need to clean up the aspects and I don’t have the time to do it now.
So, until the end of the year, I can recompile and post a new version, if necessary.
Patrick