详解WPF中MVVM架构中的多种数据绑定方式

 更新时间:2025年09月26日 09:41:41   作者:苦荞米  
这篇文章主要为大家详细介绍了WPF中MVVM架构中的多种数据绑定方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

WPF基础框架

通过继承接口INotifyPropertyChangedICommand 来实现。

数据绑定部分

INotifyPropertyChanged原代码如下,WPF框架会自动注册DataContext对象中的PropertyChanged事件(若事件存在)。然后根据该事件修改前端属性。

namespace System.ComponentModel
{
    // Notifies clients that a property value has changed.
    public interface INotifyPropertyChanged
    {
        // Occurs when a property value changes.
        event PropertyChangedEventHandler? PropertyChanged;
    }
}

注意:INotifyPropertyChanged接口并非是强制要求的,当不需要通过设置属性自动修改页面数据时,也就是不需要执行set方法时,不需要继承该接口。若使用了通知集合ObservableCollection,也是不需要专门通过事件通知页面的。

public class NativeViewModel : INotifyPropertyChanged
{
    private string _name;

    public string Name
    {
        get => _name;
        set
        {
            if (_name != value)
            {
                _name = value;
                OnPropertyChanged();
            }
        }
    }
    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
<StackPanel>
    <TextBox Text="{Binding Name}" />
</StackPanel>

数据绑定中的命令绑定

ICommand 原代码如下,可以看到其中有按钮操作常用的几种属性:是否可用、可用性变化、触发事件。与前端通过Click属性指定事件相比:一个是前端指定要执行的逻辑,一个是由vm来确定最终的执行逻辑,相当于是反转了控制方。可以根据需要使用、并非强制要求。

#nullable enable

using System.ComponentModel;
using System.Windows.Markup;

namespace System.Windows.Input
{
    //
    // 摘要:
    //     Defines a command.
    [TypeConverter("System.Windows.Input.CommandConverter, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null")]
    [ValueSerializer("System.Windows.Input.CommandValueSerializer, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null")]
    public interface ICommand
    {
        //
        // 摘要:
        //     Occurs when changes occur that affect whether or not the command should execute.
        event EventHandler? CanExecuteChanged;

        //
        // 摘要:
        //     Defines the method that determines whether the command can execute in its current
        //     state.
        //
        // 参数:
        //   parameter:
        //     Data used by the command. If the command does not require data to be passed,
        //     this object can be set to null.
        //
        // 返回结果:
        //     true if this command can be executed; otherwise, false.
        bool CanExecute(object? parameter);
        //
        // 摘要:
        //     Defines the method to be called when the command is invoked.
        //
        // 参数:
        //   parameter:
        //     Data used by the command. If the command does not require data to be passed,
        //     this object can be set to null.
        void Execute(object? parameter);
    }
}

对于ICommand属性,推荐使用方式是不要让其触发PropertyChanged,不应该被动态设置,而应该初始定好。不过如果使用场景确实需要,应该也是能生效的。

public class NativeViewModel
{
    public ICommand GreetCommand { get; }

    public NativeViewModel()
    {
        // 使用自定义的 RelayCommand 需要自己实现
        GreetCommand = new RelayCommand();
    }
}

// 需要实现的简单命令类
public class RelayCommand : ICommand
{
    // 略
}
<StackPanel>
    <Button Content="Say Hello" Command="{Binding GreetCommand}" />
</StackPanel>

CommunityToolkit.Mvvm 方式

CommunityToolkit.Mvvm 利用 C# 的源码生成器,在编译时自动生成INotifyPropertyChangedICommand的样板代码。需要nuget包CommunityToolkit.Mvvm

其仅在vm上有区别,在实际绑定方式上没有区别。

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

// 3. 使用 [ObservableObject] 特性或继承 ObservableObject
[ObservableObject]
public partial class ToolkitViewModel
{
    // 4. 使用 [ObservableProperty] 标记字段,自动生成名为 "Name" 的属性
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(GreetCommand))] // 当 Name 改变时,通知 GreetCommand 重新验证
    private string _name;

    [ObservableProperty]
    private string _greeting;

    // 5. 使用 [RelayCommand] 自动生成名为 GreetCommand 的 ICommand 属性
    [RelayCommand(CanExecute = nameof(CanGreet))]
    private void Greet()
    {
        Greeting = $"Hello from Toolkit, {Name}!";
    }

    private bool CanGreet() => !string.IsNullOrWhiteSpace(Name);
}

ReactiveUI

