WPF 入门笔记 - 06 - 命令

我们把世界看错,反说它欺骗了我们。 --飞鸟集

前言

相较而言,命令对我来说是一个新概念,因为在Winform中压根没有所谓的命令这个概念🥲。从文字角度理解,"命令"可以指代一种明确的指令或要求,用于向某个实体传达特定的操作或行为。它可以是一个动词性的词语,表示对某个对象或主体的要求或指示。命令通常具有明确的目标和执行内容,它告诉接收者要执行什么操作,并在某种程度上对行为进行约束。

在软件开发中,"命令"是一种设计模式,它描述了将操作封装为对象的方法,以便在不同的上下文中使用和重用。这种命令模式通过将请求和操作封装到一个命令对象中,使得发送者和接收者之间解耦,从而实现了更灵活和可扩展的设计。在这种模式下,命令对象充当了发送者和接收者之间的中间媒介,接收者通过执行命令对象来完成特定的操作。

在提到命令的时候,我们通常拿它来和事件作比较。我们都知道,WPF里已经有了路由事件,比如按钮在被点击以后做出的反应(直接事件),我们一般会通过ButtonClick新建一个事件,然后在这个事件里面写一些业务代码:

image-20230623144220354

private void Button_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show("You click me.");
}

当我们运行程序点击按钮时,Button_Click事件会被驱动响应点击的动作弹出一个消息对话框,没有问题对吧。我们都知道,在早期的GUI框架中,应用程序的外观和行为方式之间没有真正的分离,回到这里也一样,将业务代码直接写在事件处理程序中会导致界面和业务逻辑紧密耦合在一起,使得两者难以分离和独立变更。实际开发中,我们会给按钮一个特定功能,当按钮的功能发生变化时,我们需要在UI界面中修改与这个功能所绑定的东西,同时也需要调整业务代码,这就导致了界面元素(外观)和业务逻辑(行为)混在一起,使得按钮的XAML代码既承担了外观的定义,又承担了业务逻辑的实现。此外,假设我们有很多个一样的按钮,都需要在用户点击按钮后为它提供相同的功能,通过上述面的方法,当这个功能发生改变时,我们就需要调正每一个涉及到的按钮以及相应的事件。为了解决这个问题,WPF提供了命令机制(Command),可以将按钮的行为与其外观进行分离。

命令(Command)在WPF中是一种用于处理用户界面交互的机制,它们提供了一种在界面元素(UI)和后台逻辑之间进行解耦的方式,使得交互操作可以以一种统一的、可重用的方式进行处理。WPF命令的概念和实现是基于MVVM(Model-View-ViewModel)架构模式的,它使得界面元素的交互操作可以通过命令对象进行管理和处理,而不需要直接在界面代码中编写事件处理程序。

通过使用命令,我们能够更好地组织和管理界面交互行为,使得代码结构清晰,易于维护和扩展。同时,命令还提供了一些额外的功能,如参数传递、命令的可用性控制等,使得我们能够更灵活地处理用户的操作。

事件和命令:

事件是与用户动作进行联动的,而命令是那些想要与界面分离的动作,比如常见的复制粘贴命令,当我们点击一个具有复制功能的按钮时,相当于我们通过点击的这个动作触发了一个复制的命令,这样做的好处就是 - 界面的交互操作变得简单、代码可重用性提高,在不破坏后台逻辑的情况下可以更加灵活的控制用户界面。

命令模型

WPF命令模型主要包含以下几个基本元素:

命令(Command):指的是实现了ICommand接口的类,例如RoutedCommand类及其子类RoutedUICommand类,一般不包含具体逻辑。

命令源(Command Source):即命令的发送者,指的是实现了ICommandSource接口的类。像ButtonMenuItem等界面元素都实现了这个接口,单击它们都会执行绑定的命令。

命令目标(Command Target):即命令的接受者,指的是实现了IInputElement接口的类。

命令关联(Command Binding):即将一些外围逻辑和命令关联起来。

借用刘老师的图来看一下他们的关系:

image-20230623155843299

ICommand

ICommand接口,包含一个事件两个方法:

public interface ICommand
{
    //
    // 摘要:
    //     当出现影响是否应执行该命令的更改时发生。
    event EventHandler CanExecuteChanged;

