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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

4.【Orangepi Zero2】Linux定时器(signal、setitimer),软件PWM驱动舵机(SG90)

Linux定时器&#xff08;signal、setitimer&#xff09;&#xff0c;软件PWM驱动舵机&#xff08;SG90&#xff09; signalsetitimer示例 软件PWM驱动舵机&#xff08;SG90&#xff09; signal 详情请看Linux 3.进程间通信&#xff08;shmget shmat shmdt shmctl 共享内存、si…

【计算机网络】循环冗余校验:Cyclic Redundancy Check

1. 任务目标 利用循环冗余校验&#xff08;CRC&#xff09;检测错误。 循环冗余校验&#xff08;英语&#xff1a;Cyclic redundancy check&#xff0c;通称 CRC&#xff09;是一种根据网上数据包或计算机文件等数据产生简短固定位数校验码的一种散列函数&#xff0c;主要用来…

智慧文旅展现文化新风貌,科技助力旅行品质升级:借助智慧技术,文旅产业焕发新生机,为旅行者带来更高品质的文化体验之旅

一、引言 在数字化、智能化的浪潮下&#xff0c;文旅产业正迎来前所未有的发展机遇。智慧文旅作为文旅产业与信息技术深度融合的产物&#xff0c;不仅为旅行者带来了全新的文化体验&#xff0c;也为文旅产业注入了新的活力。本文旨在探讨智慧文旅如何借助智慧技术展现文化新风…

C++:二叉搜索树的底层模拟实现

概念&#xff1a; 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树&#xff1a; 搜索二叉树的操作&#xff1a; int a[] {8, 3, 1, 10, 6, 4, 7, 14, 13};二叉搜索树需要满足左子树比根小&#xff0c;右子树比根大&#xff0c;…

Leetcode—1235. 规划兼职工作【困难】(upper_bound、自定义排序规则)

2024每日刷题&#xff08;125&#xff09; Leetcode—1235. 规划兼职工作 算法思想 实现代码 class Solution { public:int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {int n startTime.size();vec…

循环神经网络完整实现(Pytorch 13)

一 循环神经网络的从零开始实现 从头开始基于循环神经网络实现字符级语言模型。 %matplotlib inline import math import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2lbatch_size, num_steps 32, 35 train_iter, vocab …

(六)SQL系列练习题(下)#CDA学习打卡

目录 三. 查询信息 16&#xff09;检索"1"课程分数小于60&#xff0c;按分数降序排列的学生信息​ 17&#xff09;*按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩 18&#xff09;*查询各科成绩最高分、最低分和平均分 19&#xff09;*按各科成绩…

PHP 反序列化

一、PHP 序列化 1、对象的序列化 <?php class people{public $nameGaming;private $NationLiyue;protected $Birthday12/22;public function say(){echo "老板你好呀&#xff0c;我是和记厅的镖师&#xff0c;叫我嘉明就行&#xff0c;要运货吗你&#xff1f;"…

手机恢复出厂设置ip地址会变吗

当我们对手机进行恢复出厂设置时&#xff0c;很多人会担心手机的IP地址是否会发生变化。IP地址对于手机的网络连接至关重要&#xff0c;它决定了手机在网络中的身份和位置。那么&#xff0c;手机恢复出厂设置后&#xff0c;IP地址到底会不会发生变化呢&#xff1f;虎观代理小二…

Jenkins docker部署springboot项目

1、创建jenkins容器 1&#xff0c;首先&#xff0c;我们需要创建一个 Jenkins 数据卷&#xff0c;用于存储 Jenkins 的配置信息。可以通过以下命令创建一个数据卷&#xff1a; docker volume create jenkins_data启动 Jenkins 容器并挂载数据卷&#xff1a; docker run -dit…

Python量化择时的技术指标函数

Python量化择时的技术指标函数 技术指标通过对原始数据&#xff08;开盘价、收盘价、最低价、最高价、成交量、成交金额、成交笔数&#xff09;的处理&#xff0c;来反映出市场的某一方面深层的内涵&#xff0c;这些内涵是很难通过原始数据直接看出来的。技术指标能客观地反映…

EXCEL怎样把筛选后含有公式的数据,复制粘贴到同一行的其它列?

自excel2003版之后&#xff0c;常规情况下&#xff0c;复制筛选后的数据&#xff0c;会忽略隐藏行&#xff0c;仅复制其筛选后的数据&#xff0c;粘贴则是粘贴到连续单元格区域&#xff0c;不管行是在显示状态还是隐藏状态。 一、初始数据&#xff1a; 二、题主的复制粘贴问题…

Codigger数据篇(下):数据安全的全方位保障

在数字化浪潮中&#xff0c;数据已成为现代企业的核心财富。Codigger作为领先的数据服务平台&#xff0c;深知数据安全对于用户的重要性&#xff0c;因此在深挖数据价值的同时&#xff0c;我们始终坚守数据安全防线。 一、双重加密技术保障 Codigger平台运用先进的加密通信和…

C语言学习【最基本】

C语言学习 简单的 C 程序示例 #include "stdio.h" /* 提供键盘输入与屏幕输出支持 */ /* 相当于把stdio.h文件中的所有内容都输入到该行所在位置 拷贝-粘贴 *//* void 表示不带任何参数 */ int main(void) /* 函数名 */ { …

UE—动画

1.动画蓝图 创建动画蓝图 在蓝图中添加状态机 状态机中状态的转换 转换条件设定 播放的动画 使用动画资源 使用混合空间 2.混合空间 混合空间1D 阿赵UE学习笔记——26、动画混合空间_ue 一维动画混合空间-CSDN博客 蓝图创建 混合空间内 按Ctrl到动画节点上即可预览 修改…

1. 傅里叶变换原理

1. 频率域的引入 1.1 时域角度 1.2. 频域角度 不同的角度表达的是同一件事情&#xff0c;从时间域和空间域来进行表达同一间事情 。时间域是都动态的&#xff0c;频率域是静止的 1.3. 时域角度和频域角度 1.4 相位 2 函数的时域角度 2.1 时间域 2.2 频率域 2.3 例子 2.3…

Spring扩展点(一)Bean生命周期扩展点

Bean生命周期扩展点 影响多个Bean的实例化InstantiationAwareBeanPostProcessorBeanPostProcessor 影响单个Bean的实例化纯粹的生命周期回调函数InitializingBean&#xff08;BeanPostProcessor 的before和after之间调用&#xff09;DisposableBean Aware接口在生命周期实例化过…

eSIM Network搭建指南

有任何关于GSMA\IOT\eSIM\RSP\业务应用场景相关的问题&#xff0c;欢迎W: xiangcunge59 一起讨论, 共同进步 (加的时候请注明: 来自CSDN-iot).

redis 缓存一致性,缓存穿透,缓存雪崩,缓存击穿

1.缓存一致性&#xff1a; 缓存一致性就是通过各种方法保证缓存与数据库信息一种&#xff0c;其中最多的办法就是想尽一切办法对过期key进行清除&#xff0c;以保证redis和数据库信息一只&#xff0c;其中就包括了这篇文章中提到的内存淘汰策略&#xff0c;过期key的清除等等&…

MongoDB的分片集群

MongoDB分片技术 介绍 ​ 分片&#xff08;sharding&#xff09;是MongoDB用来将大型集合分割到不同服务器上采用的方法。分片这种说法起源于关系型数据库。但是实际上非关系型数据库在分片方面相比于传统的关系型数据库更有优势。 ​ 与MySQL分库方案对比&#xff0c;MongoDB…