Note : If you are really new in WPF and don't know about Dependency Property, it would be nice to read my post on Dependency Property or you can also try WPF Tutorial series to start on.
So lets start on using the most basic code of creating your own Dependency Property.
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(DependencyHoster)); public string Description { // Will not be called each time property is retrieved. Only be called when //you explicitely call it through ur code. get { return (string)this.GetValue(DescriptionProperty); } set { this.SetValue(DescriptionProperty, value); } }
So here we define a property called Description to our DependencyObject named DependencyHoster of type string. According to the rule specified on DependencyProperty System, it is stated that you should always create a static reference of DependencyProperty and call Register to register a property to the Property System. It is also to the rule, that you should define a CLR property Stub which will call GetValue and SetValue in its getter and setter which is defined in DependencyObject to get or set the actual value defined for the instance. The Dependency property reference should be named with property name with "property" suffix. (in our case property name is Description and with suffix Property, it is named as DependencyProperty).
Now lets see what exactly happens inside when you Register a DependencyProperty. I would use our favorite tool Reflector to get deep into it.
So if you look into the Register method you will see something like this :
Now lets concentrate on the lines highlighted in the image. The First creates an object of FromNameKey, which is created with name that we pass in Register and the Type in which the key is associated with. This class is just a repository of a single key element with the type associated with it.
Next, it is checked inside a HashTable named PropertyFromName. Well, I don't know why it is used as HashTable rather than Dictionary
Another important line that actually assigns the new object of DependencyProperty to the HashTable.
So basically from the above three lines, you can deduce some of the interesting facts on DependencyProperty System as of now :
A DependencyProperty maintains a static reference of all the DependencyProperty you register in WPF object hierarchy. It maintains a HashTable named PropertyFromName which it uses internally to get the DependencyProperty object. So in other word, each dependencyProperty object is registered in a global HashTable.
Now lets check the object DependencyProperty as a whole and what it is capable of. Remember that each DependencyProperty object is created once for every DependencyProperty you define in your property system and not on every instance of object you create. Hence according to what we just seen, all TextBox will have only one TextProperty dependency property that go inside the field PropertyFromName.
Therefore the object DependencyProperty just holds the name, ownerType and its metadata. Metadata can be the Callbacks the DependencyPropertyKey element etc.
Where the value gets stored exactly ?
Well this might have come already in your mind. As I have already seen the DependencyProperty and it seems nowhere until now, a provision of value to be stored, yet a DependencyProperty has a method called GetDefaultValue for a specific type which will return the default value, but what if I assign something to a Type, where does it get stored ?
To clear this confusion, lets look into DependencyObject instance a bit and try to see what exactly written inside GetValue, SetValue and ClearValue method.
1. GetValue
GetValue is used to get the current value of an instance of a control. Remember, DependencyProperty supports a number of levels in which the effective values could be retrieved. Lets look in detail on the source of GetValue.
The code snippet that I have specified,clearly says that every DependencyObject instance actually maintains a collection of objects (EffectiveValues) which will be set only when the instance stores / modifies the value of the DependencyProperty. The code gets either from the own collection or gets it from a method GetEffectiveValue based on whether the Request is fully resolved or not. the GetEffectiveValue identifies whether the current value is in animation mode (DependencyProperty supports animation) or is in Expression.
Finally it checks whether the dependencyProperty DefaultValue is changed or not. If it isn't it gets you the default value of the property.
2. SetValue
SetValue on the other hand actually sets a value of a DependencyProperty on the instance from which it is called. As we have already seen every DependencyObject actually maintains a collection of EffectiveValueEntry which holds the value of instance object we specify with some index value.
So the first thing that you notice is inside the SetValue is actually a feature. If you pass DependencyProperty.UnsetValue as value, it will actually call ClearValue for you. Hence SetValue(DP, DependencyProperty.UnsetValue) equals ClearValue(DP).
The rest of the code looks quite simple. It checks whether the entry exists in the current object, if so it resets the value or it create a new EffectiveValueEntry and store it and update the index.
Impact of Styles
If you look into styles, it just maintains a collection of Setter. A setter on the other hand maintains a combination of DependencyProperty and the Value of the dependency property. Hence when a style is applied on an instance, it will get the value from it.
The Style is actually a DependencyProperty defined inside FrameworkElement class. So in object hierarchy, it gets the value of the Style. Once the style is set for a control, it first clears all the object instances that the object already applies to and then apply the setters to each of them.
Thus the CreateInstanceData is used to apply a new style for a type.
Remember, styles are overridden by the object values during runtime.
Summary
To summarize,
- Dependency Property System holds a collection of all DependencyProperty with its corresponding ownerType.
- Each instance of a DependencyObject holds a collection of all individual DependencyProperty that has been changed either through animation or programmatically.
- Styles produce a separate entity that holds a Key Value collection of a DependencyProperty and its value, once it is applied to a FrameworkElement, it modifies the default DependencyProperties with the ones that is defined inside styles.
- Separating DependencyProperty from the object instance saves lots of space, as Reference Types will not create object instances when each instance is created, but will use up the existing instance every time. The separation of property system also allows to have a Property to be used as attached to its children.
Conclusion
There are lots of things to be learned and lots of things to be talked about in this regard. It is just the beginning.
Feel free to comment. I would love to see if I missed out something or if anything that you didnt like.
Read more about internals from my Internals Series.
Thanks for reading.
Pretty nice post. I just stumbled upon your blog and wanted
ReplyDeleteto say that I've truly enjoyed surfing around your blog posts. After all I will be subscribing to your feed and I hope you write again soon!
testosterone booster