ReactiveUI 将响应式编程理念引入 MVVM,核心是使用ReactiveObjectWhenAnyValue等来声明数据流和反应关系。需要nuget包ReactiveUI.WPF

其仅在vm上有区别,在实际绑定方式上没有区别。

using ReactiveUI;
using System.Reactive.Linq;

// 6. 继承 ReactiveObject
public class ReactiveViewModel : ReactiveObject
{
    // 7. 使用 [Reactive] 特性或 WhenAnyValue
    private string _name;
    public string Name
    {
        get => _name;
        set => this.RaiseAndSetIfChanged(ref _name, value);
    }

    private readonly ObservableAsPropertyHelper<string> _greeting;
    public string Greeting => _greeting.Value;

    // 8. 使用 ReactiveCommand 创建命令
    public ReactiveCommand<Unit, Unit> GreetCommand { get; }

    public ReactiveViewModel()
    {
        // 判断命令何时可执行:当 Name 不为空时
        var canGreet = this.WhenAnyValue(x => x.Name, name => !string.IsNullOrWhiteSpace(name));

        // 创建命令
        GreetCommand = ReactiveCommand.CreateFromTask(
            execute: async () => { /* 可以执行异步操作 */ return $"Hello from ReactiveUI, {Name}!"; },
            canExecute: canGreet // 绑定可执行条件
        );

        // 9. 将命令的执行结果(一个IObservable<string>)订阅到 Greeting 属性
        _greeting = GreetCommand.ToProperty(this, x => x.Greeting, initialValue: "Waiting...");

        // 另一种更直接的写法(不通过命令结果):
        // GreetCommand = ReactiveCommand.Create(() => { Greeting = $"Hello from ReactiveUI, {Name}!"; }, canGreet);
        // 但上面那种方式展示了将 IObservable 流转换为属性的强大能力。
    }
}

到此这篇关于详解WPF中MVVM架构中的多种数据绑定方式的文章就介绍到这了,更多相关WPF MVVM架构的数据绑定内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • c#:CTS类型系统

    c#:CTS类型系统

    CTS通用类型系统,是.Net中一套定义类型的规则。我们要掌握c#开发,首先要建立这个类型概念,只有知道c#的元素是什么类型,才能进行相关的分析和选材。
    2012-12-12
  • C#零基础开发中最重要的概念总结

    C#零基础开发中最重要的概念总结

    这篇文章主要为大家详细介绍了C#零基础开发中最重要的一些概念,文中的示例代码讲解详细,对我们学习C#有一定的帮助,需要的可以参考一下
    2023-02-02
  • C#中常使用进度条的代码

    C#中常使用进度条的代码

    C#中常使用进度条的代码...
    2007-03-03
  • 基于C#实现获取本地磁盘目录

    基于C#实现获取本地磁盘目录

    这篇文章主要为大家详细介绍了如何利用C#实现获取本地磁盘目录的功能,文中的示例代码讲解详细,对我们学习C#有一定的帮助,感兴趣的小伙伴可以跟随小编一起了解一下
    2022-12-12
  • C#从foreach语句中枚举元素看数组详解

    C#从foreach语句中枚举元素看数组详解

    这篇文章主要给大家介绍了关于C#从foreach语句中枚举元素看数组的相关资料,文中介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-05-05
  • WPF中引入WindowsForms控件的方法

    WPF中引入WindowsForms控件的方法

    这篇文章主要介绍了WPF中引入WindowsForms控件的方法,结合实例形式分析了在WPF中调用Windows Forms控件的具体步骤与相关实现技巧,需要的朋友可以参考下
    2016-07-07
  • C#净化版WebApi框架的实现

    C#净化版WebApi框架的实现

    这篇文章主要介绍了C#净化版WebApi框架的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-03-03
  • C#特性-对象集合初始化器介绍

    C#特性-对象集合初始化器介绍

    在写一些实体类的时候,我们往往在写构造方法的时候思考很长时间,除了一个无参构造器外还在想需要写几个构造器呢?哪些参数是需要初始化的。现在你再也不需要为这事烦恼了
    2014-12-12
  • c#斐波那契数列(Fibonacci)(递归,非递归)实现代码

    c#斐波那契数列(Fibonacci)(递归,非递归)实现代码

    c#斐波那契数列(Fibonacci)(递归,非递归)实现代码,需要的朋友可以参考一下
    2013-05-05
  • C#制作二维柱状图方法

    C#制作二维柱状图方法

    在本文里小编为各位分享的是关于C#制作二维柱状图方法和步骤,需要的读者们学习下。
    2018-12-12

最新评论