    // 摘要:
    //     定义用于确定此命令是否可以在其当前状态下执行的方法。
    //
    // 参数:
    //   parameter:
    //     此命令使用的数据。如果此命令不需要传递数据,则该对象可以设置为 null。
    //
    // 返回结果:
    //     如果可以执行此命令,则为 true;否则为 false。
    bool CanExecute(object parameter);
    
    //
    // 摘要:
    //     定义在调用此命令时调用的方法。
    //
    // 参数:
    //   parameter:
    //     此命令使用的数据。如果此命令不需要传递数据,则该对象可以设置为 null。
    void Execute(object parameter);
}

反过来看:

  • Execute - 执行某个动作

  • CanExecute - 能不能执行动作

  • CanExecuteChanged - 命令状态发生变化是响应的事件

通过实现ICommand接口,我们可以创建自定义的命令对象,并将其与界面元素进行绑定。这样,界面元素就可以与命令相关联,通过调用命令的Execute方法来执行具体的操作,而无需直接编写事件处理程序。

定义命令

下面我们试着用命令的方式来实现上面的点击事件,并逐步理解模型中的内容。我们新建一个类MainViewModel来提供我们需要的功能方法:

using System.Windows;

namespace WPFDemo
{
    public class MainViewModel
    {
        public void ShowInfo()
        {
            MessageBox.Show("You click me.");
        }
    }
}

ShowInfo()这个时候跟UI界面是分开的对吧,有了方法之后,我们可以说MainViewModel中的ShowInfo就命令了吗,根据模型来看显然还不行。继续走,写一个实现ICommand接口但不带具体逻辑的类,比如CustomCommand:

image-20230623160635271

using System;
using System.Windows.Input;

namespace WPFDemo
{
    public class CustomCommand : ICommand
    {
        private readonly Action _execute;
        public CustomCommand(Action execute)
        {
            _execute = execute;
        }
        public event EventHandler CanExecuteChanged;
        public bool CanExecute(object parameter)
        {
            // 在这里实现命令的可执行逻辑
            return true; // 默认返回true,表示命令可执行
        }
        public void Execute(object parameter)
        {
            // 在这里实现命令的执行逻辑
            _execute?.Invoke();
        }
    }
}

之后在MainViewModel类中需要添加一个公共属性来暴露CustomCommand实例作为ShowInfoCommand,以便在XAML中进行绑定;

using System.Windows;

namespace WPFDemo
{
    public class MainViewModel
    {
        public CustomCommand ShowInfoCommand { get; set; }

        public MainViewModel()
        {
            ShowInfoCommand = new CustomCommand(ShowInfo);
        }
        public void ShowInfo()
        {
            MessageBox.Show("You click me.");
        }
    }
}

最后,将CustomCommand实例与界面元素进行绑定:

<Window x:Class="WPFDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:WPFDemo"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="800"
        Height="450"
        mc:Ignorable="d">
    <Window.Resources>
        <local:MainViewModel x:Key="ViewModel" />
    </Window.Resources>
    <Grid>
        <Button Command="{Binding ShowInfoCommand}" Content="Click Me" DataContext="{StaticResource ViewModel}"  />
    </Grid>
</Window>

设置数据上下文也可以在后台代码中完成

show

回到命令模型上来,梳理以下对应关系:

  1. CustomCommand 类本身是一个命令对象(ICommand),它实现了命令接口,包括 ExecuteCanExecute 方法。这个命令对象是命令模式中的 "具体命令"。
  2. MainViewModel 类作为命令的执行者(或者称为命令目标),其中包含了 ShowInfo 方法。在命令模式中,执行者负责实际执行命令所需的操作。在我们的示例中,ShowInfo 方法是具体执行的业务逻辑。
  3. XAML中,我们使用 CommandBinding 将按钮的 Click 事件与 ShowInfoCommand 关联起来。这个关联是通过在 WindowUserControlCommandBindings 集合中添加一个新的 CommandBinding 对象来完成的。这个 CommandBinding 指定了 ShowInfoCommand 作为命令和 MainViewModel 作为命令的执行者。

注意:Command属性仅仅作为Click行为的绑定,其他行为,如鼠标移入、移出。。。等行为,要使用另外的MVVM方式进行绑定。

最后,梳理下程序结构,可以看到,我们分别在MainViewModel.csMainWindow.xaml中书写业务代码和逻辑。

通知更改

