如何让WPF中的ValidationRule实现参数绑定

如何,wpf,validationrule,实现,参数,绑定 · 浏览次数 : 379

小编点评

**问题分析:** ValidationParams的Data属性始终是空的,因为FileNameValidationRule不在可视化树中,无法直接访问DataContext。 **解决方案:** 使用Freezable类来创建BindingProxy实例,并将其绑定到Data属性中。 **步骤:** 1. 创建一个继承Freezable的类BindingProxy。 2. 在WatermarkTextBox的资源字典中实例化BindingProxy,并绑定已有文件名称列表。 3. 在ValidationParams的Data属性中绑定BindingProxy实例。 **代码示例:** ```csharp public class BindingProxy : Freezable { protected override Freezable CreateInstanceCore() { return new BindingProxy(); } public object Data { get { return (object)GetValue(DataProperty); } set { SetValue(DataProperty, value); } } } ``` **其他提示:** * Freezable类可以用于定义具有可修改状态和只读状态的对象。 * 在绑定参数之前,可以使用Freezable的GetDependencyTarget方法获取参数的依赖属性。

正文

背景

应用开发过程中,常常会对用户输入内容进行验证,通常是基于类型、范围、格式或者特定的要求进行验证,以确保输入符合预期。例如邮箱输入框校验输入内容是否符合邮箱格式。在WPF中,数据模型允许将ValidationRulesBinding对象关联,可以通过继承ValidationRule类并重写Validate方法来创建自定义规则。

问题

尽管创建自定义校验规则可以满足大部分应用场景,但是当我们校验规则是动态变化的时候就有些麻烦了。例如,开发一个文件管理系统,要求文件名不能与系统中已有的文件重名。这个时候需要先获取到系统中已有文件的名称列表,并绑定到ValidationRule上。然而ValidationRule不是继承于DepedencyObject,不能添加依赖属性,自定义的验证规则中的参数不支持绑定。

解决方案

接下来将给出一个解决方案,让ValidationRule支持参数绑定。思路如下:
首先自定义一个继承DepedencyObject的类ValidationParams,并在其中添加依赖属性用于绑定数据。

public class ValidationParams:DependencyObject
{
    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(ValidationParams), new PropertyMetadata(null));
}

然后在自定义校验规则FileNameValidationRule中添加ValidationParams类型的属性。

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 (reg.IsMatch(value.ToString()) || value.ToString().Trim() == "")
            return new ValidationResult(false, "请输入字母、数字、下划线或汉字");
        else if ((Params.Data as List<string>).Contains(value.ToString()))
            return new ValidationResult(false, "名称重复,请修改名称");
        else
            return new ValidationResult(true, null);
    }
}

最后在XAML中输入框数据绑定时添加校验规则,并把已有文件的名称列表绑定到校验规则参数中。

<ctoolkit:WatermarkTextBox x:Name="FileNameWTextBox" Watermark="请输入文件名称" ShowClearButton="True" Width="418" Height="30" HorizontalAlignment="Left" Margin="90,0,0,0">
    <TextBox.Text>
        <Binding Path="FileName" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <chelper:FileNameValidationRule>
                    <chelper:FileNameValidationRule.Params>
                        <chelper:ValidationParams Data="{Binding DataContext.ListFileName,ElementName=self}"/>
                    </chelper:FileNameValidationRule.Params>
                </chelper:FileNameValidationRule>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</ctoolkit:WatermarkTextBox>

然而,事情并没有那么顺利,ValidationParams的Data始终是空的,也就是绑定不成功。这是为什么呢?经过研究发现,FileNameValidationRule并不在可视化树上,无法继承和访问到DataContext,因此绑定失败。

解决这个问题的方法也不太复杂(其实找解决办法也是花了点时间)。思路是利用资源字典和Freezable类。

  • 即使不在逻辑树中的对象也可以通过key访问到资源。
  • Freezable类的主要目的是定义具有可修改状态和只读状态的对象,但是比较幸运的是这个类的实例不在可视化树或逻辑树中也可以继承到DataContext,目前我也不清楚这里的原理。

根据这两点信息,首先定义一个继承于Freezable的类BindingProxy,包含一个用于绑定数据的依赖属性DataProperty。

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 =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new PropertyMetadata(null));
}

然后在WatermarkTextBox的资源字典中实例化BindingProxy,并绑定已有文件名称列表,然后在校验规则参数ValidationParams的Data中绑定BindingProxy实例。

