Vi. dependency property callback, validation, and enforcement values
Let's take a brief look at the basic steps of the WPF property system for dependency property operations by following this diagram:
Borrow a common legend to describe the basic steps of the WPF property system for dependency property operations:
- The first step is to determine the base value, and the assignment to the same property can occur in many places. For example, the background of the control (Background), which may be assigned in the style or in the constructor of the control, is the base value that determines the highest priority value in these values as base value.
- The second step, valuation. If the dependency property value is a computed expression, such as a binding, the WPF property system evaluates the expression to convert the result to an actual value.
- The third step, animation. Animation is a special behavior with a high priority. If the current property is being animated, the animation produces a value that is better than the previously obtained value, which is what WPF often says about animation.
- Fourth step, force. If we pass in the Coercevaluecallback delegate in Frameworkpropertymetadata, the WPF property system will call back the delagate we passed in, validate the property value, and verify that the value of the property is within our allowed range. For example, forcing the value to be greater than 0 is less than 10, and so on. During property assignment, coerce has the highest priority, which is greater than the priority of the animation.
- Fifth step, verify. Validation refers to the fact that we register a dependency property if a Validatevaluecallback delegate is provided, then finally WPF invokes our incoming delegate to verify the validity of the data. An exception is thrown when the data is invalid to notify.
So how do you use these features?
We've talked about the basic process, and here's a small example to illustrate:
The code for XAML is as follows:
<window x:class= "Wpfapp1.windowvalid" xmlns= "Http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x= "Http://schemas.microsoft.com/winfx/2006/xaml" Title= "Windowvalid" height= "width=" > <Grid> <StackPanel> <button name= " Btndptest "click=" Btndptest_click "> Property value Execution Order test </Button> </StackPanel> </grid></ Window>
The code for C # is as follows:
Using system;using system.collections.generic;using system.linq;using system.text;using System.Threading;using System.threading.tasks;using system.windows;using system.windows.controls;using System.Windows.Data;using System.windows.documents;using system.windows.input;using system.windows.media;using System.Windows.Media.Imaging ; using system.windows.shapes;using system.windows.threading;using wpfapp1.models; namespace wpfapp1{//<summary>///Windowthd.xaml interactive logic///</summary> public partial class wind Owvalid:window {public windowvalid () {InitializeComponent (); } private void Btndptest_click (object sender, RoutedEventArgs e) {SIMPLEDP test = new SIMPLEDP (); Test. VALIDDP = 1; }}} using System;using system.collections.generic;using system.linq;using system.text;using System.Threading.Tasks; Using System.Windows; Namespace wpfapp1.models{public class Simpledp:dependencyobject {PublIC static readonly DependencyProperty validdpproperty = Dependencyproperty.register ("VALIDDP", typeof (int), Typ EOF (SIMPLEDP), new Frameworkpropertymetadata (0, Frameworkpropertymetadataoptions.none, New Propertychangedcallback (onvaluechanged), New Coercevaluecallback (CoerceValue)), New Validatevaluecallback (IsValidValue)); public int VALIDDP {get {return (int) GetValue (validdpproperty);} set {SetValue (Validdpproperty, value);} } private static void OnValueChanged (DependencyObject D, DependencyPropertyChangedEventArgs e) { Console.WriteLine ("When the OnValueChanged method of the property value is called, the property value is: {0}", E.newvalue); } private static Object CoerceValue (DependencyObject D, object value) {Console.WriteLine ("When the value of the property The CoerceValue method is called, and the property value is coerced to: {0} ", value); return value; } private static BooL IsValidValue (object value) {Console.WriteLine ("When the IsValidValue method of the property value is called, validates the property value, returns a bool value, if TRUE indicates a serious pass , otherwise it will be thrown in the form of an exception: {0} ", value); return true; } }}
The results are as follows:
When the VALIDDP property changes, the Propertychangecallback is called. You can see that the results are not exactly in the order in which our previous process first coerce the validate, it is possible that WPF has done something special internally, and when the property is modified, it will first call validate to determine if the value passed in is valid, and if it does not continue subsequent operations , so you can better optimize performance. As seen from the above results, the CoerceValue is not immediately validatevalue, but directly calls the propertychanged. This is because the value has been verified previously, and if value is not changed in coerce, then no further validation is necessary. If value is changed in coerce, then the validatevalue operation will be called again, as in the order of the previous flowchart execution, and in the end we will call Validatevalue for final verification, which ensures that the final result is what we want.
The above is a brief introduction to the processing process, below we will take a case to see the actual process in the end there is no discrepancy.
The dependency property code file is as follows:
Using system;using system.collections.generic;using system.linq;using system.text;using System.Threading.Tasks; Using System.Windows; Namespace Wpfapp1.controls{class MyValiDP:System.Windows.Controls.Control {//Register the current dependency property and add Propertyc Hanged, CoerceValue, Validatevalue callback delegate public static readonly DependencyProperty Currentvalueproperty = DEPENDENCYPR Operty. Register ("CurrentValue", typeof (Double), typeof (MYVALIDP), new Frameworkprope Rtymetadata (Double.NaN, Frameworkpropertymetadataoptions.none, new PROPERTYC Hangedcallback (oncurrentvaluechanged), New Coercevaluecallback (Coercecurrentvalue)), New Validatevaluecallback (IsValidValue)); The property wrapper, which exposes the value of current to public double CurrentValue {get {return (double) GetValue (Currentvaluepr Operty); } set {SetValue (Currentvalueproperty, value);} }//Register min dependency property and add PropertyChanged, CoerceValue, Validatevalue callback delegate public static readonly Dependencypro Perty Minvalueproperty = Dependencyproperty.register ("MinValue", typeof (Double), typeof (MYVALIDP), New Frameworkpropertymetadata (double. NaN, Frameworkpropertymetadataoptions.none, New Propertychangedcallback (onminvaluechanged), New Coercevaluecallback (Coerceminvalue)), New Validatevaluecallback (IsValidValue)); The property wrapper, through which to expose the value of min public double MinValue {get {return (double) GetValue (Minvalueproperty);} set {SetValue (Minvalueproperty, value);} }//Register Max dependency property and add PropertyChanged, CoerceValue, Validatevalue callback delegate public static readonly DependencyProperty M Axvalueproperty = Dependencyproperty.register ("MaxValue", typeof (Double), typeof (Myvalid P), New FrameworkpropertymetadatA (double. NaN, Frameworkpropertymetadataoptions.none, New Propertychangedcallback (onmaxvaluechanged), New Coercevaluecallback (Coercemaxvalue)), New Validatevaluecallback (IsValidValue) ); The property wrapper, which exposes the value of max public double MaxValue {get {return (double) GetValue (Maxvalueproperty);} set {SetValue (Maxvalueproperty, value);} }//In coercecurrent Add mandatory judgment Assignment private static object Coercecurrentvalue (DependencyObject D, object value) {MYVALIDP g = (MYVALIDP) D; Double current = (double) value; if (current < G.minvalue) current = G.minvalue; if (current > G.maxvalue) current = G.maxvalue; return current; }//When the current value changes, call min and Max's CoerceValue callback delegate private static void Oncurrentvaluechanged (DependencyObject D, DependencyPropertyChangedEventArgs e) { D.coercevalue (Minvalueproperty); D.coercevalue (Maxvalueproperty); }//When the Onmin value is changed, call the CoerceValue callback delegate of current and Max private static void Onminvaluechanged (DependencyObject D, De Pendencypropertychangedeventargs e) {d.coercevalue (maxvalueproperty); D.coercevalue (Currentvalueproperty); }//In coercemin Add mandatory judgment Assignment private static object Coerceminvalue (DependencyObject D, object value) { MYVALIDP g = (MYVALIDP) D; Double min = (double) value; if (min > g.maxvalue) min = g.maxvalue; return min; }//In Coercemax Add mandatory judgment Assignment private static object Coercemaxvalue (DependencyObject D, object value) { MYVALIDP g = (MYVALIDP) D; Double max = (double) value; if (Max < g.minvalue) max = G.minvalue; return Max; }//When the max value changes, call min and current's CoerceValue callback delegate private static void OnMAxvaluechanged (DependencyObject D, DependencyPropertyChangedEventArgs e) {d.coercevalue (Minvaluepropert y); D.coercevalue (Currentvalueproperty); }//Verifies that value is valid and if TRUE indicates validation passed, otherwise it will prompt for exception public static bool IsValidValue (object value) {do Uble v = (Double) value; Return (!v.equals (double.negativeinfinity) &&!v.equals (double.positiveinfinity)); } }}
The XAML code is as follows:
<window x:class= "wpfapp1.windowprocess" xmlns= "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x= "Http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local= "Clr-namespace:wpfapp1.controls" title= " Windowprocess "height=" "width=" > <Grid> <stackpanel orientation= "Vertical" > &L T;LOCAL:MYVALIDP x:name= "myValiDP1" maxvalue= "five" minvalue= "0"/> <label content= "can be set to a minimum of 0 and a minimum of a maximum value of" He ight= "/> <stackpanel orientation=" horizontal "height=" > <label content= "The current value is: "/> <label background=" Yellow "borderbrush=" Black "borderthickness=" 1 "isenabled=" False "content=" {Binding elementname=myvalidp1, path=currentvalue} "height=" verticalalignment= "Top"/> & lt;/stackpanel> <wrappanel > <label content= "min"/> < Slider x:name= "Slidermin"Minimum=" -200 "maximum=" "width=" "valuechanged=" "slidermin_valuechanged" smallchange= "ten"/> &L T Label content= "{Binding elementname=slidermin, path=value}"/> </WrapPanel> <wrappanel & Gt <label content= "Max"/> <slider x:name= "Slidermax" minimum= "$" maximum= "$" width= "Valuech" Anged= "slidermax_valuechanged" smallchange= "ten"/> <label content= "{Binding Elementname=slidermax, Pa Th=value} "/> </WrapPanel> </StackPanel> </Grid></Window>
The C # code is as follows:
Using system;using system.collections.generic;using system.linq;using system.text;using System.Threading.Tasks; Using system.windows;using system.windows.controls;using system.windows.data;using system.windows.documents;using System.windows.input;using system.windows.media;using system.windows.media.imaging;using System.Windows.Shapes; namespace wpfapp1{//<summary>//Windowprocess.xaml interactive logic///</summary> public partial class Windowprocess:window {public windowprocess () {InitializeComponent (); Set the value of current myvalidp1.currentvalue = 100; private void Slidermin_valuechanged (object sender, Routedpropertychangedeventargs<double> e) { Sets the value of current myvalidp1.currentvalue = (int) slidermin.value; private void Slidermax_valuechanged (object sender, Routedpropertychangedeventargs<double> e) { Set the value of current MyvalIdp1.currentvalue = (int) slidermax.value; } }}
Example effects such as.
In the above example, there are three dependency property interactions--currentvalue, MinValue, and MaxValue, which interact, but their rules are minvalue≤currentvalue≤maxvalue. According to this rule, when one of the dependent properties changes, the other two dependency properties must be appropriately adjusted, and here we need to use CoerceValue this callback delegate, then the implementation is very simple, register maxvalue time to join Coercevaluecallback, processing in the Coercemaxvalue function: If the value of maximum is less than MinValue, then the MaxValue value equals MinValue In the same currentvalue, Coercevaluecallback is added to the corresponding mandatory processing. Then, when the MinValue Changedvaluecallback is called, call the CoerceValue callback delegate of CurrentValue and MaxValue, so that the interaction of the dependency property of a change should be the "thousand-variable".
In other words, when one of several dependent properties of interaction changes, the coercevalue of the dependency property that it affects is called in its propertychangecallback to ensure the correctness of the interaction relationship. The previous increase in Validatevalue is mainly to verify the validity of the data, the most set value will be called after the validation, if the validation is unsuccessful, throws an exception.
WPF Getting Started Tutorial series 14-Dependency properties (iv)