学懂C#编程:常用框架学习(三)———学会并深入理解WPF核心之MVVM模式

1. 理解基本概念

WPF: WPF是微软的一个用于开发Windows客户端应用程序的框架。它提供了丰富的UI元素和样式,以及强大的数据绑定和动画支持。

MVVM(Model-View-ViewModel): MVVM是一种架构模式,它将应用程序分为三个主要部分:

  • Model:代表数据以及业务逻辑。
  • View:用户界面,用于显示数据。
  • ViewModel:充当Model和View之间的桥梁,负责处理UI逻辑和业务逻辑的分离。

2. MVVM的特点

  • 解耦:Model、View和ViewModel之间高度解耦,使得代码更易于维护和测试。
  • 可重用性:ViewModel可以独立于View被重用。
  • 易于测试:由于ViewModel不包含任何UI相关的代码,因此可以独立于UI进行测试。
3. 实现MVVM的关键技术
  • 依赖属性(Dependency Properties):在ViewModel中使用,使得UI可以监听其变化并自动更新。
  • 命令(ICommand接口):代替事件处理,使得UI可以调用ViewModel中的方法而无需硬编码到代码后面。
  • 数据上下文(DataContext):将ViewModel与View关联起来,使得View可以访问ViewModel中的属性和命令。

4. 核心特性:数据绑定

数据绑定是WPF中一个核心特性,它允许你将UI元素的属性直接与应用程序的数据源的属性连接起来。这意味着,当数据源的值发生变化时,UI会自动更新以反映这些变化,反之亦然。这大大简化了代码,提高了可维护性和灵活性。

数据绑定的基本要素:
  1. :要绑定的数据源头,通常是业务对象的属性。
  2. 目标:UI元素的属性,比如TextBox的Text属性。
  3. 路径:在源对象中指定的属性路径。
  4. 模式:绑定的方向,如OneWay(单向从源到目标)、TwoWay(双向)、OneTime(一次性)等。
  5. 转换器:可选的,用于在数据源和目标类型之间转换数据的类。
示例:
cs 复制代码
<!-- 在XAML中设置数据绑定 -->
<TextBox Text="{Binding Path=UserName, Mode=TwoWay}" />
cs 复制代码
// 在C#代码中设置数据上下文
public MainWindow()
{
    InitializeComponent();
    DataContext = new UserModel { UserName = "John Doe" };
}

5. 示例解析

下面通过简单的实例展示,让我们更容易学会和理解WPF的核心编程MVVM模式及其数据绑定特性。

假设我们有一个简单的应用程序,显示一个 Person 对象的列表,并允许用户添加新的 Person

1) 定义 Model
cs 复制代码
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
2) 定义 ViewModel
cs 复制代码
//定义了一个名为MainViewModel的公共类,它实现了INotifyPropertyChanged接口。这个接口用于在属性值更改时通知绑定的UI元素。
public class MainViewModel : INotifyPropertyChanged
{
    //声明了一个私有成员people,它是一个ObservableCollection<Person>类型的集合。ObservableCollection是一个特殊的集合,当集合中的项目被添加、删除或更改时,它可以自动通知绑定的UI元素。
    private ObservableCollection<Person> people;

    //声明了一个私有成员selectedPerson,它用于存储当前选中的Person对象。
    private Person selectedPerson;


    //以下这段代码定义了一个公共属性People,它允许外部访问people集合。在setter中,如果people的值发生变化(即与新的value不相等),则更新people并通知任何绑定的UI元素该属性已更改。
    public ObservableCollection<Person> People
    {
        get { return people; }
        set
        {
            if (people != value)
            {
                people = value;
                OnPropertyChanged(nameof(People));
            }
        }
    }

    //类似于People属性,这里定义了SelectedPerson属性。当selectedPerson的值变化时,它会通知绑定的UI元素。
    public Person SelectedPerson
    {
        get { return selectedPerson; }
        set
        {
            if (selectedPerson != value)
            {
                selectedPerson = value;
                OnPropertyChanged(nameof(SelectedPerson));
            }
        }
    }

    //声明了一个RelayCommand类型的公共属性AddPersonCommand。RelayCommand是一个常用的命令实现,通常用于MVVM模式中以处理UI命令(如按钮点击)
    public RelayCommand AddPersonCommand { get; set; }