<ctoolkit:WatermarkTextBox x:Name="FileNameWTextBox" Watermark="请输入文件名称" ShowClearButton="True" Width="418" Height="30" HorizontalAlignment="Left" Margin="90,0,0,0">
    <ctoolkit:WatermarkTextBox.Resources>
        <chelper:BindingProxy x:Key="FileNamesProxy" Data="{Binding DataContext.ListFileName,ElementName=self}"/>
    </ctoolkit:WatermarkTextBox.Resources>
    //上文中已有代码此处省略...
    <chelper:ValidationParams Data="{Binding Source={StaticResource FileNamesProxy},Path=Data}"/>
    //上文中已有代码此处省略...
</ctoolkit:WatermarkTextBox>

小结

在WPF中,默认情况下,DataContext是通过可视化树来传递的。父元素的DataContext会自动传递给其子元素,以便子元素可以访问父元素的数据对象。但是,不在可视化树上的对象,无法继承和直接绑定到DataContext。本文的案例也是在这个地方卡壳了,虽然最终解决了这个问题,但是Freezable类如何继承到DataContext的原理还有待研究。

与如何让WPF中的ValidationRule实现参数绑定相似的内容:

如何让WPF中的ValidationRule实现参数绑定

###背景 应用开发过程中,常常会对用户输入内容进行验证,通常是基于类型、范围、格式或者特定的要求进行验证,以确保输入符合预期。例如邮箱输入框校验输入内容是否符合邮箱格式。在WPF中,数据模型允许将`ValidationRules`与`Binding`对象关联,可以通过继承`ValidationRu

[WPF]用HtmlTextBlock实现消息对话框的内容高亮和跳转

动手写一个简单的消息对话框一文介绍了如何实现满足常见应用场景的消息对话框。但是内容区域的文字仅仅起到信息展示作用,对于需要部分关键字高亮,或者部分内容有交互性的场景(例如下图提示信息中的“what's the risk?”需要跳转)则无能为力了。本文将介绍如何在WPF中灵活的实现消息对话框中局部文字

WPF/C#:如何将数据分组显示

WPF Samples中的示例 在WPF Samples中有一个关于Grouping的Demo。 该Demo结构如下: MainWindow.xaml如下:

WPF/C#:如何实现拖拉元素

前言 在Canvas中放置了一些元素,需要能够拖拉这些元素,在WPF Samples中的DragDropObjects项目中告诉了我们如何实现这种效果。 效果如下所示: 拖拉过程中的效果如下所示: 具体实现 xaml页面 我们先来看看xaml:

WPF/C#:实现导航功能

前言 在WPF中使用导航功能可以使用Frame控件,这是比较基础的一种方法。前几天分享了wpfui中NavigationView的基本用法,但是如果真正在项目中使用起来,基础的用法是无法满足的。今天通过wpfui中的mvvm例子来说明在wpfui中如何通过依赖注入与MVVM模式使用导航功能。实践起来

WPF/C#:在WPF中如何实现依赖注入

本文先介绍依赖注入的概念,再解释为什么要进行依赖注入,最后通过 WPF Gallery 这个项目学习如何在WPF中使用依赖注入。

WPF/C#:数据绑定到方法

在WPF Samples中有一个关于数据绑定到方法的Demo,该Demo结构如下: 运行效果如下所示: 来看看是如何实现的。 先来看下MainWindow.xaml中的内容:

CefSharp自定义滚动条样式

在WinForm/WPF中使用CefSharp混合开发时,通常需要自定义滚动条样式,以保证应用的整体风格统一。本文将给出一个简单的示例介绍如何自定义CefSharp中滚动条的样式。 基本思路 在前端开发中,通过CSS来控制滚动条的样式是件寻常的事情。CefSharp也提供了功能强大的API方便开发人

【炫丽】从0开始做一个WPF+Blazor对话小程序

大家好,我是沙漠尽头的狼。 .NET是免费,跨平台,开源,用于构建所有应用的开发人员平台。 本文演示如何在WPF中使用Blazor开发漂亮的UI,为客户端开发注入新活力。 注 要使WPF支持Blazor,.NET版本必须是 6.0 或更高版本,本文所有示例使用的.NET 7.0,版本要求见链接,截图

WPF/C#:在DataGrid中显示选择框

前言 在使用WPF的过程中可能会经常遇到在DataGrid的最前或者最后添加一列选择框的需求,今天跟大家分享一下,在自己的项目中是如何实现的。 整体实现效果如下: 如果对此感兴趣,可以接下来看具体实现部分。 实践 假设数据库中的模型如下: public class Person { public i