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
- 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
Thanx for the help, it works splendid over here
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