    //构造函数中,初始化了People集合和AddPersonCommand命令。AddPersonCommand被设置为执行AddPerson方法
    public MainViewModel()
    {
        // 初始化People集合  
        People = new ObservableCollection<Person>
        {
            new Person { Name = "John Doe", Age = 30 },
            new Person { Name = "Jane Doe", Age = 25 }
        };

        // 初始化AddPersonCommand命令
        AddPersonCommand = new RelayCommand(AddPerson);
    }

    //AddPerson方法是一个私有方法,用于向People集合中添加一个新的Person对象。这里parameter参数没有被使用,但在某些情况下,它可能包含有关要添加的人的额外信息。
    private void AddPerson(object parameter)
    {
        People.Add(new Person { Name = "New Person", Age = 0 });
    }

    //这行代码声明了PropertyChanged事件,它是INotifyPropertyChanged接口的一部分。当ViewModel中的任何属性更改时,它将触发此事件。
    public event PropertyChangedEventHandler PropertyChanged;

    //OnPropertyChanged是一个受保护的方法,用于触发PropertyChanged事件。它接受一个字符串参数propertyName,该参数指定了已更改的属性的名称。PropertyChanged?.Invoke(...)是一种空值合并运算符的使用,它确保如果PropertyChanged事件不是null,则调用它。
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

程序解析:

这段代码定义了一个名为MainViewModel的类,它是MVVM架构中的ViewModel部分,主要用于处理数据和逻辑,同时实现INotifyPropertyChanged接口以支持UI的数据绑定。下面是逐行的解释:

1、类定义 :定义了MainViewModel类并让它实现了INotifyPropertyChanged接口,这意味着它能够通知UI当其属性值改变时。

2、私有字段 :定义了两个私有字段,peopleselectedPerson,分别用于存储一个ObservableCollection<Person>(表示一组人)和一个当前选中的人。

3、 People属性 :定义了一个公开的People属性,类型为ObservableCollection<Person>。使用了属性模式(getter和setter)。当people字段的值发生改变时,会调用OnPropertyChanged方法通知任何绑定到此属性的UI元素更新。

4、 SelectedPerson属性 :定义了公开的SelectedPerson属性,类型为Person。同样,当selectedPerson字段的值改变时,会通知UI更新。

5、AddPersonCommand属性 :定义了一个公开的AddPersonCommand属性,类型为RelayCommand,用于处理添加人的逻辑。

6、 构造函数MainViewModel的构造函数初始化了People集合,添加了两个默认的Person对象,并实例化了一个AddPersonCommand,将其委托给AddPerson方法。

7、 AddPerson方法 :这是一个私有方法,当AddPersonCommand被执行时被调用,用于向People集合中添加一个新的Person对象。

8、 PropertyChanged事件 :定义了INotifyPropertyChanged接口要求的事件,用于通知属性变更。

9、 OnPropertyChanged方法 :这是一个受保护的方法,用于在属性值更改时调用,它会触发PropertyChanged事件,告知UI哪个属性发生了变化。

总之,这个MainViewModel类管理着一组人的集合(People),并允许用户选择其中一个(SelectedPerson),同时还提供了一个命令(AddPersonCommand)来向集合中添加新的成员。这样的设计使得UI和业务逻辑分离,便于测试和维护。

3) 定义 View
cs 复制代码
<Window x:Class="MvvmExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MVVM Example" Height="300" Width="400">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ListBox ItemsSource="{Binding People}" SelectedItem="{Binding SelectedPerson}" Grid.Row="0">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" Margin="5"/>
                        <TextBlock Text="{Binding Age}" Margin="5"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button Content="Add Person" Command="{Binding AddPersonCommand}" Grid.Row="1" Margin="10"/>
    </Grid>
</Window>
  • ItemsSource="{Binding People}":将列表框的项源绑定到ViewModel的People属性。
  • SelectedItem="{Binding SelectedPerson}":将列表框的选定项绑定到ViewModel的SelectedPerson属性。
  • <DataTemplate>:定义数据模板,用于展示Person对象。
  • <StackPanel ...>:使用堆栈面板水平排列两个文本块,分别展示PersonNameAge属性。
  • Content="Add Person":设置按钮的文本为"Add Person"。
  • Command="{Binding AddPersonCommand}":将按钮的点击命令绑定到ViewModel的AddPersonCommand,当按钮被点击时,会执行ViewModel中相应的命令逻辑。
