WPF之绑定验证(错误模板使用)

news2024/10/6 6:50:13

1,前言:

         默认情况下,WPF XAML 中使用的绑定并未开启绑定验证,这样导致用户在UI上对绑定的属性进行赋值时即使因不符合规范内部已抛出异常(此情况仅限WPF中的数据绑定操作),也被程序默认忽略,UI层面也无异常提示,无法确定值是否已更改。而这些问题可通过Validation提供的附加属性,附加事件,错误模板进行检测提示,从而有效的解决绑定中产生的异常问题。

例如以下情况:

<TextBox>
    <TextBox.Text>
         <Binding Path="UnitCost"  >                                                       
         </Binding>
    </TextBox.Text>
 </TextBox>
public decimal UnitCost
        {
            get { return unitCost; }
            set
            {
                //测试UI属性绑定异常抛出捕捉
                if (value < 0)
                {
                    throw new ArgumentException("值不能小于0");
                }
                unitCost = value;
                OnPropertyChanged(nameof(UnitCost));
            }
        }

        在UI层面用户通过绑定将当前的 UnitCost 值设置为小于0时(仅限通过绑定输入的值),虽在代码中已产生异常,但运行程序对该绑定中产生的异常默认进行了忽略,不提示异常,导致该值是否已更新,无法确定。

2,数据验证的应用。

         数据绑定进行数据源更新时先进行验证再进行装换,所以对于文本框而言,数据在验证时都是字符串型。

2.1,开启绑定中的验证通知:NotifyOnValidationError="True"

<TextBox  Grid.Row="2" Grid.Column="1">
                    <TextBox.Text>
                        <Binding Path="UnitCost" NotifyOnValidationError="True" ValidatesOnExceptions="True"  >
                            
                        </Binding>
                    </TextBox.Text>
                </TextBox>

2.2,开启异常验证捕捉规则:ValidatesOnExceptions="True",用于捕捉在该绑定中产生的任何异常(可选)。

        此设定与以下绑定  ExceptionValidationRule 异常验证规则等同 :

<TextBox  Grid.Row="2" Grid.Column="1">
                    <TextBox.Text>
                        <Binding Path="UnitCost" NotifyOnValidationError="True"  >
                            <Binding.ValidationRules>
                                <ExceptionValidationRule></ExceptionValidationRule>
                            </Binding.ValidationRules>
                        </Binding>
                    </TextBox.Text>
                </TextBox>

2.3,绑定自定义验证规则。

        自定义的验证规则类需要继承自System.Windows.Controls下的抽象类ValidationRule。

 class RangeValidationRule : ValidationRule
    {
        /// <summary>
        /// 范围上限
        /// </summary>
        public decimal MaxNum { get; set; } = 10000;
        /// <summary>
        /// 范围下限
        /// </summary>
        public decimal MinNum { get; set; }
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            string valStr = value.ToString();
            if (string.IsNullOrEmpty(valStr))
            {
                return new ValidationResult(false, "不能为空值");
            }
            decimal val;
            if(!decimal.TryParse(valStr,NumberStyles.Any,cultureInfo,out val))
            {
                return new ValidationResult(false, "输入的内容非法,请输入有效的货币值");
            }
            if(val>MaxNum || val < MinNum)
            {
                return new ValidationResult(false, $"只能是:{MinNum} - {MaxNum}之间的货币值");
            }
            return new ValidationResult(true, "");
        }
    }

2.4,在绑定中添加自定义的验证规则,并设置相应属性。

 <TextBox  Grid.Row="2" Grid.Column="1">
                    <TextBox.Text>
                        <Binding Path="UnitCost" NotifyOnValidationError="True" ValidatesOnExceptions="True"  >
                            <Binding.ValidationRules>
                                <local:RangeValidationRule MinNum="10" MaxNum="10000"></local:RangeValidationRule>
                            </Binding.ValidationRules>
                        </Binding>
                    </TextBox.Text>
                </TextBox>

2.5,验证失败时WPF自动将控件使用的模板切换为由Validation.ErrorTemplate附加属性定义的模板。在文本框中新模板将文本框的轮过改成一条细的红色边框(默认错误模板)。