WPF中,实现属性的通知更改是通过实现 INotifyPropertyChanged 接口来实现的。这个接口定义了一个 PropertyChanged 事件,当属性的值发生变化时,可以通过触发该事件来通知界面进行更新。

在进行演示之前,先来看看我们上面所使用的例子能否像之前学习的绑定一样实现自动更新,按照业务分离的逻辑,我们在MainViewModel.cs中添加一个Name字段并在页面进行绑定。

MainViewModel.cs

using System.Windows;

namespace WPFDemo
{
    public class MainViewModel
    {
        public CustomCommand ShowInfoCommand { get; set; }
        public string Name { get; set; }

        public MainViewModel()
        {
            Name = "狗熊岭第一狙击手";
            ShowInfoCommand = new CustomCommand(ShowInfo);
        }
        public void ShowInfo()
        {
            Name = "光头强";
            MessageBox.Show("You click me.");
        }
    }
}

MainWindow.xaml

<Window x:Class="WPFDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:WPFDemo"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="800"
        Height="450"
        WindowStartupLocation="CenterScreen"
        mc:Ignorable="d">
    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>
    <Grid>
        <StackPanel>
            <Button Command="{Binding ShowInfoCommand}" Content="Click Me" />
            <TextBox Text="{Binding Name}" />
        </StackPanel>
    </Grid>
</Window>

之前xaml中的数据上下文不太好,需要在每个控件中都定义一次😎

上面的代码中,我们将Name绑定到了TextBox的文本中,并在点击按钮是改变Name的值,如果它自己可以通知更改,自动更新的话那么在Name变化的时候文本也应该变化,对吧。我们运行试一下:

show

可以看到Name的变化时Text并没有随之变化,这说明Name发生改变以后并没有通知Text也进行变化。如果你喜欢捣鼓的话,可以看看Text如果被修改以后Name会不会变化。

回到正题,上文提到过,实现属性的通知更改是通过实现 INotifyPropertyChanged 接口来实现的,我们来对自定义的MainViewModel.cs稍作修改实现属性的通知更改:

using System.ComponentModel;
using System.Windows;

namespace WPFDemo
{
    public class MainViewModel : INotifyPropertyChanged
    {
        public CustomCommand ShowInfoCommand { get; set; }
        public event PropertyChangedEventHandler PropertyChanged;
        
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged(nameof(Name));
            }
        }
        
        private void OnPropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        
        public MainViewModel()
        {
            Name = "狗熊岭第一狙击手";
            ShowInfoCommand = new CustomCommand(ShowInfo);
        }
        
        public void ShowInfo()
        {
            Name = "光头强";
            MessageBox.Show("You click me.");
        }
    }
}

💭nameof(Name) 是C# 6.0 引入的一个语法糖,它可以在编译时获取属性、方法、字段、类型等成员的名称作为一个字符串。

WPF中,nameof(Name) 用于在属性更改通知中指定属性的名称。它的作用是避免硬编码属性名称,从而减少在重构过程中出现由于重命名属性而导致的错误。

修改过后,MainViewModel 类实现了 INotifyPropertyChanged 接口,并在 Name 属性的 setter 中进行了属性更改通知。当 Name 属性的值发生变化时,会触发 PropertyChanged 事件,通知界面进行更新。

show

进阶玩法

在实现通知更改的方式中,可以将通知更改的逻辑定义在一个基类中,例如 ViewModelBase 类。这个基类可以包含通用的属性更改通知实现,以便其他具体的视图模型类可以继承该基类并重用这些通知更改的功能。

以下是一个简单的示例,展示了如何在 ViewModelBase 类中实现通知更改:

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace WPFDemo
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

在上面的代码中,ViewModelBase 类实现了 INotifyPropertyChanged 接口,并提供了 OnPropertyChanged 方法用于触发属性更改通知事件,并将属性名称作为参数传递。

默认情况下,CallerMemberName 特性用于自动获取调用该方法的成员的名称,并作为属性名称传递。

通过继承 ViewModelBase 类,并使用 OnPropertyChanged 方法来设置属性,可以简化视图模型类中属性更改通知的实现:

MainViewModel.cs

using System.Windows;

