background
During application development, user input is often verified, usually based on type, scope, format or specific requirements to ensure that the input meets expectations. For example, the mailbox input box checks whether the input content meets the mailbox format. In WPF, the data model allows theValidationRules
andBinding
Object association can be inheritedValidationRule
Class and rewriteValidate
Method to create custom rules.
question
Although creating custom verification rules can meet most application scenarios, it is a bit troublesome when our verification rules change dynamically. For example, developing a file management system requires that the file name cannot be duplicated with the existing files in the system. At this time, you need to first obtain the name list of existing files in the system and bind toValidationRule
superior. HoweverValidationRule
Not inherited fromDepedencyObject
, dependency attributes cannot be added, and the parameters in custom validation rules do not support binding.
Solution
Next, a solution will be given to let ValidationRule support parameter binding. The idea is as follows:
First, customize a class ValidationParams that inherits DepedencyObject, and add dependency attributes to bind the data.
public class ValidationParams:DependencyObject { public object Data { get { return (object)GetValue(DataProperty); } set { SetValue(DataProperty, value); } } public static readonly DependencyProperty DataProperty = ("Data", typeof(object), typeof(ValidationParams), new PropertyMetadata(null)); }
Then add the property of type ValidationParams in the custom verification rule FileNameValidationRule.
public class FileNameValidationRule : ValidationRule { public ValidationParams Params { get; set; } public override ValidationResult Validate(object value, CultureInfo cultureInfo) { Regex reg = new Regex("[^()()a-zA-Z0-9_\u4e00-\u9fa5]"); if ((()) || ().Trim() == "") return new ValidationResult(false, "Please enter letters, numbers, underscores or Chinese characters"); else if (( as List<string>).Contains(())) return new ValidationResult(false, "Double name, please change the name"); else return new ValidationResult(true, null); } }
Finally, add a verification rule when input box data binding in XAML, and bind the name list of existing files to the verification rule parameters.
<ctoolkit:WatermarkTextBox x:Name="FileNameWTextBox" Watermark="Please enter the file name" ShowClearButton="True" Width="418" Height="30" HorizontalAlignment="Left" Margin="90,0,0,0"> <> <Binding Path="FileName" UpdateSourceTrigger="PropertyChanged"> <> <chelper:FileNameValidationRule> <chelper:> <chelper:ValidationParams Data="{Binding ,ElementName=self}"/> </chelper:> </chelper:FileNameValidationRule> </> </Binding> </> </ctoolkit:WatermarkTextBox>
However, things didn't go so smoothly, ValidationParams' Data is always empty, that is, the binding is unsuccessful. Why is this? After research, it was found that FileNameValidationRule is not on the visual tree and cannot inherit and access the DataContext, so the binding failed.
The solution to this problem is actually not too complicated (in fact, it took some time to find a solution). The idea is to use resource dictionary and Freezable classes.
- Even objects not in the logical tree can access resources through keys.
- The main purpose of the Freezable class is to define objects with modifiable states and read-only states, but luckily, instances of this class can also be inherited to DataContext in the visual tree or logical tree. I don't know the principle here at the moment.
Based on these two points of information, first define a class BindingProxy inherited from Freezable, which contains a dependency property DataProperty for binding data.
public class BindingProxy : Freezable { protected override Freezable CreateInstanceCore() { return new BindingProxy(); } public object Data { get { return (object)GetValue(DataProperty); } set { SetValue(DataProperty, value); } } // Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc... public static readonly DependencyProperty DataProperty = ("Data", typeof(object), typeof(BindingProxy), new PropertyMetadata(null)); }
Then instantiate BindingProxy in the resource dictionary of WatermarkTextBox, bind the list of existing file names, and then bind the BindingProxy instance in the Data of the verification rule parameter ValidationParams.
<ctoolkit:WatermarkTextBox x:Name="FileNameWTextBox" Watermark="Please enter the file name" ShowClearButton="True" Width="418" Height="30" HorizontalAlignment="Left" Margin="90,0,0,0"> <ctoolkit:> <chelper:BindingProxy x:Key="FileNamesProxy" Data="{Binding ,ElementName=self}"/> </ctoolkit:> //The existing codes in the above are omitted here... <chelper:ValidationParams Data="{Binding Source={StaticResource FileNamesProxy},Path=Data}"/> //The existing codes in the above are omitted here...</ctoolkit:WatermarkTextBox>
summary
In WPF, by default, DataContext is passed through a visual tree. The DataContext of the parent element is automatically passed to its child element so that the child element can access the parent element's data object. However, objects that are not on the visual tree cannot be inherited and bound directly to the DataContext. The case in this article is also stuck in this place. Although this problem was finally solved, the principle of how the Freezable class inherits to DataContext remains to be studied.
This is the article about the ValidationRule implementation parameter binding solution in WPF. For more related WPF ValidationRule parameter binding content, please search for my previous article or continue browsing the related articles below. I hope everyone will support me in the future!