3,使用Validation提供的附加属性,附加事件对异常进行处理。

Validation.HasError附加属性,验证当前元素是否存在验证错误
Validation.Error附加事件,当前元素验证错误事件(路由事件)
Validation.Errors附加属性,当前元素产生的验证错误信息集合
Validation.ErrorTemplate附加属性,当前的元素的错误模板(模板类型:ControlTemplate)

        Validation.ErrorEvent为附加事件即为路由事件,所以可在其父容器进行注册监听。

3.1,在父容器添加附加事件,监听子元素产生的验证错误。

Validation.Error="Grid_Error"

        示例:

 <Grid Margin="10" DataContext="{Binding ElementName=listBox01, Path=SelectedItem}" Validation.Error="Grid_Error">
                <Grid.RowDefinitions>
                    <RowDefinition ></RowDefinition>
                    <RowDefinition ></RowDefinition>
                    <RowDefinition ></RowDefinition>
                    <RowDefinition ></RowDefinition>
                    <RowDefinition Height="3*"></RowDefinition>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="auto"></ColumnDefinition>
                    <ColumnDefinition ></ColumnDefinition>
                </Grid.ColumnDefinitions>
               
                <TextBlock Text="Model Number"></TextBlock>
                <TextBox Grid.Column="1" Text="{Binding ModelNumber, TargetNullValue=[Empty]}"></TextBox>
                <TextBlock Grid.Row="1" Text="Model Name"></TextBlock>
                <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding ModelName, TargetNullValue=[Empty]}"></TextBox>
                <TextBlock Grid.Row="2" Text="Unit Cost"></TextBlock>
                <TextBox  Grid.Row="2" Grid.Column="1">
                    <TextBox.Text>
                        <Binding Path="UnitCost" NotifyOnValidationError="True" ValidatesOnExceptions="True"  >
                            <Binding.ValidationRules>
                                <local:RangeValidationRule MinNum="10" MaxNum="10000"></local:RangeValidationRule>
                            </Binding.ValidationRules>
                        </Binding>
                    </TextBox.Text>
                </TextBox>
                <TextBlock Grid.Row="3" Text="Descriptionz:"></TextBlock>
                <TextBox  Grid.Row="4" Grid.ColumnSpan="2" Style="{x:Null}" Text="{Binding Description}"></TextBox>
            </Grid>
        </Border>
    </Grid>
  private void Grid_Error(object sender, ValidationErrorEventArgs e)
        {
            if (e.Action == ValidationErrorEventAction.Added)
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine($"RoutedEvent:{e.RoutedEvent.Name}");
                sb.AppendLine($"Source:{e.Source}");
                sb.AppendLine($"ErrorContent:{e.Error.ErrorContent}");
                sb.AppendLine($"{e.Error.RuleInError.GetType().Name}");
                //sb.AppendLine($"Message:{e.Error.Exception.Message}");
                MessageBox.Show(sb.ToString());
            }
        }

3.2,根据当前元素的是否出现验证错误进行样式设置。

<Trigger Property="Validation.HasError" Value="true">

        示例:

<Trigger Property="Validation.HasError" Value="true">                               
                                <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" >                                  
                                </Setter>
                            </Trigger>

3.3,绑定当前元素的当前验证错误信息。

Path=(Validation.Errors)[0].ErrorContent

        示例:

 <Style.Triggers>
                            <Trigger Property="Validation.HasError" Value="true">
                                <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" >
                                </Setter>
                            </Trigger>
                        </Style.Triggers>

        注意此时的附加属性样式与Path关联的附加属性样式有差异:

        Property中附加属性无括号包裹:

<Trigger Property="Validation.HasError" Value="true">

        Path中的附加属性需用括号包裹

<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" >

4,错误模板应用。

        当验证控件的Validation.HasError属性被设置为true时,WPF自动将控件使用的模板切换为由Validation.ErrorTemplate附加属性定义的模板。在文本框中新模板将文本框的轮过改成一条细的红色边框。

        ErrorTemplate的类型为ControlTemplate。