namespace WPFDemo
{
    public class MainViewModel : ViewModelBase
    {
        public CustomCommand ShowInfoCommand { get; set; }
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged();
            }
        }

        private string _description;
        public string Description
        {
            get { return _description; }
            set { _description = value; OnPropertyChanged(); }
        }

        public MainViewModel()
        {
            Name = "狗熊岭第一狙击手";
            Description = "光头强";
            ShowInfoCommand = new CustomCommand(ShowInfo);
        }
        public void ShowInfo()
        {
            Name = "光头强";
            Description = "狗熊岭第一突破手,麦克阿瑟如是说。";
            MessageBox.Show("You click me.");
        }
    }
}

show

在这个示例中,MainViewModel 类继承了 ViewModelBase 类,并使用 OnPropertyChanged 方法来设置 Name 属性。当 Name 属性的值发生更改时,SetProperty 方法会自动处理属性更改通知的逻辑,无需手动触发事件或编写重复的代码。

通过将通知更改的实现定义在基类中,可以实现更简洁、可维护和可重用的代码,避免在每个具体的视图模型类中重复编写通知更改的逻辑。

夹带私货 - 命令参数

有时候我们需要在执行命令时传递参数。在WPF中,可以使用CommandParameter属性来传递参数给命令。

CommandParameter是一个附加属性,可以将任意对象指定为命令的参数。当命令执行时,命令的Execute方法会接收到该参数,并可以在命令处理逻辑中使用它。

以下是一个示例,展示如何在XAML中为命令指定CommandParameter

<Button Content="Click Me" Command="{Binding MyCommand}" CommandParameter="Hello, World!" />

在这个示例中,我们将ButtonCommand属性绑定到MyCommand命令。同时,我们通过CommandParameter属性指定了一个字符串参数"Hello, World!"。当点击按钮时,该参数将传递给MyCommand命令的Execute方法。

在命令的执行逻辑中,可以通过命令参数来获取传递的值。以下是一个简单的命令类示例:

CustomCommand.cs

using System;
using System.Windows.Input;

namespace WPFDemo
{
    public class CustomCommand : ICommand
    {
        private readonly Action<object> _execute;
        public CustomCommand(Action<object> execute)
        {
            _execute = execute;
        }
        public event EventHandler CanExecuteChanged;
        public bool CanExecute(object parameter)
        {
            // 在这里实现命令的可执行逻辑
            return true; // 默认返回true,表示命令可执行
        }
        public void Execute(object parameter)
        {
            // 在这里实现命令的执行逻辑
            _execute?.Invoke(parameter as string);
        }
    }
}

CustomCommand 类接受一个 Action<object> 类型的参数,在构造函数中将传递的方法保存到 _execute 字段中。然后,在 Execute 方法中,通过调用 _execute?.Invoke(parameter) 来执行传递的方法,并将 parameter 作为参数传递给该方法。

这样,当你在 MainViewModel 中创建 CustomCommand 实例时,可以将 ShowInfo 方法作为参数传递进去,

MainViewModel.cs:

using System.Windows;

namespace WPFDemo
{
    public class MainViewModel
    {
        public CustomCommand ShowInfoCommand { get; set; }
        public MainViewModel()
        {
            ShowInfoCommand = new CustomCommand(ShowInfo);
        }
        public void ShowInfo(object parameter)
        {
            MessageBox.Show(parameter as string);
        }
    }
}

那么ShowInfo(object parameter)的参数从哪里来呢 - CommandParameter附加属性:

<Window x:Class="WPFDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:WPFDemo"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="800"
        Height="450"
        WindowStartupLocation="CenterScreen"
        mc:Ignorable="d">
    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>
    <Grid>
        <StackPanel>
            <Button Command="{Binding ShowInfoCommand}" Content="Click Me" CommandParameter="Hello, World!"  />
        </StackPanel>
    </Grid>
</Window>

show

这样看上去可能比较蠢,我们可以些微调整一下在页面中完成显示内容的修改,代码就不贴了,大家应该知道:

show

通过命令参数,可以实现更灵活的命令处理逻辑,根据传递的参数来执行不同的操作。同时,使用命令参数也可以实现与界面元素的交互,例如根据按钮的点击位置传递坐标信息等。

MVVM

在文章开篇介绍命令时我们就有提到过WPF命令的概念和实现是基于MVVM(Model-View-ViewModel)架构模式的,而且从后续编码以及项目结构也可以看出来示例确实涉及了MVVM(Model-View-ViewModel)的思想,下边我们来看一下具体什么是MVVM以及一些主流的MVVM框架的引用与使用方法。

