Property Change Callback
In the example in the previous chapter, the settings for each parameter are very easy to understand. If we just need to create a separate dependency property, the underlying knowledge of creating dependency properties mentioned above is sufficient to meet the requirements. But things are often not so perfect. In a system, very few attributes exist independently, especially in a class library composed of WPF, a descriptive interface. For example, the value of one property may be limited by many other properties, or a change in one property value may cause other dependency property values to change.
In the WPF property system, the maintenance of all of these relationships is done through metadata and callbacks that are passed in when attributes are created. When creating an associated property, we can pass in a callback function called when a property has changed, Validatevaluecallback, and a metadata propertymetadata. In this metadata, we can also record the callback function Propertychangedcallback and coercevaluecallback that will be called when two property values change. What exactly is the relationship between these three callback functions? Let's first write a dependency property that uses these three callback functions:
Public Static ReadOnly DependencyProperty hintproperty = dependencyproperty.register ("Hint" typeoftypeof(Autocompleteedit), new Frameworkpropertymetadata ( String.Empty, Hintchanged, Coercehint), new validatevaluecallback (ishintvalid));
The simple implementation of these callback functions is as follows:
Public Static BOOLIshintvalid (Objectvalue) {Console.WriteLine ("Ishintvalid called"); return true;} Public Static voidhintchanged (DependencyObject obj, DependencyPropertyChangedEventArgs args) {Console.WriteLine ("Hintchanged called");} Public StaticObject Coercehint (DependencyObject D, Object BaseValue) {Console.WriteLine ("Coercehint called"); returnBaseValue;}
As you can see from the information printed in the console by these three functions, if we assign a value to the Hint property in the program, these three functions are sequentially called ishintvalid (), Coercehint (), and hintchanged (). In the dependency property, what are the callbacks Validatevaluecallback,coercevaluecallback and propertychangedcallback that correspond to these three functions?
The first thing to see is validatevaluecallback. This type of function is used to check whether the current assignment for a property conforms to the requirement of the current type for that property. It takes a parameter of type object to represent the property value that will be set. Because in general, each dependency has a CLR property wrapper, and the property wrapper has a type that is consistent with the type required by the dependency property, so in Validatevaluecallback, Software developers can directly convert parameters of type object to the actual type that the dependency property has. If the passed-in property value is a valid value for the type, the function needs to return true, otherwise false is returned. In the case where false is returned, the property system throws an exception to indicate that the current operation on the dependency property is attempting to set the property value to an illegal value.
Since the Validatevaluecallback function is used in the Dependencyproperty.register () function call, it needs to be a static function. And because it only accepts a parameter value that represents the property value that will be set, the function does not have access to the type instance where the property is set, and it is more impossible to determine whether the property value is reasonable based on the information on the instance of the type. In other words, validatevaluecallback only perform type validation, not validation on the instance.
The use of this type of callback is simple: when you need to limit the value of a property of a type, the software developer needs to indicate the function that the callback needs to use during the registration of the dependency property. Take the borderthickness attribute of the border class as an example:
Public ... DependencyProperty borderthicknessproperty = DependencyProperty new Validatevaluecallback ( Border.isthicknessvalid));
In the Isthicknessvalid () function, the Border class adds a value criterion to the value of the BorderThickness property through the thickness.isvalid () function:
Private Static BOOL Isthicknessvalid (object value) { = (Thickness) value; // The thickness value is not allowed to be negative, cannot be Nan, cannot be positive infinity, nor can it be negative infinity return thickness. IsValid (falseto False);}
The next call is Coercevaluecallback. This function is used to limit the value of the current dependency property based on other dependency properties when a dependency property is changed. A function of this type accepts two parameters: represents the type instance for which the property needs to be set, and the property value that needs to be set. So in this function, the software developer can verify that the property value that needs to be set satisfies the current state of the instance through the type instance. If the property value that you want to set does not meet the current state of the instance, then the software developer can return a restricted value that conforms to the current instance and ultimately the value of the dependency property. It is important to note that the function is called only when the value changes. If the assignment to the dependency property is the first assignment to it, it will not be called.
A special return value allowed by Coercevaluecallback is unsetvalue. When the value is treated as a return value for Coercevaluecallback, the setting of the dependency property becomes invalid. The value logged by the dependency property will still be the original value of the dependency property.
One of the most common cases where a coercevaluecallback callback is used is the inter-correlation of some attributes. Take the example of the implementation of the Max, Min, and value three dependency properties of the Rangebase class. WPF identifies the use of the Coercevaluecallback callback during the registration of the dependency property value:
Public ... DependencyProperty valueproperty = DependencyProperty . Register ( ...) New Propertychangedcallback (rangebase.onvaluechanged), ...);
The function Rangebase.constraintorange () verifies the value of the property that you want to set, based on the Max and Min properties that are set on the current instance. If the value of value is greater than the value of max, the function returns the maximum value to prevent the Value property from being greater than Max, andif value is less than min, the function returns the minimum value to prevent the value the value of the property is less than Min. The implementation code for this function is as follows:
Internal Static ObjectConstraintorange (DependencyObject D,Objectvalue) {rangebase Base2=(rangebase) D; DoubleMinimum =Base2. Minimum; DoubleNum2 = (Double) value; if(Num2 <minimum) { returnminimum; } DoubleMaximum =Base2. Maximum; if(Num2 >maximum) { returnmaximum; } returnvalue;}
I believe you now have a question: why does the implementation of Microsoft divide the validation of a dependency property into Validatevaluecallback and Coercevaluecallback two, that is, by type validation and by instance validation of these two steps? The answer to this question needs to be explained in several ways.
The first is the WPF property system's support for desired value. After the software developer has passed the Validatevaluecallback setting for a property, the WPF property system records the value as desired value of the property. However, in the process of checking with the Coercevaluecallback callback, the value displayed by the property may change because it does not meet the constraints of each related attribute. The property value displayed by the WPF property at this point is the changed value. The Coercevaluecallback callback is called again to constrain the desired value when these related attributes of the constraint change. The result of the constraint may be a new property value.
For example, suppose a type has a Min,max attribute defined, and the Value property should be between these two values. During an assignment to value, because it is larger than the value recorded by the Max property, its value is forced to be constrained to the maximum value recorded by the Max property. Next, when the Max property changes, the callback function of the Max property refreshes the property value of value with Coercevaluecallback. The original assignment to the Value property may already be between Max and Min, so that it returns to its true value.
Within the WPF system, support for the desired value feature is done through the MODIFIEDVALUE structure:
PrivateSystem.Windows.ModifiedValue Ensuremodifiedvalue () {... System.Windows.ModifiedValue value2= This. _value asSystem.Windows.ModifiedValue; if(value2 = =NULL) {value2=NewSystem.Windows.ModifiedValue {BaseValue= This. _value//Basevalue used to record the original assignment }; This. _value =value2; } returnvalue2;}
As you can see from the code above, WPF internally uses a type called Modifiedvalue as its numerical record structure. The structure can record the underlying assignment of a property through the Basevalue property. At the same time it can store some other information, such as the value after the animation processing:
Internal class modifiedvalue{ privateobject _animatedvalue; Private Object _basevalue; Private Object _coercedvalue; Private Object _expressionvalue; ...}
I believe that here you will see how the WPF property system supports certain features. For example, how WPF supports the ability to set an animation on a property does not change the original value of the dependency property.
As we have described in the previous tutorial, the value of a dependency property is recorded by an instance of the Effectivevalueentry type contained in the type instance. In the implementation of many member functions within the Effectivevalueentry class, WPF often obtains Modifiedvalue type instances through the Ensuremodifiedvalue () function, and then operates on these member properties:
Internal void Setcoercedvalue (objectobject baseValue, bool skipbasevaluechecks) { this. Ensuremodifiedvalue (). Coercedvalue = value; This true ; This false ;}
For example, in the above code, the Setcoercedvalue () function first obtains an instance of the Modifiedvalue type recorded in Effectivevalueentry, and sets its Coercedvalue property and the associated flag bit property.
Another point that has to be mentioned is that the Coercevaluecallback function uses the static property Unsetvalue of the DependencyProperty. In the WPF property system, if a function returns this value, it is prompted that the WPF property system should ignore the execution of this feature. For example, during the execution of a binding, if the execution result of the binding is unsetvalue, then the execution of the binding will be ignored. This is also true during the execution of the Coercevaluecallback function: If the Coercevaluecallback function executes as unsetvalue, then the assignment to the dependency property will be invalid.
The last is the property's change callback Propertychangedcallback. In this type of function, software developers can refresh other properties by calling the CoerceValue () function to update the individual properties associated with the currently assigned property. One of the most obvious examples is the implementation of the maximum property:
Public ... DependencyProperty maximumproperty = DependencyProperty . Register ( ...) New Propertychangedcallback (rangebase.onmaximumchanged), ...); Private Static void onmaximumchanged (DependencyObject D, DependencyPropertyChangedEventArgs e) { = (rangebase) d ; ... Element. CoerceValue (Valueproperty);}
Well, it's here today. In the next article, we'll look into the metadata in the property system.
Reprint please specify the original address: http://www.cnblogs.com/loveis715/p/4343342.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 (2 of 4)