I was hoping that this series of articles would provide insight into the implementation of the WPF property system and how the XAML compiler uses these dependency properties, and finally analyze the actual implementation code of the WPF property system. However, the introduction of WPF attribute system code in the course of writing can touch all aspects of the property system. and its internal implementation code involves a lot of internal algorithms, explaining them may cause the reader to create more confusion. So I finally changed my mind, redefining this series of articles to describe the various features that the WPF property system provides, along with the various features that explain how the WPF property system is actually implemented.
This series of articles will start with the most basic knowledge about dependency properties and eventually go deep into the internal implementation of the WPF property system. Through this series of articles, I hope you will learn about the various aspects of the WPF property system and benefit your daily programming.
The composition of the dependency property
The various features provided by the WPF property system are primarily exposed through dependency properties. Therefore, the most important way to understand the property system is to understand exactly what functionality a dependency property provides. In this section, we'll make a brief introduction to the dependency property feature mentioned in this article.
The first is the composition of the dependency property. If you need to define a dependency property for a type, the type first needs to derive from the DependencyObject class to gain support for the property system. In the standard implementation of a dependency property, a dependency property exposes a public static member of the DependencyProperty type on that type as the ID of the dependency property. For example, the Contentproperty static property of the ContentControl class. At the same time, the software developer needs to expose the CLR wrapper for the dependency property, allowing the user to set the value of the dependency property directly through the content property during code writing:
1 New ContentControl (); 2 New Button ();
As you can see, the use of user code for dependency properties is no different from normal. NET properties. This is all due to the wrapper of the CLR property on the dependency property. The wrapper contains the actual code that operates on the WPF property system.
Of course, dependency properties provide richer functionality than normal CLR properties, or WPF does not have to re-implement a property system. It can be said that the implementation of the various features in WPF is inseparable from dependency property support, such as binding, template, animation, property value priority, property value inheritance and property value correction and change callback and so on. Enabling support for these features is straightforward: when creating a dependency property, the software developer needs to identify the support for each feature through metadata, and can pass in a custom function as a callback for the property change. At the same time, a derived class can also reset the settings provided by the base class when registering dependency properties, and also support a type to add dependency properties defined in another type to its own dependency property through the Addowner () function call.
But the most important reason to use dependency properties is that dependency properties have better performance. You might suspect that implementing a property with a normal. NET attribute implementation is pretty straightforward:
Private Object _content; Public Object content{ getreturn _content;} Set {_content = value;}}
In each accessor, the acquisition and setting of the property is just a single line of statements, where is there a space for ascension? In fact, the performance improvement that the dependency property refers to is not the time, but the space savings of the dependency property. In normal CLR properties, software developers need to provide a corresponding private member variable for each property to record the values of each property. But what is the reality? In the dozens of properties provided by a control, we often use a few of them to complete the setting of the control. This, together with WPF support for templates and content models, makes it a huge memory to write a complex UI using plain CLR properties.
The implementation of the WPF property system uses the flyweight pattern: there is a member named _effectivevalues in each function of the DependencyObject type. This member is an array of type Effectivevalueentry. It records the values of all assigned dependency properties in ascending order of the dependency property ID. When you try to get the value of a dependency property, the WPF property system first finds the record for that dependency property from the array. If the record exists, the value of the dependency property contained in the record is returned, otherwise the WPF property system returns the default value for that dependency property. It runs as follows: (. NET source code excerpt, expanded)
ObjectGetValue (DependencyProperty dp) {//find the entry for the current dependency property from _effectivevaluesEntryindex Entryindex = This. LookupEntry (DP. Globalindex) Effectivevalueentry entry; if(entryindex.found) {entry= This. _effectivevalues[entryindex.index]; } Else { //creates a default entry with a value of UnsetvalueEntry =NewEffectivevalueentry (DP, Basevaluesourceinternal.unknown); } if(Entry. Value! =dependencyproperty.unsetvalue) {returnEntry. Value;//returns the valid values recorded in the entry } //if the value recorded in the entry is not valid, the default entry for the dependency property is created, and the value it contains is the default valuePropertyMetadata metadata = dp. GetMetaData ( This. Dependencyobjecttype);
return Effectivevalueentry.createdefaultvalueentry (DP, metadata. Getdefaultvalue (this, DP)). Value;
}
Therefore, the more efficient the WPF property system refers to is not the speed at which the property is accessed at execution time, but the memory footprint that is sacrificed to some extent by the execution speed. Using the flyweight pattern is actually a common choice in large application implementations. When a program contains a large number of specific types of data with the same type, we can effectively reduce the memory it occupies by using this pattern.
Defining dependency Properties
Before explaining how dependency properties support each feature, let's start by explaining exactly what information was entered into the WPF property system when the dependency property was created.
The code for creating a dependency property through Dependencyproperty.register () is as follows:
Public Static ReadOnly DependencyProperty hintproperty = dependencyproperty.register ("Hint" typeoftypeof(Autocompleteedit), new Frameworkpropertymetadata ( String.Empty), new validatevaluecallback (ishintvalid));
Here, we use one of the most complex overloads of the function. This overload contains all the parameters that you can set when you create a dependency property. First, the software developer needs to indicate the name of the dependency property that needs to be created in the register () function. In the example above, the dependency property will have a name of hint. The property will be registered on type Autocompleteedit, and its own type is string. Without assigning a value to the property, the property will have a default value of String.Empty, and the validation function ishintvalid () will be called when the value is about to change.
When registering a DependencyProperty with the register () function, we need to set the static variable that accepts the return value to ReadOnly. This is because a property that is decorated with this keyword cannot be changed once it is assigned, allowing the compiler to optimize it.
What exactly does the Dependencyproperty.register () function do inside the WPF property system? Searching for this function in the. NET source code finds that either the register () function that registers the dependency property or the registerattached () function that registers the attached property actually calls a common function, Registercommon (). Does that mean that dependency properties and attached properties are really just different exposures to the same composition that the WPF property system supports? In fact, it does. For a property system, an attached property is actually a dependency property, but the WPF property system exposes different APIs in different ways, given the different ways in which dependency properties and attached properties are used. Therefore, the various features supported by the WPF dependency property are also supported by the attached property.
Let's keep looking down. In the Registercommon () function, we can see the following code:
Private StaticDependencyProperty Registercommon (stringname, type PropertyType, type ownertype, ...) { //Create a structure about the name of the dependency property and the type, and the key that will be looked up as a dependency propertyFromnamekey key =NewFromnamekey (name, ownertype); ...... DependencyProperty DP=NewDependencyProperty (name, PropertyType, OwnerType, Defaultmetadata, Validatevaluecallback); Defaultmetadata.seal (DP,NULL); ...Lock(Synchronized) {//insert into static member Dependencyproperty.propertyfromnamePropertyfromname[key] =DP; } ...returnDP;}
As you can see from the code summary above, the Registercommon () function simply creates an instance of DependencyProperty and records it in the private static member Propertyfromname as the name of the property and its type as the keyword. That is, all the dependency properties of the current program are recorded in the private member.
Many of the APIs exposed by the property system use the content logged by member Propertyfromname, such as the Dependencyproperty.fromname () function, which is implemented based on it. At the same time, this centralized registration also illustrates a problem: in the property system, the dependency property is not actually stored in a particular type, but rather is stored in a global variable as a component of a type key. This also explains where the WPF property system is less consistent with ordinary CLR properties when it is used. If we need to read and write dependency property values through the DependencyProperty instance returned by the register () function, instead of using the property name directly.
When defining a dependency property, the name of the Dependencyproperty.register () function that represents the dependency property is often an important reason that the dependency property is not working properly. A more common mistake is that the dependency property is registered with a name that does not correspond to the name of the dependency property. In the style, template, and normal dependency property settings, WPF internally looks for the corresponding dependency property within the property system through the property name + "Properties". For example, when assigning a value to the Name property of a FrameworkElement element in XAML, the XAML compiler translates the assignment to assign a value to the dependency property with ID nameproperty. If a parameter that is passed in to represent a dependency property name does not match the dependency property ID that is recorded in the type when the Dependencyproperty.register () function is called, the use of these dependency properties is invalidated.
Another thing is that a derived class automatically inherits the dependency property of the base class. You may have some questions in mind: derived classes and base classes should be two different types, so a key that is composed of derived classes and property names should not be able to access the dependency properties defined by the base class. However, the actual situation is that the acquisition of a dependency property starts along the inheritance hierarchy from the current type until the top of the type inheritance hierarchy is reached, or the corresponding dependency property is found by the combination of type and property name. In this way, the individual dependency properties defined in the base class are visible to the derived class.
At the same time, the way in which this dependency property is used by WPF makes it possible to override dependency properties. In a derived class of a DependencyObject class, a software developer can register a dependency property with the same name through the Dependencyproperty.register () function to complete the override of a dependency property. The WPF property system returns this newly registered dependency property when the property is found in the type and its derived classes. The biggest benefit of overriding a property through the Dependencyproperty.register () function is that it can change the type of the dependency property. This is not done by functions such as Overrdiemetadata () and Addowner ().
After adding a dependency property to the property system, we need to add a CLR property wrapper for the dependency property:
Public string hint{ getreturn (string) GetValue (Hintproperty);} Set {SetValue (hintproperty, value);}}
In the CLR property wrapper, we need to get or set the value of the dependency property through the GetValue () and the SetValue () function. Because the use of some dependency properties within WPF is done directly by calling GetValue () and SetValue (), it is best for software developers to call GetValue () and the SetValue () function directly in the CLR property wrapper. Instead of adding other custom logic, when you use certain features, such as executing a trigger declared in XAML, it appears that the setting for a property only calls the GetValue () and SetValue () functions.
It is important to note that we need to use the SetValue () function carefully in our program. As with Hintproperty's declaration, the ID of a dependency property will exist in a type with a static public member. Program code outside of this type can be assigned to a dependency property through the SetValue () function and the ID. Here's the problem: first, the SetValue () property can accept any type of instance, so user code is extremely prone to the wrong type of property set. Another point is that the call to the function empties the use of the dependency property declared by the current program, such as existing triggers, data bindings, and styles. If you want to avoid clearing the current use, then we need to use the Setcurrentvalue () function. The function also sets the property of the current dependency property to a numeric value, but does not affect its use as declared by the current program. This is the most basic difference between it and the SetValue () function. This function is useful for developing a custom control: Sometimes the inside of a control needs to make changes to the property value without clearing the user code's use of the property value.
Next, we can use the dependency property hint in the program:
<Hint= "Please enter text"/>
There is a slight difference between creating a read-only dependency property and creating a dependency property: 1) When registering a property, call the Registerreadonly method instead of the Register method. 2) The software developer does not need to provide a set accessor. 3) Read-only property registration returns a DependencyPropertyKey. And, by standard practice, the type DependencyPropertyKey should not be a member with public access rights. Software developers should expose their recorded DependencyProperty. The following is the code that implements the read-only property Hashint:
Public Static ReadOnlyDependencyProperty Hashintproperty =Hashintkey.dependencyproperty;Private Static ReadOnlyDependencyPropertyKey Hashintkey =Dependencyproperty.registerreadonly ("Hashint",typeof(BOOL), typeof(Autocompleteedit),NewFrameworkpropertymetadata (false), NewValidatevaluecallback (Ishashintvalid)); Public BOOLhashint{Get{return(BOOL) GetValue (Hashintproperty); }}
Because property values cannot be set, WPF's read-only dependency property cannot complete support for features such as animations. But it is often used as an important support for some other functions. As ContentControl adds the Hascontent property for its content property to allow the WPF program to take it as input in many functions, such as triggers, bindings, and so on.
In the next article, we'll explain the property change callbacks.
Reprint please specify the original address: http://www.cnblogs.com/loveis715/p/4343330.html
Commercial reprint please contact me in advance:[email protected], I will only ask to add the author name and blog Home link.
WPF-Property Systems (1 of 4)