MVVM是一种软件架构模式,是Model(数据类型),View(界面),ViewModel(数据与界面之间的桥梁)的缩写,旨在将用户界面的逻辑与业务逻辑分离,以提高代码的可维护性、可测试性和可扩展性。

MVVM模式由以下三个核心组件组成:

  1. Model(模型):模型代表应用程序的数据和业务逻辑。它独立于用户界面和视图模型,负责处理数据的获取、存储和处理等任务。
  2. View(视图):视图是用户界面的可视部分,负责展示数据和与用户的交互。它通常是XAML文件,描述了用户界面的布局和外观。视图应该只关注外观和布局,而不涉及具体的业务逻辑。
  3. ViewModel(视图模型):视图模型充当视图与模型之间的中介。它通过暴露命令和属性来向视图提供数据绑定和事件处理的功能。视图模型通过与模型交互获取和处理数据,并将处理后的数据以属性的形式提供给视图绑定。视图模型还负责处理用户交互,并将交互结果传递给模型进行处理。

MVVM模式的优势在于它实现了界面逻辑和业务逻辑的分离,使得代码更加清晰、可维护和可测试,视图和视图模型的解耦使得可以独立开发和测试它们,同时也提供了更好的可扩展性,使得可以在不影响视图的情况下修改和增加业务逻辑。

总结起来,MVVM模式通过将用户界面、数据和业务逻辑进行分离,提供了一种结构化的方式来开发和维护WPF应用程序。它使得开发人员能够更好地管理代码,并使得应用程序更具可扩展性和可测试性。

构建MVVM框架时有自己造轮子的,就像上面命令的示例中那样;也有使用现成的开源项目,下面例举两个比较常见的现成的框架包。

MvvmLight

项目地址:mvvmlight

MVVM Light is not maintained anymore. Over the years, thousands of users have found this project useful with millions of downloads. We are truly overwhelmed by the kindness shown by a huge amount of members of this awesome community. Thanks so much for making this little project so popular and for making it all worthwhile.

MvvmLight不再维护。多年来,成千上万的用户发现这个项目有数百万的下载量。我们真的被这个令人敬畏的社区的大量成员所表现出的善意所淹没。非常感谢你让这个小项目如此受欢迎,让它一切都变得有价值。

We strongly recommend looking into the Microsoft MVVM Toolkit, an open source project from the Windows Community Toolkit, as the successor of MVVM Light.

我们强烈建议研究Microsoft MVVM Toolkit,这是Windows社区工具包中的一个开源项目,作为MVVM Light的继任者。

MvvmLight 是一个轻量级的 MVVM 框架,它提供了许多用于实现 MVVM 模式的工具和功能。以下是 MvvmLight 的一些主要特点和组成部分:

  1. ViewModel 基类: MvvmLight 提供了一个称为 ViewModelBase 的基类,用于派生自定义的视图模型类。这个基类实现了 INotifyPropertyChanged 接口,简化了属性更改通知的实现,并提供了一些其他实用的功能。
  2. 事件聚合器(Event Aggregator): MvvmLight 提供了一个事件聚合器,用于实现视图模型之间的松耦合通信。视图模型可以发布事件,其他视图模型可以订阅并响应这些事件,实现了解耦的消息传递机制。
  3. 命令实现: MvvmLight 提供了 RelayCommand 类,它是一个通用的实现了 ICommand 接口的命令类。通过使用 RelayCommand,开发人员可以轻松地在视图模型中定义和执行命令,并与视图进行绑定。
  4. 消息传递: MvvmLight 提供了一个消息传递机制,允许视图模型之间进行简单的消息通信。开发人员可以发送消息并订阅消息,以实现视图模型之间的通信和交互。
  5. 服务定位器(Service Locator): MvvmLight 提供了一个简单的服务定位器,用于解耦视图模型和具体的服务实现。通过使用服务定位器,视图模型可以轻松地获取所需的服务实例,而不需要直接实例化服务类。

MvvmLight 是一个功能丰富且易于使用的 MVVM 框架,它提供了许多有用的工具和功能,帮助开发人员更轻松地实现 MVVM 模式,接下来我们试一试将MvvmLight框架应用在我们昨天的例子中。

