Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How Dependency Properties are Stored in WPF

0.00/5 (No votes)
4 Jan 2017 1  
This article provides in depth overview of how dependency properties are stored in WPF Property System.

Introduction

While at work, someone asked me a question: As we know that Dependency Properties in WPF are usually public static and readonly, but each instance of owner objects can have their individual value at the same time. How is this possible? This is not in accordance with our understanding of static CLR properties. In this article, first we will be looking into few classes of WPF in order to answer this question. While we went into the discussion, it led to many more questions which we will be covering in the next article.

Note: We will use acronym "DP" instead of "Dependency Property" in the rest of the article for convenience of writing and reading. Basic understanding of WPF framework is a pre-requisite for this article.

Outline

  • General DP Creation Code
  • What happens inside Register method
  • How getter works for DP
  • How setter works for DP
  • How WPF Property System Reduces Memory footprint

General DP Creation Code

With this article, in the attached demo code, we have used two TextBoxes and initially will focus on "MinLines" DP of TextBox class. In MainWindow.xaml, we created two instances of TextBox, object named as TextBoxOne has local value of "MinLines" DP as 2 while object named as TextBoxTwo has local value of "MinLines" DP as 4. While we run the application, both TextBoxes have different values for MinLines DP as per their local values.

In order to understand working of DP, we will take an example of inbuilt "MinLines" DP and see how the local values are stored. Before diving into how DP and its local values are stored, first let's see how TextBox class defines "MinLines" DP. The below code is taken from reference code of TextBox available here.

/// <summary>
/// Dependency ID for the MinLines property
/// Default value: 1
/// </summary>
public static readonly DependencyProperty MinLinesProperty =
                DependencyProperty.Register(
                    "MinLines", // Property name
                    typeof(int), // Property type
                    typeof(TextBox), // Property owner
                    new FrameworkPropertyMetadata(
                        1,
                        FrameworkPropertyMetadataOptions.AffectsMeasure,
                        new PropertyChangedCallback(OnMinMaxChanged)),
                    new ValidateValueCallback(MinLinesValidateValue));
 
/// <summary>
/// Minimum number of lines to size to.
/// </summary>
[DefaultValue(1)]
public int MinLines
{
    get { return (int) GetValue(MinLinesProperty); }
    set { SetValue(MinLinesProperty, value); }
}

Just by looking at the above code, we can notice many things different from normal CLR properties:

  • While creating a DP, we just call static method Register of DependencyProperty class.
  • Variable MinLinesProperty is public static readonly and it is called identifier of DP.
  • Getter of DP is calling a method GetValue and there is casting to int.
  • Setter of DP is also calling a method SetValue.
  • There is no backing private field for DP as we used to have for CLR properties.

And we are calling methods for registering DP and while getting or setting value the DP. The mechanism how DP works and stores its value is hidden in WPF Property System. Further, we will look closely at what happens when you call those methods.

What Happens inside Register Method

The brief working of Register Method of DependencyProperty class is given below:

  • You call static method: Register of DependencyProperty class with arguments while creating a DP.
  • Register method validates Metadata (if given) and calls the below method:

    DependencyProperty property = RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);

  • Inside RegisterCommon method, first, a key for DP is being created using Name of dependency property and its owner type.
  • A class called as FromNameKey will be used as Key. The Key maintains integer Hashcode for DP. It overrides GetHashCode method and generates HashCode based on DP name and its OwnerType.
  • DependencyProperty class has a private HashTable named as PropertyFromName. Then RegisterCommon method checks if HashTable called PropertyFromName does not that Key already.
  • Then if metadata is not given, default metadata is being created.
  • Then instance of DP is created using new keyword:
    // Create property
    DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, 
    defaultMetadata, validateValueCallback);
  • New instance of DP is created and stored in HashTable called PropertyFromName as shown below:
    PropertyFromName[key] = dp;
  • And newly created DP is returned to from RegisterCommon method to Register method of DependencyProperty class.
  • Finally, Register method returns the DP instance to the class which called Register method to store that in identifier too for its use. In our case, variable/identifier MinLinesProperty will receive the return value of Register method.

Above is just a simplified summary of what happens, feel free to deep drive into the code - reference code available here. DependencyProperty class maintains a static reference of all the DependencyProperty in a hashtable called PropertyFromName. Each dependencyProperty object is registered in that HashTable.

There are total 5 different overloads of public static Register methods in DependencyProperty class to register DP. Similarly, other 5 overloads of RegisterAttach method to register Attached properties. For more, have a look at DependencyProperty Class detail on MSDN.

How getter Works for DP

