首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-02-17 09:00
  • 2484
  • 5426
blog.csdn.net

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

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

2.2 数据绑定的基本语法

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


  • 1

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


  • 1

三、绑定模式

3.1 单向绑定(One - Way Binding)

定义与原理

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

  • 1

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

public class MyViewModel
{
    private string _readOnlyProperty;
    public string ReadOnlyProperty
    {
        get { return _readOnlyProperty; }
        set
        {
            if (_readOnlyProperty!= value)
            {
                _readOnlyProperty = value;
                // 通知属性更改(通常使用 INotifyPropertyChanged 接口)
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

适用场景

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

3.2 双向绑定(Two - Way Binding)

定义与原理

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


  • 1

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

public class MyViewModel
{
    private string _userInput;
    public string UserInput
    {
        get { return _userInput; }
        set
        {
            if (_userInput!= value)
            {
                _userInput = value;
                // 通知属性更改(通常使用 INotifyPropertyChanged 接口)
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

适用场景

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

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

定义与原理

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


  • 1

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

public class MyViewModel
{
    private double _sliderValue;
    public double SliderValue
    {
        get { return _sliderValue; }
        set
        {
            if (_sliderValue!= value)
            {
                _sliderValue = value;
                // 通知属性更改(通常使用 INotifyPropertyChanged 接口)
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

适用场景

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

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

定义与原理

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


  • 1

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

public class MyViewModel
{
    private string _initialValue;
    public string InitialValue
    {
        get { return _initialValue; }
        set
        {
            if (_initialValue!= value)
            {
                _initialValue = value;
                // 通知属性更改(通常使用 INotifyPropertyChanged 接口)
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

适用场景

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

四、数据绑定转换器

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();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

4.3 在 XAML 中使用转换器

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


    

  • 1
  • 2
  • 3
  • 4

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

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();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

    在 XAML 中使用多值转换器


    


    
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

五、数据验证

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, "请输入有效的整数");
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

    在 XAML 中使用该验证规则:


    
        
            
                
            
        
    

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

数据验证的反馈

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


    
        
            
                
            
        
    
    
        
    

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

在 XAML 中使用IDataErrorInfo验证

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


  • 1

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> _errors = new Dictionary>();

    public bool HasErrors => _errors.Any();

    public event EventHandler ErrorsChanged;

    public IEnumerable GetErrors(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName) ||!_errors.ContainsKey(propertyName))
        {
            return null;
        }
        return _errors[propertyName];
    }

    private void ValidatePassword()
    {
        List passwordErrors = new List();
        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));
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

在 XAML 中使用INotifyDataErrorInfo验证

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

<StackPanel>
    <TextBox Text="{Binding Path=Password}">
        <TextBox.Style>
            <Style TargetType="TextBox">
                
                    "Validation.HasError" Value="true">
                        "ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
                    
                
            Style>
        TextBox.Style>
    TextBox>
    <TextBox Text="{Binding Path=ConfirmPassword}">
        <TextBox.Style>
            <Style TargetType="TextBox">
                
                    "Validation.HasError" Value="true">
                        "ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
                    
                
            Style>
        TextBox.Style>
    TextBox>
StackPanel>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

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> _errors = new Dictionary>();

    public bool HasErrors => _errors.Any();

    public event EventHandler ErrorsChanged;

    public IEnumerable GetErrors(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName) ||!_errors.ContainsKey(propertyName))
        {
            return null;
        }
        return _errors[propertyName];
    }

    private void ValidateUsername()
    {
        List usernameErrors = new List();
        // 假设这里有检查用户名唯一性的逻辑,例如查询数据库
        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));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

    在 XAML 中:


    
        
            
                
                    
                
            
        
    
    
        
            
        
    

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

跨字段验证

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

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> _errors = new Dictionary>();

    public bool HasErrors => _errors.Any();

    public event EventHandler ErrorsChanged;

    public IEnumerable GetErrors(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName) ||!_errors.ContainsKey(propertyName))
        {
            return null;
        }
        return _errors[propertyName];
    }

    private void ValidateStock()
    {
        List stockErrors = new List();
        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));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

    在 XAML 中:


    
        
            
        
    
    
        
            
        
    

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

六、总结

    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,如果相关下载没有跳转,请查看这个地址,相关链接没有跳转,皆是抄袭本文,转载请备注本文原地址。


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


原文地址:http://iyenn.com/rec/1651287.html(防止抄袭,原文地址不可删除)

接商务合作/毕业设计等,欢迎!
微信名片
注:本文转载自blog.csdn.net的xcLeigh的文章"https://blog.csdn.net/weixin_43151418/article/details/145304085"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

139
资讯
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top