在项目中添加MvvmLight的NuGet包,以便引入MvvmLight框架的相关组件和功能,对应版本安装就行了,注意区分自己的项目是在.Net Framework框架下还是.Net Core

image-20230624124732466

安装完以后可以看到,项目的结构会发生些许变化,它会自动创建了一个名为ViewModel的文件夹以及MainViewModel.csViewModelLocator.cs文件,同时App.xaml文件也会发生变化:

  • MainViewModel.cs - 继承自由MvvmLight框架提供的ViewModelBase类,ViewModelBase类又继承类ObservableObject,同时实现了ICleanup接口,而ObservableObject类实现了INotifyPropertyChanged接口,用于通知属性的改变,需要时调用RaisePropertyChanged()即可。

  • ViewModelLocator.cs - 文件中只有一个ViewModelLocator类,类中包括一个构造函数、一个类型为MainViewModelMain属性、以及一个静态的Cleanup函数。在构造函数中,创建了一个SimpleIoc类型的单实例,用于注册MainViewModel,然后用ServiceLocator对这个SimpleIoc类型的单实例进行包裹,方便统一管理。

  • App.xaml文件变化 - ViewModelLocator类被生成资源字典并加入到了全局资源。

  • image-20230624133020772

    • DataContext="{Binding Main, Source={StaticResource Locator}}"

安装完成以后,我们把之前我们自造的MVVM模式的文件从项目排除,在生成的MainViewModel.cs编写之前的业务逻辑代码:

ViewModel\MainViewModel.cs

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Windows;

namespace WPFDemo.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        public RelayCommand<object> ShowInfoCommand { get; }

        public string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; RaisePropertyChanged(); }
        }
        public string _description;
        public string Description
        {
            get { return _description; }
            set { _description = value; RaisePropertyChanged(); }
        }

        public MainViewModel()
        {
            Name = "菠萝吹雪";
            Description = "哦,尊严,多少钱一斤哪。";

            ShowInfoCommand = new RelayCommand<object>(ShowInfo);
        }
        public void ShowInfo(object parameter)
        {
            Name = parameter as string;
            Description = parameter as string;
            MessageBox.Show(parameter as string);
        }
    }
}

MainWindow.xaml

<Window x:Class="WPFDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:WPFDemo"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="800"
        Height="450"
        DataContext="{Binding Main, Source={StaticResource Locator}}"
        WindowStartupLocation="CenterScreen"
        mc:Ignorable="d">
    <Grid>
        <StackPanel>
            <TextBox Height="30" Text="{Binding Name}" />
            <TextBox Height="30" Text="{Binding Description}" />
            <TextBox x:Name="textBox"
                     Height="30"
                     Text="Hello,World!" />
            <Button Command="{Binding ShowInfoCommand}"
                    CommandParameter="{Binding ElementName=textBox, Path=Text}"
                    Content="Click Me" />
        </StackPanel>
    </Grid>
</Window>

运行程序:

show

CommunityToolkit.Mvvm

MVVM 工具包简介

CommunityToolkit.Mvvm包(又名 MVVM 工具包,以前名为 Microsoft.Toolkit.Mvvm)是一个现代、快速且模块化的 MVVM 库。

image-20230624142924683

💭没有.Net Framework的版本,本章终。。。

Done

WPF中,命令是一种用于处理用户交互操作的机制。它将操作行为与界面元素解耦,使得界面元素可以通过命令进行触发和执行相应的逻辑。WPF中的命令模型通过ICommand接口和相关的实现类来实现。命令模式的设计思想是将命令的发送者(例如按钮)与命令的执行者(例如视图模型中的方法)解耦,使得它们之间不直接依赖,提高代码的可维护性和重用性。

MVVM(Model-View-ViewModel)是一种用于构建WPF应用程序的软件架构模式,它将应用程序的逻辑分为三个核心部分:模型、视图和视图模型。模型表示数据模型,负责数据的获取和处理;视图表示用户界面,负责界面的展示;视图模型是视图和模型之间的桥梁,负责处理业务逻辑、提供数据绑定和命令等功能。MVVM通过数据绑定和命令的方式实现视图和视图模型的通信,将界面逻辑和业务逻辑分离,使得代码更加清晰、可测试和可维护。

posted @ 2023-06-24 14:40  PixelKiwi  阅读(1468)  评论(0编辑  收藏  举报