当前位置: 首页 > news >正文

WPF进阶 | WPF 数据绑定进阶:绑定模式、转换器与验证

在这里插入图片描述
在这里插入图片描述

WPF进阶 | WPF 数据绑定进阶:绑定模式、转换器与验证

  • 一、前言
  • 二、WPF 数据绑定基础回顾
    • 2.1 数据绑定的基本概念
    • 2.2 数据绑定的基本语法
  • 三、绑定模式
    • 3.1 单向绑定(One - Way Binding)
    • 3.2 双向绑定(Two - Way Binding)
    • 3.3 单向到源绑定(One - Way - To - Source Binding)
    • 3.4 一次性绑定(One - Time Binding)
  • 四、数据绑定转换器
    • 4.1 转换器的概念与作用
    • 4.2 实现IValueConverter接口
    • 4.3 在 XAML 中使用转换器
    • 4.4 多值转换器(Multi - Value Converter)
  • 五、数据验证
    • 5.1 数据验证的重要性
    • 5.2 基于绑定的验证
    • 5.3 实现IDataErrorInfo接口
    • 5.4 实现INotifyDataErrorInfo接口
    • 5.5 数据验证的综合应用
  • 六、总结
  • 结束语
  • 优质源码分享

WPF进阶 | WPF 数据绑定进阶:绑定模式、转换器与验证 ,在 WPF 应用程序开发中,数据绑定是连接用户界面(视图)与应用程序数据(模型)的桥梁。它使得视图能够自动反映数据的变化,反之亦然,极大地提高了开发效率和代码的可维护性。基础的数据绑定知识是开发者的必备技能,而深入理解绑定模式、掌握转换器的使用以及实现有效的数据验证,则是构建复杂且健壮的 WPF 应用程序的关键。本文将深入探讨这些进阶主题,帮助开发者充分发挥 WPF 数据绑定的强大功能。

一、前言

    在数字浪潮汹涌澎湃的时代,程序开发宛如一座神秘而宏伟的魔法城堡,矗立在科技的浩瀚星空中。代码的字符,似那闪烁的星辰,按照特定的轨迹与节奏,组合、交织、碰撞,即将开启一场奇妙且充满无限可能的创造之旅。当空白的文档界面如同深邃的宇宙等待探索,程序员们则化身无畏的星辰开拓者,指尖在键盘上轻舞,准备用智慧与逻辑编织出足以改变世界运行规则的程序画卷,在 0 和 1 的二进制世界里,镌刻下属于人类创新与突破的不朽印记。

    在当今数字化时代,桌面应用程序的用户界面(UI)设计至关重要,它直接影响着用户体验与产品的竞争力。而 WPF(Windows Presentation Foundation)作为微软推出的一款强大的 UI 框架,其布局系统更是构建精美界面的核心要素。WPF 布局系统为开发者提供了丰富多样的布局方式,能够轻松应对各种复杂的界面设计需求,无论是简洁明了的工具软件,还是功能繁杂的企业级应用,都能借助其打造出令人惊艳的视觉效果与流畅的交互体验。

    WPF从入门到精通专栏,旨在为读者呈现一条从对 WPF(Windows Presentation Foundation)技术懵懂无知到精通掌握的学习路径。首先从基础入手,介绍 WPF 的核心概念,涵盖其独特的架构特点、开发环境搭建流程,详细解读布局系统、常用控件以及事件机制等基础知识,帮助初学者搭建起对 WPF 整体的初步认知框架。随着学习的深入,进阶部分聚焦于数据绑定、样式模板、动画特效等关键知识点,进一步拓展 WPF 开发的能力边界,使开发者能够打造出更为个性化、交互性强的桌面应用界面。高级阶段则涉及自定义控件开发、MVVM 设计模式应用、多线程编程等深层次内容,助力开发者应对复杂的业务需求,构建大型且可维护的应用架构。同时,通过实战项目案例解析,展示如何将所学知识综合运用到实际开发中,从需求分析到功能实现再到优化测试,全方位积累实践经验。此外,还探讨了性能优化、与其他技术集成以及安全机制等拓展性话题,让读者对 WPF 技术在不同维度有更深入理解,最终实现对 WPF 技术的精通掌握,具备独立开发高质量桌面应用的能力。

🛕 点击进入WPF从入门到精通专栏

在这里插入图片描述

二、WPF 数据绑定基础回顾

2.1 数据绑定的基本概念

    数据绑定是一种将数据源(如对象的属性)与目标元素(如 UI 控件的属性)连接起来的机制。通过数据绑定,当数据源发生变化时,目标元素能够自动更新以反映这些变化;反之,当目标元素的值改变时,数据源也可以相应地更新。例如,将一个 TextBoxText 属性绑定到一个视图模型中的字符串属性,当用户在 TextBox 中输入内容时,视图模型中的属性值会自动更新,反之亦然。

2.2 数据绑定的基本语法

    在 XAML 中,数据绑定的基本语法如下:

<TextBox Text="{Binding Path=MyProperty, Source={StaticResource MyDataSource}}"/>

    上述代码中,Path指定了要绑定的数据源属性,Source指定了数据源对象。如果数据源是视图模型,并且视图的DataContext已经设置为该视图模型实例,那么可以省略Source属性,简化为:

<TextBox Text="{Binding Path=MyProperty}"/>

三、绑定模式

3.1 单向绑定(One - Way Binding)

定义与原理

    单向绑定是指数据从数据源流向目标元素。当数据源中的属性值发生变化时,目标元素会自动更新,但目标元素的值变化不会影响数据源。例如,将一个 LabelContent 属性绑定到一个视图模型中的字符串属性,用于显示一些只读信息。

<Label Content="{Binding Path=ReadOnlyProperty}"/>

    在代码背后,当ReadOnlyProperty的值发生变化时,Label 的显示内容会自动更新。