4)定义 RelayCommand
cs 复制代码
public class RelayCommand : ICommand
{
    private readonly Action<object> execute;
    private readonly Predicate<object> canExecute;

    public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
    {
        this.execute = execute ?? throw new ArgumentNullException(nameof(execute));
        this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return canExecute == null || canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        execute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
}

代码解析:

这段代码定义了一个名为RelayCommand的类,它实现了.NET中的ICommand接口,常用于MVVM(Model-View-ViewModel)架构中,作为绑定到UI元素(如按钮)的命令,以便在用户交互时执行特定的操作。下面是逐行的解释:

  1. 类定义 :定义了RelayCommand类,并指明它实现了ICommand接口。

  2. 私有字段

    • execute:一个Action<object>委托,代表命令执行时需要调用的实际操作,参数object可以携带命令执行时需要的数据。
    • canExecute:一个Predicate<object>委托,用于判断命令是否可以执行,同样接受一个object参数,返回布尔值。

3-6. 构造函数:构造函数接受两个参数:

  • execute委托,必须提供,否则抛出ArgumentNullException异常。
  • canExecute委托,默认值为null,意味着如果不提供,则默认命令总是可执行的。

7-12. CanExecute方法 :这是ICommand接口的一部分,用于检查命令当前是否可以执行。如果canExecute委托存在,则调用它并传入parameter,返回其结果;如果canExecutenull,则默认返回true,表示命令始终可执行。

13-16. Execute方法 :同样是ICommand接口的一部分,用于执行命令。直接调用execute委托并将参数传递给它。

17-26. CanExecuteChanged事件 :这是一个特殊的事件,用于通知UI命令的可执行状态可能已改变,应当重新查询其状态。这里通过CommandManager.RequerySuggested事件来实现,当添加或移除事件处理器时,自动订阅或取消订阅这个事件。这样,每当UI需要重新评估命令状态时(比如依赖的属性变化),框架会自动调用CanExecute方法来更新UI元素(如使按钮变为可用或不可用)。

总的来说,RelayCommand类提供了一种简洁的方式,将UI命令与后台操作逻辑解耦,方便在MVVM模式中使用命令绑定,增强了代码的可测试性和可维护性。

解释

  1. Model : Person 类表示数据模型。

  2. ViewModel : MainViewModel 类包含 People 集合和 SelectedPerson 属性,以及 AddPersonCommand 命令。People 集合使用 ObservableCollection,以便在集合更改时通知 UI。

  3. View : 在 XAML 中,通过设置 DataContextMainViewModel,将 View 与 ViewModel 关联。使用 {Binding} 语法将 ListBoxItemsSource 绑定到 People 集合,将 SelectedItem 绑定到 SelectedPerson 属性。按钮的 Command 属性绑定到 AddPersonCommand 命令。

通过这种方式,MVVM 模式使得界面与业务逻辑完全分离,提高了代码的可维护性和可测试性。数据绑定和命令机制是实现 MVVM 的关键技术。

相关推荐
koko4215 分钟前
天津小公司面经
java·学习·面试
七夜星七夜月2 小时前
生成与无监督学习 —— 奶茶店的 “新品研发与原料优化体系”
学习
iconball2 小时前
个人用云计算学习笔记 --19 (MariaDB服务器)
linux·运维·笔记·学习·云计算
Lynnxiaowen2 小时前
今天我们开始学习python3编程之python基础
linux·运维·python·学习
少吃一口都不行2 小时前
脚手架学习
前端·javascript·学习
东风西巷2 小时前
Avast Cleanup安卓版(手机清理优化) 修改版
android·学习·智能手机·软件需求
ajassi20003 小时前
开源 C# 快速开发(十七)进程--消息队列MSMQ
windows·开源·c#
报错小能手4 小时前
linux学习笔记(21)线程同步——互斥锁
linux·笔记·学习
『往事』&白驹过隙;5 小时前
浅谈内存DDR——DDR4性能优化技术
科技·物联网·学习·性能优化·内存·ddr