4.1,自定义错误模板。

 <Style TargetType="TextBox">
                        <Setter Property="Margin" Value="0,3"></Setter>
                        <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
                        <Setter Property="Validation.ErrorTemplate">
                            <Setter.Value>
                                <ControlTemplate>
                                    <Border BorderBrush="Green" BorderThickness="1">
                                        <DockPanel LastChildFill="True">
                                        <TextBlock  DockPanel.Dock="Right" Background="Red" ToolTip="{Binding ElementName=adornedElement1, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" HorizontalAlignment="Center">*</TextBlock>
                                            <AdornedElementPlaceholder x:Name="adornedElement1"></AdornedElementPlaceholder>
                                    </DockPanel>
                                    </Border>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>

                    </Style>

        错误模板是使用装饰层,装饰层位于普通窗口之上的绘图层。

<AdornedElementPlaceholder x:Name="adornedElement1"></AdornedElementPlaceholder>

        AdornedElementPlaceholder,这里指代被修饰的元素即文本框(AdornedElementPlaceholder必须位于ControlTemplate中,为固定写法。)。

4.2,通过AdornedElementPlaceholder获取被修饰对象上的验证错误信息。

<TextBlock  DockPanel.Dock="Right" Background="Red" ToolTip="{Binding ElementName=adornedElement1, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" HorizontalAlignment="Center">*</TextBlock>

5,效果

6,Demo链接:

https://download.csdn.net/download/lingxiao16888/89263053

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1642622.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux设置脚本任意位置执行

记得备份 &#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 修改文件之后记得用 source 文件名 刷新 注意&#xff1a;刷新文件之后在当前窗口…

02.zabbix配置web界面

zabbix配置web界面 访问搭建好的地址&#xff1a; http://192.168.111.66/zabbix 检查配置都是正常&#xff0c;下一步 对应的信息&#xff0c;我设置的密码是&#xff1a;123456&#xff0c;下一步即可&#xff1b; 给服务器随意设置一个名字&#xff0c;下一步 检查数据…

022、Python+fastapi,第一个Python项目走向第22步:ubuntu 24.04 docker 安装mysql8集群、redis集群(三)

这次来安装mysql8了&#xff0c;以前安装不是docker安装&#xff0c;这个我也是第一次&#xff0c;人人都有第一次嚒 前言 前面的redis安装还是花了点时间的&#xff0c;主要是网上教程&#xff0c;各有各的好&#xff0c;大家千万别取其长处&#xff0c;个人觉得这个环境影响…

一、RocketMQ基本概述与部署

RocketMQ基本概述与安装 一、概述1.MQ概述1.1 用途1.2 常见MQ产品1.3 MQ常用的协议 2.RocketMQ概述2.1 发展历程 二、相关概念1.基本概念1.1 消息&#xff08;Message&#xff09;1.2 主题&#xff08;Topic&#xff09;1.3 标签&#xff08;Tag&#xff09;1.4 队列&#xff0…

stamps做sbas-insar,时序沉降图怎么画?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

【计算机网络】计算机网络的定义和分类

一.定义 计算机网络并没有一个精确和统一的定义&#xff0c;在计算机网络发展的不同阶段&#xff0c;人们对计算机网络给出了不同的定义&#xff0c;这些定义反映了当时计算机网络技术的发展水平。 例如计算机网络早期的一个最简单定义&#xff1a;计算机网络是一些互连的、自…

短视频素材去哪里找免费?短视频素材从哪儿下载?

在这个数字内容为王的时代&#xff0c;视频已经成为沟通信息和吸引观众的强大工具。无论是在市场营销、教育还是娱乐领域&#xff0c;高质量的视频素材都是制作引人注目内容的关键。以下列出的网站提供多样的视频素材&#xff0c;帮助您增强视觉叙述&#xff0c;并在竞争激烈的…

查找算法与排序算法

查找算法 二分查找 (要求熟练) // C// 二分查找法&#xff08;递归实现&#xff09; int binarySearch(int *nums, int target, int left, int right) // left代表左边界&#xff0c;right代表右边界 {if (left > right) return -1; // 如果左边大于右边&#xff0c;那么…

Docker部署nginx并且实现https访问

