Sunday, August 10, 2008

Internal of DependencyProperty (2)

Set the value of DP
In the preceding sections, it shows you how to register and what .NET does for you. If we want to set the value of MyContent, we just need to dot this:
this.TestCtrl1.MyContent = "Button Clicked!";
According to the definition of this property, you can find that it invokes SetValue method internally.
What happens in SetValue…?
It takes three steps to do this, as the following diagram.

[1.1] you should know the DP can only be set by the thread that creates it, how to ensure this is done in VerifyAccess, which calls Dispatcher.VerifyAccess method. The Dispatcher maintains a prioritized queue of work items for a specific thread.
When a Dispatcher is created on a thread, it becomes the only Dispatcher that can be associated with the thread, even if the Dispatcher is shut down. Dispatcher.VerifyAccess checks whether current thread is the thread that creates it.

[1.2] it gets the PropertyMetadata with the help of GetMetadata, which is discussed previously.

[1.3] it is responsible for setting the value. If you have read the WPF unleashed, you may remember the following diagram.

It illustrates the five-step process that WPF runs each dependency property through in order to calculate its final value.
SetValueCommon
First, let’s take a look at the sequence diagram.

Here is one thing that all the values of the DependencyProperty are stores in _effectiveValues, which is an instance field in DependencyObject. That means before setting the value, it needs to search this field first in order to find the existing value for this property. This step is done by LookupEntry method [1.1], this method returns an EntryIndex, which is the location of the property in the _effectiveValues. _effectiveValues is an array of EffectiveValueEntry, which contains the value of the property and also includes some methods to modify the value. These methods will be discussed later.

[1.2] next step is to validate the value that you’re going to set.

[1.3] since it gets the location in step 1.1, it can use the index to get the EffectiveValueEntry.

[1.4] it creates a new EffectiveValueEntry object, which can be used to store the new value.

[1.5] UpdateEffectiveValue method finishes the five-step process.

After the final value is calculated, the value is stored in the _effectiveValues field.
Get the value of DP
You can retrieve the value through GetValue method.

Tuesday, August 05, 2008

Internal of DependencyProperty (1)

How to create a new DependencyProperty?
Let’s start from creating a new DependencyProperty. This example is simple, a new UserControl is created, which is named TestCtrl. In this UserControl, I create a new DependencyProperty as the following codes.
public partial class TestCtrl : UserControl
{
public static readonly DependencyProperty ContentProperty;

static TestCtrl()
{
TestCtrl.ContentProperty = DependencyProperty.Register("MyContent",
typeof(string), typeof(TestCtrl),
new FrameworkPropertyMetadata(string.Empty, new PropertyChangedCallback(OnContentChanged)),
new ValidateValueCallback(OnDefaultValueValidated));
}

public TestCtrl()
{
InitializeComponent();
}

public string MyContent
{
get { return (string)GetValue(TestCtrl.ContentProperty); }
set { SetValue(TestCtrl.ContentProperty, value); }
}

private static void OnContentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
MessageBox.Show(string.Format("Old Value: {0}; New Value: {1}", e.OldValue, e.NewValue));
TestCtrl current = obj as TestCtrl;
if (current != null)
{
current.TestBlock.Text = (string)e.NewValue;
}

}

private static bool OnDefaultValueValidated(object value)
{
MessageBox.Show("Validated Successfully");

return true;
}
}
It’s easy, isn’t it? Do you want to know what does the .Net do for you?
What does Register do…?
From this simple example, it’s easy to define a DependencyProperty, because .NET does a lot for you. Let’s see what it does.

When Register method is invoked, it invokes three methods internally.
1. RegisterParameterValidation. This method is going to validate the input parameters and ensure that all the arguments are set properly.
2. RegisterCommon. This method creates DependencyProperty and stores it.
3. OverrideMetadata. This method merges the PropertyMetadata with the PropertyMetadata of base type of your DependencyObject.
RegisterParameterValidation is simple, so it won’t be discussed here. In this section, I pay more attention to RegisterCommon and OverrideMetadata.
RegisterCommon
The following diagram is the sequence diagram of this method.





I am going to analyze this method step by step.
[1.3.1] it creates a FromNameKey object, which contains the name of the DependencyProperty and the owner type, this type is the class where this DependencyProperty is defined. This FromNameKey object is used as a key to store this dependency property.

[1.3.2] before any works, it first checks whether this property has been defined. PropertyFromName is a static field defined in DependencyProperty, and its type is Hashtable. Its key is FromNameKey and value is DependencyProperty.

[1.3.3] in ValidateMetadataDefaultValue method, it validates the default value of this PropertyMetadata. In our example, this default value is String.Empty. After the validating, it’ll invoke ValidateValueCallback, in our example, it is the method named OnDefaultValueValidated.

[1.3.4] if the validation is success, it creates the DependencyProperty object.

[1.3.5] PropertyMetadata::Seal is invoked, which invokes OnApply method internally. OnApply method is called when this metadata has been applied to a property, which indicates that the metadata is being sealed.

[1.3.6] the new dependency property is added in the PropertyFromName.
OverrideMetadata

When the DP(DependencyProperty) has already been added in PropertyFromName, there is one thing to do with PropertyMetadata, which is done in OverrideMetadata.


The discussion follows the same approach.


[1.1] SetupOverrideMetadata method uses owner type and PropertyMetadata that is passed in DependencyProperty constructor to get the DependencyObjectType of owner type and PropertyMetadata of the base class of current DependencyObject. DependencyObjectType represents a specific underlying system (CLR) Type of a DependencyObject. DependencyObjectType is essentially a wrapper for the (CLR) Type so that it can extend some of its capabilities.

[1.1.2] as said in step 1.1, SetupOverrideMetadata returns a PropertyMetadata object, which is for the base class of current DependencyObject. This work is done by GetMetadata method. In step 1.1, the DependencyObjectType of owner type is got, through this object, the DependencyObjectType of base type can be got, which is used by GetMetadata method to get the corresponding PropertyMetadata.
How to get the PropertyMetadata for the base class? All the PropertyMetadata objects are stored in _metadataMap field, which is defined as instance field in DependencyProperty. Notice that this field is instance field; this means that it contains the PropertyMetadata objects that belong to current object. The PropertyMetadata can be found in this field using the DependencyObjectType.Id as the key. How to use this PropertyMetadata of the base class will be discussed later.

[1.2] ProcessOverrideMetadata method stores this object in the _metadataMap field and merges the PropertyMetadata with the PropertyMetadata of the base class, which is got in step 1.1.2.

[1.2.2] InvokeMerge method merges the PropertyMetadata with the PropertyMetadata of the base class, which is got in step 1.1.2. The following values are copied from baseMetadata to current Metadata:
1. The default value;
2. PropertyChangedCallback invocation list;
3. CoerceValueCallback.

This is the whole process of registering a new DependencyProperty.

Next post, I am going to talk about the GetValue process in DependencyProperty.

Search YouTube