public class MyViewModel
{private string _readOnlyProperty;public string ReadOnlyProperty{get { return _readOnlyProperty; }set{if (_readOnlyProperty!= value){_readOnlyProperty = value;// 通知属性更改(通常使用 INotifyPropertyChanged 接口)}}}
}

适用场景

    单向绑定适用于显示静态数据或不需要用户交互修改的数据,如显示应用程序的版本号、当前登录用户的名称等。

3.2 双向绑定(Two - Way Binding)

定义与原理

    双向绑定允许数据在数据源和目标元素之间双向流动。即当数据源的属性值改变时,目标元素会更新;反之,当目标元素的值改变时,数据源的属性值也会相应更新。例如,在一个文本框中输入用户信息,然后将这些信息保存到视图模型的属性中。

<TextBox Text="{Binding Path=UserInput, Mode=TwoWay}"/>

    在视图模型中,当UserInput属性的值发生变化时,TextBox 的文本会更新;当用户在 TextBox 中输入内容时,UserInput属性的值也会随之改变。

public class MyViewModel
{private string _userInput;public string UserInput{get { return _userInput; }set{if (_userInput!= value){_userInput = value;// 通知属性更改(通常使用 INotifyPropertyChanged 接口)}}}
}

适用场景

    双向绑定常用于用户输入数据的场景,如登录界面的用户名和密码输入框、表单填写等。

3.3 单向到源绑定(One - Way - To - Source Binding)

定义与原理

    单向到源绑定与单向绑定相反,数据从目标元素流向数据源。当目标元素的值发生变化时,数据源的属性值会更新,但数据源的变化不会影响目标元素。例如,在一个滑块(Slider)控件中,用户通过拖动滑块改变值,这个值需要更新到视图模型的某个属性中。

<Slider Value="{Binding Path=SliderValue, Mode=OneWayToSource}"/>

    当用户拖动滑块时,SliderValue属性的值会更新。

public class MyViewModel
{private double _sliderValue;public double SliderValue{get { return _sliderValue; }set{if (_sliderValue!= value){_sliderValue = value;// 通知属性更改(通常使用 INotifyPropertyChanged 接口)}}}
}

适用场景

    单向到源绑定适用于用户操作引发数据更新,但不需要实时反映到 UI 上的场景,如一些后台配置参数的设置。

3.4 一次性绑定(One - Time Binding)

定义与原理

    一次性绑定只在数据绑定建立时将数据源的值应用到目标元素,之后数据源的变化不会再影响目标元素。这种绑定方式适用于数据在初始化后不会再改变的情况。

<TextBlock Text="{Binding Path=InitialValue, Mode=OneTime}"/>

    数据绑定建立时,InitialValue的值会被设置到 TextBlockText 属性中,之后InitialValue的任何变化都不会影响 TextBlock 的显示。

public class MyViewModel
{private string _initialValue;public string InitialValue{get { return _initialValue; }set{if (_initialValue!= value){_initialValue = value;// 通知属性更改(通常使用 INotifyPropertyChanged 接口)}}}
}

适用场景

    一次性绑定适用于显示一些初始配置信息或不经常变化的数据,如应用程序启动时加载的版权声明等。

四、数据绑定转换器

4.1 转换器的概念与作用

    数据绑定转换器是实现IValueConverter接口的类,用于在数据源和目标元素之间进行数据转换。有时候,数据源的数据类型与目标元素所需的数据类型不匹配,或者需要对数据进行格式化处理,这时就需要使用转换器。例如,将一个表示日期的DateTime对象转换为特定格式的字符串,以便在 TextBlock 中显示。

4.2 实现IValueConverter接口

Convert 方法

    Convert方法用于将数据源的值转换为目标元素所需的值。例如,将一个布尔值转换为可见性(Visibility)枚举值,用于控制控件的显示与隐藏。

public class BooleanToVisibilityConverter : IValueConverter
{public object Convert(object value, Type targetType, object parameter, CultureInfo culture){if (value is bool boolValue){return boolValue? Visibility.Visible : Visibility.Collapsed;}return Visibility.Collapsed;}public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){throw new NotImplementedException();}
}

ConvertBack 方法

    ConvertBack方法用于将目标元素的值转换回数据源的值,通常在双向绑定中使用。例如,将一个表示可见性的枚举值转换回布尔值。

public class VisibilityToBooleanConverter : IValueConverter
{public object Convert(object value, Type targetType, object parameter, CultureInfo culture){if (value is Visibility visibility){return visibility == Visibility.Visible;}return false;}public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){if (value is bool boolValue){return boolValue? Visibility.Visible : Visibility.Collapsed;}return Visibility.Collapsed;}
}

4.3 在 XAML 中使用转换器

    在 XAML 中,可以通过以下方式使用转换器:

<Window.Resources><local:BooleanToVisibilityConverter x:Key="boolToVisConverter"/>
</Window.Resources>
<Button Visibility="{Binding Path=IsButtonVisible, Converter={StaticResource boolToVisConverter}}"/>

    上述代码中,IsButtonVisible是视图模型中的一个布尔属性,通过BooleanToVisibilityConverter将其转换为 ButtonVisibility 属性所需的值。

4.4 多值转换器(Multi - Value Converter)

概念与应用场景

    多值转换器实现IMultiValueConverter接口,它可以将多个数据源的值转换为一个目标值。例如,在一个用户登录界面中,需要根据用户名和密码是否都不为空来决定登录按钮是否可用。这时可以使用多值转换器,将用户名和密码两个属性的值作为输入,输出一个布尔值来控制按钮的 IsEnabled 属性。

实现IMultiValueConverter接口

public class UsernamePasswordToButtonEnabledConverter : IMultiValueConverter
{public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture){if (values!= null && values.Length == 2 &&values[0] is string username &&!string.IsNullOrEmpty(username) &&values[1] is string password &&!string.IsNullOrEmpty(password)){return true;}return false;}public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture){throw new NotImplementedException();}
}

    在 XAML 中使用多值转换器

<Window.Resources><local:UsernamePasswordToButtonEnabledConverter x:Key="usernamePasswordToButtonEnabledConverter"/>
</Window.Resources>
<StackPanel><TextBox x:Name="usernameTextBox"/><TextBox x:Name="passwordTextBox" PasswordChar="*"/><Button Content="登录" IsEnabled="{Binding Path={x:Static local:ViewModelLocator.ViewModel.Username, ElementName=usernameTextBox, Mode=OneWay}, Path2={x:Static local:ViewModelLocator.ViewModel.Password, ElementName=passwordTextBox, Mode=OneWay}, Converter={StaticResource usernamePasswordToButtonEnabledConverter}}"/>
</StackPanel>

五、数据验证

5.1 数据验证的重要性

    在 WPF 应用程序中,数据验证确保用户输入的数据符合特定的规则和格式。例如,在一个注册表单中,用户输入的邮箱地址需要符合邮箱格式,密码需要满足一定的长度和复杂度要求。有效的数据验证可以提高数据的准确性和完整性,避免因错误数据导致的程序异常。

5.2 基于绑定的验证

使用 ValidationRules

    可以通过创建自定义的ValidationRule类来实现数据验证。例如,创建一个验证整数是否在指定范围内的ValidationRule。

public class IntegerRangeValidationRule : ValidationRule
{public int Min { get; set; }public int Max { get; set; }public override ValidationResult Validate(object value, CultureInfo cultureInfo){if (int.TryParse(value as string, out int number)){if (number >= Min && number <= Max){return ValidationResult.ValidResult;}else{return new ValidationResult(false, $"值必须在 {Min} 到 {Max} 之间");}}else{return new ValidationResult(false, "请输入有效的整数");}}
}

    在 XAML 中使用该验证规则:

<TextBox><TextBox.Text><Binding Path="MyIntegerProperty" UpdateSourceTrigger="PropertyChanged"><Binding.ValidationRules><local:IntegerRangeValidationRule Min="1" Max="100"/></Binding.ValidationRules></Binding></TextBox.Text>
</TextBox>

数据验证的反馈

    当数据验证失败时,WPF 提供了一些内置的机制来反馈给用户。例如,TextBox 的背景会变为红色,并且可以通过ToolTip显示错误信息。

<TextBox><TextBox.Text><Binding Path="MyIntegerProperty" UpdateSourceTrigger="PropertyChanged"><Binding.ValidationRules><local:IntegerRangeValidationRule Min="1" Max="100"/></Binding.ValidationRules></Binding></TextBox.Text><TextBox.Style><Style TargetType="TextBox"><Style.Triggers><Trigger Property="Validation.HasError" Value="true"><Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/></Trigger></Style.Triggers></Style></TextBox.Style>
</TextBox>

5.3 实现IDataErrorInfo接口

接口概述

    IDataErrorInfo接口提供了另一种数据验证的方式。在视图模型类中实现该接口,可以对属性进行验证。例如,验证一个字符串是否为有效的邮箱地址。

public class UserViewModel : IDataErrorInfo
{private string _email;public string Email{get { return _email; }set{if (_email!= value){_email = value;// 通知属性更改(通常使用 INotifyPropertyChanged 接口)}}}public string Error => string.Empty;public string this[string columnName]{get{if (columnName == nameof(Email)){if (!Regex.IsMatch(Email, @"^[a-zA - Z0 - 9_.+-]+@[a-zA - Z0 - 9 -]+\.[a-zA - Z0 - 9-.]+$")){return "请输入有效的邮箱地址";}}return string.Empty;}}
}

在 XAML 中使用IDataErrorInfo验证

    在 XAML 中,不需要额外配置验证规则,WPF 会自动检测视图模型是否实现了IDataErrorInfo接口并进行验证。

<TextBox Text="{Binding Path=Email}"/>

5.4 实现INotifyDataErrorInfo接口

接口优势

    INotifyDataErrorInfo接口是IDataErrorInfo接口的增强版本,它支持动态验证和多个错误信息。例如,在一个复杂的表单中,一个字段可能有多个验证规则,并且验证结果可能会根据其他字段的值动态变化。
接口实现

public class AdvancedUserViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
{private string _password;public string Password{get { return _password; }set{if (_password!= value){_password = value;OnPropertyChanged(nameof(Password));ValidatePassword();}}}private string _confirmPassword;public string ConfirmPassword{get { return _confirmPassword; }set{if (_confirmPassword!= value){_confirmPassword = value;OnPropertyChanged(nameof(ConfirmPassword));ValidatePassword();}}}private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();public bool HasErrors => _errors.Any();public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;public IEnumerable GetErrors(string propertyName){if (string.IsNullOrEmpty(propertyName) ||!_errors.ContainsKey(propertyName)){return null;}return _errors[propertyName];}private void ValidatePassword(){List<string> passwordErrors = new List<string>();if (string.IsNullOrEmpty(Password)){passwordErrors.Add("密码不能为空");}if (Password.Length < 6){passwordErrors.Add("密码长度至少为6位");}if (!string.IsNullOrEmpty(ConfirmPassword) && Password!= ConfirmPassword){passwordErrors.Add("两次输入的密码不一致");}if (_errors.ContainsKey(nameof(Password))){_errors.Remove(nameof(Password));}if (passwordErrors.Any()){_errors.Add(nameof(Password), passwordErrors);}
if (_errors.ContainsKey(nameof(ConfirmPassword)))
{
_errors.Remove(nameof(ConfirmPassword));
}
if (passwordErrors.Any())
{
_errors.Add(nameof(ConfirmPassword), passwordErrors);
}
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Password)));
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(ConfirmPassword)));
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

在 XAML 中使用INotifyDataErrorInfo验证

    在XAML中,WPF会自动识别视图模型实现的INotifyDataErrorInfo接口,并提供相应的验证反馈。当密码或确认密码不符合规则时,相关的TextBox会显示错误样式,例如红色边框,同时可以通过绑定Validation.Errors属性来显示具体的错误信息。

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

5.5 数据验证的综合应用

结合多种验证方式

    在实际项目中,往往需要综合运用多种数据验证方式。例如,在一个用户注册表单中,对于年龄字段,可以使用ValidationRules来验证其是否为正整数,确保输入的格式正确。同时,在视图模型中实现INotifyDataErrorInfo接口,用于验证用户名是否唯一。这样,通过不同验证方式的结合,既能保证数据格式的正确性,又能满足业务逻辑上的唯一性要求。

// 年龄验证规则

public class AgeValidationRule : ValidationRule
{public override ValidationResult Validate(object value, CultureInfo cultureInfo){if (int.TryParse(value as string, out int age)){if (age > 0){return ValidationResult.ValidResult;}else{return new ValidationResult(false, "年龄必须为正整数");}}else{return new ValidationResult(false, "请输入有效的年龄");}}
}public class RegistrationViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
{private string _username;public string Username{get { return _username; }set{if (_username!= value){_username = value;OnPropertyChanged(nameof(Username));ValidateUsername();}}}private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();public bool HasErrors => _errors.Any();public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;public IEnumerable GetErrors(string propertyName){if (string.IsNullOrEmpty(propertyName) ||!_errors.ContainsKey(propertyName)){return null;}return _errors[propertyName];}private void ValidateUsername(){List<string> usernameErrors = new List<string>();// 假设这里有检查用户名唯一性的逻辑,例如查询数据库if (IsUsernameExists(_username)){usernameErrors.Add("用户名已存在");}if (_errors.ContainsKey(nameof(Username))){_errors.Remove(nameof(Username));}if (usernameErrors.Any()){_errors.Add(nameof(Username), usernameErrors);}ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Username)));}private bool IsUsernameExists(string username){// 实际实现中应查询数据库等数据源return false;}public event PropertyChangedEventHandler PropertyChanged;protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}
}

    在 XAML 中:

<StackPanel><TextBox><TextBox.Text><Binding Path="Age" UpdateSourceTrigger="PropertyChanged"><Binding.ValidationRules><local:AgeValidationRule/></Binding.ValidationRules></Binding></TextBox.Text></TextBox><TextBox Text="{Binding Path=Username}"><TextBox.Style><Style TargetType="TextBox"><Style.Triggers><Trigger Property="Validation.HasError" Value="true"><Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/></Trigger></Style.Triggers></Style></TextBox.Style></TextBox>
</StackPanel>

跨字段验证

    跨字段验证在处理复杂业务逻辑时非常重要。比如在一个库存管理系统中,有 “入库数量” 和 “库存总量” 两个字段,当进行入库操作时,需要确保 “入库数量” 加上当前 “库存总量” 不超过仓库的最大容量。这就需要在视图模型中实现跨字段验证逻辑。

public class InventoryViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
{private int _currentStock;public int CurrentStock{get { return _currentStock; }set{if (_currentStock!= value){_currentStock = value;OnPropertyChanged(nameof(CurrentStock));ValidateStock();}}}private int _incomingQuantity;public int IncomingQuantity{get { return _incomingQuantity; }set{if (_incomingQuantity!= value){_incomingQuantity = value;OnPropertyChanged(nameof(IncomingQuantity));ValidateStock();}}}private const int MaxCapacity = 1000;private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();public bool HasErrors => _errors.Any();public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;public IEnumerable GetErrors(string propertyName){if (string.IsNullOrEmpty(propertyName) ||!_errors.ContainsKey(propertyName)){return null;}return _errors[propertyName];}private void ValidateStock(){List<string> stockErrors = new List<string>();if (_currentStock + _incomingQuantity > MaxCapacity){stockErrors.Add("入库后库存将超过最大容量");}if (_errors.ContainsKey(nameof(CurrentStock))){_errors.Remove(nameof(CurrentStock));}if (_errors.ContainsKey(nameof(IncomingQuantity))){_errors.Remove(nameof(IncomingQuantity));}if (stockErrors.Any()){_errors.Add(nameof(CurrentStock), stockErrors);_errors.Add(nameof(IncomingQuantity), stockErrors);}ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(CurrentStock)));ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(IncomingQuantity)));}public event PropertyChangedEventHandler PropertyChanged;protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}
}

    在 XAML 中:

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

六、总结

    WPF 的数据绑定进阶特性,包括绑定模式、转换器与验证,为开发者提供了一套强大且灵活的工具集,用于构建高效、可靠且用户体验良好的应用程序。

    绑定模式的多样化使得开发者能够根据不同的业务场景,精确控制数据在视图与数据源之间的流动方向,确保数据的传递符合应用程序的需求。数据绑定转换器则解决了数据类型不匹配和数据展示格式化的问题,使得数据能够以恰当的形式在 UI 上呈现和交互。而数据验证机制从多个层面保障了数据的质量,无论是简单的格式验证,还是复杂的业务规则验证,都能够有效地防止错误数据进入系统,提升了应用程序的稳定性和可靠性。

    在实际开发中,开发者需要深入理解这些特性,并根据项目的具体需求进行合理组合与应用。例如,在开发企业级业务应用时,可能会在数据输入表单中广泛应用双向绑定结合数据验证,确保用户输入的数据准确无误且能够实时更新到业务模型中;在数据展示界面,则会使用单向绑定和转换器来格式化和呈现数据。随着 WPF 技术的不断发展,数据绑定相关的功能也可能会不断优化和扩展,开发者应持续关注并深入学习,以充分发挥 WPF 的优势,打造出更优秀的桌面应用程序。通过不断地实践和探索,开发者能够更好地利用 WPF 数据绑定的进阶特性,为用户带来更加流畅、高效的应用体验。

结束语

        展望未来,WPF 布局系统依然有着广阔的发展前景。随着硬件技术的不断革新,如高分辨率屏幕、折叠屏设备的日益普及,WPF 布局系统有望进一步强化其自适应能力,为用户带来更加流畅、一致的体验。在应对高分辨率屏幕时,能够更加智能地缩放和布局元素,确保文字清晰可读、图像不失真;对于折叠屏设备,可动态调整布局结构,充分利用多屏空间,实现无缝切换。

        性能优化方面,微软及广大开发者社区将持续努力,进一步降低复杂布局的计算开销,提高布局更新的效率,使得 WPF 应用在处理大规模数据、动态界面时依然能够保持高效响应。通过改进算法、优化内存管理等手段,让 WPF 布局系统在性能上更上一层楼。

        亲爱的朋友,无论前路如何漫长与崎岖,都请怀揣梦想的火种,因为在生活的广袤星空中,总有一颗属于你的璀璨星辰在熠熠生辉,静候你抵达。

         愿你在这纷繁世间,能时常收获微小而确定的幸福,如春日微风轻拂面庞,所有的疲惫与烦恼都能被温柔以待,内心永远充盈着安宁与慰藉。

        至此,文章已至尾声,而您的故事仍在续写,不知您对文中所叙有何独特见解?期待您在心中与我对话,开启思想的新交流。


--------------- 业精于勤,荒于嬉 ---------------

请添加图片描述

--------------- 行成于思,毁于随 ---------------

优质源码分享

  • 【百篇源码模板】html5各行各业官网模板源码下载

  • 【模板源码】html实现酷炫美观的可视化大屏(十种风格示例,附源码)

  • 【VUE系列】VUE3实现个人网站模板源码

  • 【HTML源码】HTML5小游戏源码

  • 【C#实战案例】C# Winform贪吃蛇小游戏源码


在这里插入图片描述


     💞 关注博主 带你实现畅游前后端

     🏰 大屏可视化 带你体验酷炫大屏

     💯 神秘个人简介 带你体验不一样得介绍

     🎀 酷炫邀请函 带你体验高大上得邀请


     ① 🉑提供云服务部署(有自己的阿里云);
     ② 🉑提供前端、后端、应用程序、H5、小程序、公众号等相关业务;
     如🈶合作请联系我,期待您的联系。
    :本文撰写于CSDN平台,作者:xcLeigh所有权归作者所有) ,https://blog.csdn.net/weixin_43151418,如果相关下载没有跳转,请查看这个地址,相关链接没有跳转,皆是抄袭本文,转载请备注本文原地址。


     亲,码字不易,动动小手,欢迎 点赞 ➕ 收藏,如 🈶 问题请留言(评论),博主看见后一定及时给您答复,💌💌💌


原文地址:https://blog.csdn.net/weixin_43151418/article/details/145304085(防止抄袭,原文地址不可删除)

相关文章:

WPF进阶 | WPF 数据绑定进阶:绑定模式、转换器与验证

WPF进阶 | WPF 数据绑定进阶&#xff1a;绑定模式、转换器与验证 一、前言二、WPF 数据绑定基础回顾2.1 数据绑定的基本概念2.2 数据绑定的基本语法 三、绑定模式3.1 单向绑定&#xff08;One - Way Binding&#xff09;3.2 双向绑定&#xff08;Two - Way Binding&#xff09;…...

八股——Java基础(四)

目录 一、泛型 1. Java中的泛型是什么 ? 2. 使用泛型的好处是什么? 3. Java泛型的原理是什么 ? 什么是类型擦除 ? 4.什么是泛型中的限定通配符和非限定通配符 ? 5. List和List 之间有什么区别 ? 6. 可以把List传递给一个接受List参数的方法吗&#xff1f; 7. Arra…...

2025蓝桥杯JAVA编程题练习Day1

1.刑侦科推理试题 题目描述 有以下10道单选题&#xff0c;编程求这10道题的答案。 这道题的答案是&#xff1a; A. A B. B C. C D. D 第5题的答案是&#xff1a; A. C B. D C. A D. B 以下选项中哪一题的答案与其他三项不同&#xff1a; A. 第3题 B. 第6题 C. 第2题 D.…...

数据结构与算法-要点整理

知识导图: 一、数据结构 包含:线性表(数组、队列、链表、栈)、散列表、树(二叉树、多路查找树)、图 1.线性表 数据之间就是“一对一“的逻辑关系。 线性表存储数据的实现方案有两种,分别是顺序存储结构和链式存储结构。 包含:数组、队列、链表、栈。 1.1 数组…...

SaaS底层盈利逻辑剖析:运维费与服务费的战略抉择

一、引言 1.1 研究背景与意义 在数字化浪潮的推动下&#xff0c;SaaS&#xff08;软件即服务&#xff09;行业近年来取得了迅猛发展&#xff0c;成为软件产业中不可或缺的一部分。SaaS 通过互联网提供软件服务&#xff0c;企业无需进行复杂的本地软件安装和硬件购置&#xff…...

Python爬虫之——Cookie存储器

目录 专栏导读1、背景介绍2、库的安装3、核心代码4、完整代码总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍&#x1f308; 博客主页&#xff1a;请点击——> 一晌小贪欢的博客主页求关注 &…...

【数据结构】(2)时间、空间复杂度

一、衡量算法好坏的指标 时间复杂度衡量算法的运行速度&#xff0c;空间复杂度衡量算法所需的额外空间。这些指标&#xff0c;是某场景中选择使用哪种数据结构和算法的依据。如今&#xff0c;计算机的存储器已经变得容易获得&#xff0c;所以不再太关注空间复杂度。 二、渐进表…...

理解 IS-IS 中重要概念之间的关系

本文为 “IS-IS 中重要概念” 相关文章合辑。 未整理去重。 理解 IS-IS、CLNS、CMNS、NSAP、NET 等概念之间的关系 1. 核心概念 IS-IS (Intermediate System to Intermediate System) 一种链路状态路由协议&#xff0c;基于 SPF&#xff08;最短路径优先&#xff09;算法计…...

AI 模型评估与质量控制:生成内容的评估与问题防护

在生成式 AI 应用中&#xff0c;模型生成的内容质量直接影响用户体验。然而&#xff0c;生成式模型存在一定风险&#xff0c;如幻觉&#xff08;Hallucination&#xff09;问题——生成不准确或完全虚构的内容。因此&#xff0c;在构建生成式 AI 应用时&#xff0c;模型评估与质…...

Mybatis-plus缓存

mybatis-plus缓存 MyBatis-Plus 是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上提供了更多的便利性和强大的功能&#xff0c;包括但不限于分页、条件构造器、通用 Mapper、代码生成器等。MyBatis-Plus 也内置了基础的缓存功能&#xff0c;但需要注意的是&#xff…...

unity学习20:time相关基础 Time.time 和 Time.deltaTime

目录 1 unity里的几种基本时间 1.1 time 相关测试脚本 1.2 游戏开始到现在所用的时间 Time.time 1.3 时间缩放值 Time.timeScale 1.4 固定时间间隔 Time.fixedDeltaTime 1.5 两次响应时间之间的间隔&#xff1a;Time.deltaTime 1.6 对应测试代码 1.7 需要关注的2个基本…...

系统思考—转型

“我知道自己有问题&#xff0c;但问题到底出在哪里&#xff1f;” 很多中小企业主都会在这样的迷茫中徘徊。市场变化太快、团队执行力不强、内部沟通不畅……这些问题似乎无处不在。但其实&#xff0c;真正让企业陷入困境的&#xff0c;并不是问题本身&#xff0c;而是——看…...

Java面试题2025-设计模式

1.说一下开发中需要遵守的设计原则&#xff1f; 设计模式中主要有六大设计原则&#xff0c;简称为SOLID &#xff0c;是由于各个原则的首字母简称合并的来(两个L算一个,solid 稳定的)&#xff0c;六大设计原则分别如下&#xff1a; 1、单一职责原则 单一职责原则的定义描述非…...

本地Harbor仓库搭建流程

Harbor仓库搭建流程 本文主要介绍如何搭建harbor仓库&#xff0c;推送本地镜像供其他机器拉取构建服务 harbor文档&#xff1a;Harbor 文档 | 配置 Harbor YML 文件 - Harbor 中文 github下载离线安装包 Releases goharbor/harbor 这是harbor的GitHub下载地址&#xff0c…...

爬虫基础之爬取某基金网站+数据分析

声明: 本案例仅供学习参考使用&#xff0c;任何不法的活动均与本作者无关 网站:天天基金网(1234567.com.cn) --首批独立基金销售机构-- 东方财富网旗下基金平台! 本案例所需要的模块: 1.requests 2.re(内置) 3.pandas 4.pyecharts 其他均需要 pip install 模块名 爬取步骤: …...

Qt调用FFmpeg库实时播放UDP组播视频流

基于以下参考链接&#xff0c;通过改进实现实时播放UDP组播视频流 https://blog.csdn.net/u012532263/article/details/102736700 源码在windows&#xff08;qt-opensource-windows-x86-5.12.9.exe&#xff09;、ubuntu20.04.6(x64)(qt-opensource-linux-x64-5.12.12.run)、以…...

YOLOv10改进,YOLOv10检测头融合DynamicHead,添加小目标检测层(四头检测)+CA注意机制,全网首发

摘要 作者提出一种新的检测头,称为“动态头”,旨在将尺度感知、空间感知和任务感知统一在一起。如果我们将骨干网络的输出(即检测头的输入)视为一个三维张量,其维度为级别 空间 通道,这样的统一检测头可以看作是一个注意力学习问题,直观的解决方案是对该张量进行全自…...

【ComfyUI专栏】推荐几个常用的云端ComfyUI平台

如果我们本身的系统资源不足&#xff0c;但是我们依然能够使用显卡来利用ComfyUI生成我们需要的图片或者视频。当前平台中主要有两个不同的廉价平台提供了ComfyUI的功能&#xff0c;这里提供的资源基本上都是基于分钟进行计算。这些平台的好处就是基本上不需要你额外进行配置。…...

std::pair源码:operator=被delete了,提供的是sfinae版本

D:\DevTools\VS2017\VC\Tools\MSVC\14.16.27023\include\utility pair& operator(const volatile pair&) delete;真正版本&#xff1a;template<class _Other1 _Ty1,class _Other2 _Ty2,enable_if_t<conjunction_v<is_assignable<_Ty1&, const _Oth…...

数据要素白皮书(2024年)(解读+全文阅读)

数据要素白皮书&#xff08;2024年&#xff09;》由中国通信标准化协会大数据技术标准推进委员会发布&#xff0c;版权受法律保护&#xff0c;转载需注明来源。该白皮书深入分析了当前数据要素在全球及中国的发展态势&#xff0c;并提出了关键观察与展望。 全球范围内&#xf…...

C#常用257单词

1、Visual Studio 直译&#xff1a;可视化开发环境 说明&#xff1a;简称VS&#xff0c;我们编写程序的集成开发环境&#xff0c;可以写代码、编译代码、调试代码、发布程序。 2、.Net 直译&#xff1a;dotNet 说明&#xff1a;我们常说的.net读作dotnet&#xff0c;表示网…...

禅道2024年度回顾:品牌向善,才能向上!

很高兴禅道顺利完成了2024年的跃升计划&#xff0c;并交出了一份认真的答卷&#xff01; 这一年&#xff0c;禅道的产品在变强&#xff0c;完成了产品的重构与全面升级&#xff1b; 这一年&#xff0c;禅道的朋友在变多&#xff0c;服务到更多不同行业的客户&#xff1b; 这…...

RK3568 adb使用

文章目录 一、adb介绍**ADB 主要功能****常用 ADB 命令****如何使用 ADB****总结** 二、Linux下载adb**方法 1&#xff1a;使用包管理器&#xff08;适用于 Ubuntu/Debian 系统&#xff09;****方法 2&#xff1a;通过 Snap 安装&#xff08;适用于支持 Snap 的系统&#xff09…...

koodo-reader-电脑跨平台现代阅读器[支持ebook等电子书,支持多种格式]

koodo-reader-电脑跨平台现代阅读器[支持ebook等电子书&#xff0c;支持多种格式] 链接&#xff1a;https://pan.xunlei.com/s/VOH_rDBEXIU8lg4aA6IztZ4PA1?pwdg724# 特色 支持阅读格式&#xff1a; EPUB (.epub) PDF (.pdf) Kindle (.azw3, .mobi, .azw) 纯文本 (.txt…...

03:Heap代码的分析

Heap代码的分析 1、内存对齐2、Heap_1.c文件代码分析3、Heap_2.c文件代码分析4、Heap_4.c文件代码分析5、Heap_5.c文件代码分析 1、内存对齐 内存对齐的作用是为了CPU更快的读取数据。对齐存储与不对齐存储的情况如下&#xff1a; 计算机读取内存中的数据时是一组一组的读取的…...

二分查找题目:寻找两个正序数组的中位数

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;寻找两个正序数组的中位数 出处&#xff1a;4. 寻找两个正序数组的中位数 难度 8 级 题目描述 要求 给定两个大…...

Java Web-Tomcat Servlet

Web服务器-Tomcat Web服务器简介 Web 服务器是一种软件程序&#xff0c;它主要用于在网络上接收和处理客户端&#xff08;如浏览器&#xff09;发送的 HTTP 请求&#xff0c;并返回相应的网页内容或数据。以下是关于 Web 服务器的详细介绍&#xff1a; 功能 接收请求&#…...

渗透测试-WAF是什么以及原理解释 waf功能详解

目录 waf功能介绍 waf出现的地点: 什么是waf 功能: 常见的系统攻击分为两类 一是利用Web服务器的漏洞进行攻击 二是利用网页自身的安全漏洞进行攻击 WAF主要功能&#xff1a; waf的特点1 waf主要功能2 网马木马主动防御及查杀 流量监控 网站漏洞防御功能 危险组件…...

Vue3 provide/inject用法总结

1. 基本概念 provide/inject 是 Vue3 中实现跨层级组件通信的方案&#xff0c;类似于 React 的 Context。它允许父组件向其所有子孙组件注入依赖&#xff0c;无论层级有多深。 1.1 基本语法 // 提供方&#xff08;父组件&#xff09; const value ref(hello) provide(key, …...

C# 提取PDF表单数据

目录 使用工具 C# 提取多个PDF表单域的数据 C# 提取特定PDF表单域的数据 PDF表单是一种常见的数据收集工具&#xff0c;广泛应用于调查问卷、业务合同等场景。凭借出色的跨平台兼容性和标准化特点&#xff0c;PDF表单在各行各业中得到了广泛应用。然而&#xff0c;当需要整合…...

【JAVA项目】基于ssm的【宠物医院信息管理系统】

【JAVA项目】基于ssm的【宠物医院信息管理系统】 技术简介&#xff1a;采用JSP技术、ssm框架、B/S架构、MySQL技术等实现。 系统简介&#xff1a;宠物医院信息管理系统&#xff0c;在系统首页可以查看首页、医学知识、医生信息、药品信息、新闻资讯、留言反馈、我的、跳转到后台…...

书生大模型实战营2

L0——入门岛 Python基础 Conda虚拟环境 虚拟环境是Python开发中不可或缺的一部分&#xff0c;它允许你在不同的项目中使用不同版本的库&#xff0c;避免依赖冲突。Conda是一个强大的包管理器和环境管理器。 创建新环境 首先&#xff0c;确保你已经安装了Anaconda或Minico…...

产业园管理系统提升企业综合管理效率与智能化水平的成功案例分析

内容概要 在当前科技迅猛发展的时代&#xff0c;越来越多的企业意识到数字化转型的重要性。为了提升管理效率和智能化水平&#xff0c;产业园管理系统应运而生&#xff0c;成为众多园区和商办写字楼不可或缺的一部分。无论是工业园、物流园还是公寓&#xff0c;这些系统都能为…...

《AI赋能光追:开启图形渲染新时代》

光线追踪技术是图形渲染领域的重大突破&#xff0c;能够通过模拟光的传播路径&#xff0c;精准渲染反射、折射、阴影和间接光照等效果&#xff0c;实现高度逼真的场景呈现。而人工智能的加入&#xff0c;更是为光线追踪技术带来了前所未有的变革&#xff0c;主要体现在以下几个…...

危机13小时:追踪一场GitHub投毒事件

事件概要 自北京时间 2024.12.4 晚间6点起&#xff0c; GitHub 上不断出现“幽灵仓库”&#xff0c;仓库中没有任何代码&#xff0c;只有诱导性的病毒文件。当天&#xff0c;他们成为了 GitHub 上 star 增速最快的仓库。超过 180 个虚假僵尸账户正在传播病毒&#xff0c;等待不…...

利用JSON数据类型优化关系型数据库设计

利用JSON数据类型优化关系型数据库设计 前言 在关系型数据库中&#xff0c;传统的结构化存储方式要求预先定义好所有的列及其数据类型。 然而&#xff0c;随着业务的发展&#xff0c;这种设计可能会显得不够灵活&#xff0c;尤其是在需要扩展单个列的描述功能时。 JSON数据…...

如何学习Java后端开发

文章目录 一、Java 语言基础二、数据库与持久层三、Web 开发基础四、主流框架与生态五、分布式与高并发六、运维与部署七、项目实战八、持续学习与提升总结路线图 学习 Java 后端开发需要系统性地掌握多个技术领域&#xff0c;从基础到进阶逐步深入。以下是一个详细的学习路线和…...

AI刷题-蛋糕工厂产能规划、优质章节的连续选择

挑两个简单的写写 目录 一、蛋糕工厂产能规划 问题描述 输入格式 输出格式 解题思路&#xff1a; 问题理解 数据结构选择 算法步骤 关键点 最终代码&#xff1a; 运行结果&#xff1a;​编辑 二、优质章节的连续选择 问题描述 输入格式 输出格式 解题思路&a…...

SpringBoot统一数据返回格式 统一异常处理

统一数据返回格式 & 统一异常处理 1. 统一数据返回格式1.1 快速入门1.2 存在问题1.3 案列代码修改1.4 优点 2. 统一异常处理 1. 统一数据返回格式 强制登录案例中,我们共做了两部分⼯作 通过Session来判断⽤⼾是否登录对后端返回数据进⾏封装,告知前端处理的结果 回顾 后…...

[MySQL]事务的理论、属性与常见操作

目录 一、事物的理论 1.什么是事务 2.事务的属性&#xff08;ACID&#xff09; 3.再谈事务的本质 4.为什么要有事务 二、事务的操作 1.事务的支持版本 2.事务的提交模式 介绍 自动提交模式 手动提交模式 3.事务的操作 4.事务的操作演示 验证事务的回滚 事务异常…...

JWT实现单点登录

文章目录 JWT实现单点登录JWT 简介存在问题及解决方案登录流程后端程序实现前端保存Tokenstore存放信息的缺点及解决 校验流程&#xff1a;为gateway增加登录校验拦截器 另一种单点登录方法&#xff1a;Token&#xff0b;Redis实现单点登录 JWT实现单点登录 登录流程&#xff…...

docker 学习笔记

一、docker容器快速上手以及简单操作 docker的image和container image镜像 docker image就是一个read.only文件&#xff0c;可以理解成一个模版&#xff0c;docker image具有分层的概念 可以自己制作&#xff0c;也可以从registry拉去 container容器 一个运行中的docker …...

Lesson 119 A true story

Lesson 119 A true story 词汇 story n. 故事&#xff0c;传记&#xff0c;小说&#xff0c;楼层storey 搭配&#xff1a;tell a story 讲故事&#xff0c;说谎    true story 真实的故事    the second floor 二楼 例句&#xff1a;我猜他正在说谎。    I guess he…...

c语言版贪吃蛇(Pro Max版)附源代码

1 背景 贪吃蛇是一款经典的电子游戏&#xff0c;最早出现在20世纪70年代的街机游戏中。游戏的核心玩法是玩家控制一条蛇在有限的空间内移动&#xff0c;通过吃食物来增长身体长度&#xff0c;同时避免撞到墙壁、障碍物或自身。随着蛇的长度增加&#xff0c;游戏难度逐渐提升。 …...

蓝桥村打花结的花纸选择问题

在这篇文章中&#xff0c;我们将探讨一个有趣的算法问题&#xff0c;这个问题涉及到中国传统手工艺——打花结。我们需要判断给定的矩形花纸是否可以通过折叠操作使其面积变为特定的值 X&#xff0c;从而适合用来打花结。 问题描述 解题思路 这个问题可以通过循环方法来解决。…...

SSM开发(三) spring与mybatis整合(含完整运行demo源码)

目录 本文主要内容 一、Spring整合MyBatis的三个关键点 二、整合步骤 1、创建一个Maven项目 2、在pom.xml文件中添加jar包的依赖 3、配置MyBatis 注解实现方式 XML配置文件实现 4、配置Spring 5、测试运行 本文主要内容 1. Spring + Mybatis整合; 2. MyBatis两种SQL…...

【Matlab高端绘图SCI绘图模板】第006期 对比绘柱状图 (只需替换数据)

1. 简介 柱状图作为科研论文中常用的实验结果对比图&#xff0c;本文采用了3组实验对比的效果展示图&#xff0c;代码已调试好&#xff0c;只需替换数据即可生成相关柱状图&#xff0c;为科研加分。通过获得Nature配色的柱状图&#xff0c;让你的论文看起来档次更高&#xff0…...

Elasticsearch中的度量聚合:深度解析与实战应用

在大数据和实时分析日益重要的今天&#xff0c;Elasticsearch以其强大的搜索和聚合能力&#xff0c;成为了众多企业和开发者进行数据分析和处理的首选工具。本文将深入探讨Elasticsearch中的度量聚合&#xff08;Metric Aggregations&#xff09;&#xff0c;展示其如何在数据分…...

重回C语言之老兵重装上阵(十六)C语言可变参数

C语言可变参数 在C语言中&#xff0c;标准库提供了一些函数允许接收可变数量的参数。最典型的例子就是 printf 和 scanf&#xff0c;它们能够处理不确定数量的参数。为了实现这一功能&#xff0c;C语言提供了可变参数函数的概念。 1. 可变参数函数的概念 可变参数函数是指函数…...

第4章 神经网络【1】——损失函数

4.1.从数据中学习 实际的神经网络中&#xff0c;参数的数量成千上万&#xff0c;因此&#xff0c;需要由数据自动决定权重参数的值。 4.1.1.数据驱动 数据是机器学习的核心。 我们的目标是要提取出特征量&#xff0c;特征量指的是从输入数据/图像中提取出的本质的数 …...