实验环境&#xff1a; 在已有的docker环境和nginx镜像的基础上进行操作 1、生成私钥 &#xff08;1&#xff09;openssl genrsa -out key.pem 2048 生成证书签名请求 (CSR) 并自签证书: &#xff08;2&#xff09;openssl req -new -x509 -key key.pem -out cert.pem -day…

Vitis HLS 学习笔记--HLS流水线基本用法

目录 1. 简介 2. 示例 2.1 对内层循环打拍 2.2 对外层循环打拍 2.3 优化数组访问后打拍 3. 总结 1. 简介 本文介绍pipeline的基本用法。pipeline是一种用于提高硬件设计性能的技术。本文介绍了pipeline在累加计算函数中的应用。通过优化内外层循环和数组访问&#xff0c…

C#中.net8WebApi加密解密

尤其在公网之中&#xff0c;数据的安全及其的重要&#xff0c;除过我们使用jwt之外&#xff0c;还可以对传送的数据进行加密&#xff0c;就算别人使用抓包工具&#xff0c;抓到数据&#xff0c;一时半会儿也解密不了数据&#xff0c;当然&#xff0c;加密也影响了效率&#xff…

【Linux】awk命令学习

最近用的比较多&#xff0c;学习总结一下。 文档地址&#xff1a;https://www.gnu.org/software/gawk/manual/gawk.html 一、awk介绍二、语句结构1.条件控制语句1&#xff09;if2&#xff09;for3&#xff09;while4&#xff09;break&continue&next&exit 2.比较运…

线性数据结构-手写链表-LinkList

为什么需要手写实现数据结构&#xff1f; 其实技术的本身就是基础的积累和搭建的过程&#xff0c;基础扎实 地基平稳 万丈高楼才会久战不衰&#xff0c;做技术能一通百&#xff0c;百通千就不怕有再难得技术了。 一&#xff1a;链表的分类 主要有单向&#xff0c;双向和循环链表…

陈随易:论技术思维和产品思维

大家好&#xff0c;我是不被定义的前端之虎陈随易。 我的个人网站是&#xff1a;https://chensuiyi.me&#xff0c;欢迎大家眼熟我。 写这篇文章呢&#xff0c;源于一次群聊。 群友有一个产品&#xff0c;其中涉及到免费用户和付费用户对 pdf 的查看权限问题&#xff0c;使用…

EPAI手绘建模APP颜色、贴图、材质、样式

⑦ 颜色选择页面 1) 颜色环选色。 图 65 颜色选择器-颜色环 2) RGB选色。 图 66 颜色选择器-RGB 3) HSL选色。 图 67 颜色选择器-HSL 4) 国风颜色库选色。 图 68 颜色选择器-国风 5) CSS颜色库选色。 图 69 颜色选择器-CSS 6) 历史颜色&#xff1a;保存最近使用的多个颜色&…

鸿蒙开发仿咸鱼TabBar

鸿蒙开发自定义TabBar&#xff0c;实现tabBar 上中间按钮凸起效果 第一步、定义数据模型 export default class TabItemData{defaultIcon: ResourceselectedIcon: Resourcetitle: stringisMiddle: booleanconstructor(defaultIcon:Resource, selectedIcon:Resource, title:st…

基于改进暗原色先验和颜色校正的水下图像增强,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

初始化Linux或者Mac下Docker运行环境

文章目录 1 Mac下安装Docker2 Linux下安装Docker2.1 确定Linux版本2.2 安装Docker2.3 配置加速镜像 3 Docker安装校验4 安装docker-compose4.1 直接下载二进制文件4.2 移动二进制文件到系统路径4.3 设置可执行权限4.4 验证安装 1 Mac下安装Docker mac 安装 docker 还是比较方便…

OpenCV如何为我们的应用程序添加跟踪栏(71)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:OpenCV的周期性噪声去除滤波器(70) 下一篇 :OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 在前面的教程中&#xff08;关于使用 OpenCV 添加&#xff08;混合&#xff09;两个图像和…

基于SSM的“软件缺陷管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SSM的“软件缺陷管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能结构图 管理员登录 首页 项目经理列表 项目经理添加…