The working of getter of a DP can be described as follows:

  • The Getter works differently than CLR property getter. It does not return a value from a private field, but calls a method GetValue(DependencyProperty) from DependencyObject class.
  • DependencyProperty nowhere has a provision to store local value of DP. DP only has its default value and a method called GetDefaultValue which will return the default value of DP, that’s all.
  • One reason why DP can be defined only in a class inherited from DependencyObject, is that Getter of DP needs method GetValue which comes from base, i.e., DependencyObject class.
  • When you call GetValue, you pass the local public static read only variable (identifier) which has a little information and metadata related to DP (but not value).
  • GetValue method makes a call to the following method:
    GetValueEntry( LookupEntry(dp.GlobalIndex), dp, null, RequestFlags.FullyResolved) 

    which returns a struct called EffectiveValueEntry.

  • Inside method GetValueEntry, for simplicity, the following code is most relevant here:
    entry = _effectiveValues[entryIndex.Index];
    It looks into a private array of struct EffectiveValueEntry called as _effectiveValues which holds all the instance values for a DP of the class. This array is defined in class DepencentObject as private instance member so all class inherited from DepencentObject will have and uses it. _effectiveValues works as the collection of effective values for the DependencyObjects.
  • struct EffectiveValueEntry has a field called Value which is of object type so it can hold instance value of DP based on any data type.
  • Method EntryIndex LookupEntry (int targetIndex) takes GlobalIndex (Zero-based globally unique index of the property) of DP and looks for an entry that matches the given DP.
  • Finally casting happens in getter of DP from object to particular data type (as return type of GetValue method is object).

Following is the code how _effectiveValues is defined in class DependencyObject:

// The cache of effective values for this DependencyObject
// This is an array sorted by DP.GlobalIndex.  This ordering is
// maintained via an insertion sort algorithm.
   private EffectiveValueEntry[] _effectiveValues;

and same private array is exposed internal property of class DependencyObject as EffectiveValues:

// The cache of effective (aka "computed" aka "resolved") property
// values for this DO.  If a DP does not have an entry in this array
// it means one of two things:
//  1) if it's an inheritable property, then its value may come from
//     this DO's InheritanceParent
//  2) if it's not an inheritable property (or this DO's InheritanceParent
//     doesn't have an entry for this DP either), then the value for
//     that DP on this DO is the default value.
// Otherwise, the DP will have an entry in this array describing the
// current value of the DP, where this value came from, and how it
// has been modified
internal EffectiveValueEntry[] EffectiveValues
{
    [FriendAccessAllowed] // Built into Base, also used by Framework.
    get { return _effectiveValues; }
}

How Setter Works for DP

The working of getter of a DP is as follows:

  • Similar to Getter, Setter also works differently than CLR property setter. It does not store a value in a private field, but calls a method SetValue from DependencyObject class.
  • When you call SetValue, you pass the local public static read only identifier and given value (casting happens as object).
  • SetValue method makes a call to the following method which returns void:
    SetValueCommon(dp, value, metadata, false, false, OperationType.Unknown, false)
  • Inside method SetValueCommon, first it will look for entry index in _effecticeValues array.
    EntryIndex entryIndex = LookupEntry(dp.GlobalIndex);
  • And then struct EffectiveValueEntry is created for given DP and finally SetValueCommon will call following methods to insert/update value in _effectiveVaues array:
    SetEffectiveValue(entryIndex, dp, dp.GlobalIndex, metadata, 
    newExpr, BaseValueSourceInternal.Local);
    .................
    .................
    UpdateEffectiveValue(entryIndex, dp, metadata, oldEntry, 
    ref newEntry, coerceWithDeferredReference, coerceWithCurrentValue, operationType);
  • Both SetValue and SetValueCommon methods are void so return nothing.

For a side note, there are following methods in class DependencyObject which updates values in _effectiveVaues array being called during different operations:

private EffectiveValueEntry GetEffectiveValue(
	            EntryIndex entryIndex, DependencyProperty dp,
	            RequestFlags requests)

private void InsertEntry(
	            EffectiveValueEntry entry, uint entryIndex)

internal void SetEffectiveValue(
	            EntryIndex entryIndex, DependencyProperty dp, 
	            PropertyMetadata metadata, EffectiveValueEntry newEntry, 
	            EffectiveValueEntry oldEntry)

internal void SetEffectiveValue(
	            EntryIndex entryIndex, DependencyProperty dp, 
	            int targetIndex, PropertyMetadata metadata, 
	            object value, BaseValueSourceInternal valueSource)
        
private void SetExpressionValue(
	            EntryIndex entryIndex, object value, object baseValue)

How WPF Property System Reduces Memory Footprint

Now let's summarize what we concluded about how DP is stored internally in image given below:

Besides, let's see how CLR properties are stored. For example, if a class called Customer is having 100 CLR properties, then while you create an instance of Customer, the memory for all 100 CLR properties will be allocated in Heap.

Now let's take a TextBox class in WPF, which has more than 150 dependency properties, but until we set local values, there will be no memory consumed for a DP to store local values for created owner instance. Still, you can use getter and it will return value, either its default value or as per Dependency Property Value Resolution mechanism. As we have seen, default values of DPs are not owner instance specific like different TextBoxes. Default value of DP is stored with global DP static object in PropertyFromName HashTable (instance of DependencyProperty class for MinLines in our example) .

So in case of DP, the local values for instance consumes memory only if they are set. This is how WPF property system helps to reduce memory footprint of an application.

Conclusion

In this article, we looked into Dependency Property creation and how its getter and setter work. We understood how Dependency Property storage mechanism helps to reduce memory footprint of an application. Thanks for reading. Your comments and suggestions for improvement are most welcome.

References

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here