WPF-MVVM架构

在WPF(Windows Presentation Foundation)开发中,MVVM(Model-View-ViewModel)是一种非常流行的设计模式,它旨在将应用程序的UI逻辑与业务逻辑和数据模型分离,从而提高代码的可维护性、可测试性和可扩展性。MVVM模式由三个核心部分组成:Model(模型)、View(视图)和ViewModel(视图模型)。

1. Model(模型)

Model代表应用程序的数据和业务逻辑。它通常包含数据字段、属性以及操作这些数据的方法。Model是独立于View和ViewModel的,意味着它不知道也不关心数据是如何被展示或修改的。Model层主要负责数据的存储、检索和验证等业务逻辑。

2. View(视图)

View是用户界面的表示层,负责展示数据。在WPF中,View通常是由XAML(可扩展应用程序标记语言)和后台代码(C#或VB.NET)组成的。XAML用于定义UI的布局和样式,而后台代码则用于处理用户交互。View通过数据绑定与ViewModel进行交互,但View本身不直接处理业务逻辑或数据访问。

3. ViewModel(视图模型)

ViewModel是Model和View之间的桥梁,它封装了与View交互的Model数据。ViewModel为View提供数据,并处理用户交互逻辑。ViewModel通常包含与View绑定的属性(这些属性通常与Model中的属性相对应),以及用于更新这些属性的命令(如按钮点击事件)。ViewModel还负责将Model的数据转换为View可以理解和展示的格式,并处理用户输入,将其转换为Model可以理解的格式。

MVVM的优势

  • 高内聚低耦合:Model、View和ViewModel之间的职责明确,相互之间的依赖关系降到最低,提高了代码的可维护性和可扩展性。
  • 易于测试:由于ViewModel不依赖于View,因此可以独立于UI进行单元测试。
  • 提高开发效率:设计师和开发人员可以并行工作,设计师可以专注于View的设计,而开发人员则专注于ViewModel和Model的实现。
  • 更好的用户体验:ViewModel可以处理复杂的用户交互逻辑,使得View层更加简洁,从而提供更好的用户体验。

实现MVVM

在WPF中实现MVVM通常需要使用一些辅助库,如Prism、Caliburn.Micro或MVVM Light等,这些库提供了实现MVVM模式所需的基础结构和工具。然而,即使没有这些库,你也可以通过WPF的数据绑定、命令和INotifyPropertyChanged接口等特性来手动实现MVVM模式。

阶段一:

html 复制代码
<Grid>
    <StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0 5 0 0">
            <TextBlock Text="第一个数:" Margin="0 0 10 0"/>
            <TextBox Name="textNumber1" Width="150"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
            <TextBlock Text="第二个数:" Margin="0 0 10 0"/>
            <TextBox Name="textNumber2" Width="150"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
            <TextBlock Text="结       果:" Margin="0 0 10 0"/>
            <TextBox Name="textResult" Width="150"/>
        </StackPanel>

        <Button x:Name="btnResult" Content="计算结果" Width="150" HorizontalAlignment="Left" Margin="10 15 0 0" Click="btnResult_Click"/>
    </StackPanel>

</Grid>
cs 复制代码
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void btnResult_Click(object sender, RoutedEventArgs e)
    {
        this.textResult.Text = int.Parse(textNumber1.Text) + int.Parse(textNumber2.Text) + "";
    }
}

阶段二:

创建ViewModel文件

1.更改启动的路径

App.xaml文件 : StartupUri="Views/MainWindow.xaml"

2.更改MainWindow.xaml文件
html 复制代码
<Grid>
    <StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0 5 0 0">
            <TextBlock Text="第一个数:" Margin="0 0 10 0"/>
            <TextBox  Width="150" Text="{Binding Number1}"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
            <TextBlock Text="第二个数:" Margin="0 0 10 0"/>
            <TextBox  Width="150" Text="{Binding Number2}"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
            <TextBlock Text="结       果:" Margin="0 0 10 0"/>
            <TextBox  Width="150" Text="{Binding Result}"/>
        </StackPanel>

        <Button x:Name="btnResult" Content="计算结果" Width="150" HorizontalAlignment="Left" Margin="10 15 0 0" Command="{Binding CalcCommand}"/>
    </StackPanel>

</Grid>
3.更改MainWindow.xaml.cs文件把视图模型对象给到界面绑定
cs 复制代码
 public MainWindow()
 {
     InitializeComponent();
     //把视图模型对象给到界面绑定
     this.DataContext = new MainWindowViewModel(); 
 }
4.MainWindowViewModel编写
cs 复制代码
public class MainWindowViewModel:INotifyPropertyChanged
{
    private int _number1=10;
    public int Number1 {
        get { return _number1; }
        set{ _number1 = value;  }
    }

    private int _number2=16;
    public int Number2
    {
        get { return _number2; }
        set { _number2 = value;  }
    }

    private int _result;
    public int Result
    {
        get { return _result; }
        set { _result = value; OnPropertyChanged(nameof(Result)); }
    }


    //数值更改进行界面的同时
    public event PropertyChangedEventHandler? PropertyChanged;
    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }


    public CommandBase CalcCommand 
    {
        get => new CommandBase 
        { 
            DoExecte = new Action<object>(CalcResult)
        };
    }
    //真正需要执行的计算逻辑
    private void CalcResult(object obj)
    {
        this.Result = _number1 + _number2;
        Debug.Write("AAA");
    }

}
5.CommandBase通用类的编写,按钮控件按下触发
cs 复制代码
  public class CommandBase : ICommand
  {
      public event EventHandler? CanExecuteChanged;
      public Func<object, bool>? CanExecution {  get; set; }
      public Action<object>? DoExecte { get; set; }

      //IsEnabled="False",就不会发起操作
      public bool CanExecute(object? parameter)
      {
          if(CanExecution != null)
          {
              CanExecute(parameter);
          }
          return true;
      }


      //正在执行命令的方法
      public void Execute(object? parameter)
      {
          DoExecte!.Invoke(parameter!);
      }
  }

阶段三

将数据放入Model中

1.CommandBase
cs 复制代码
 public class CommandBase : ICommand
 {
     public event EventHandler? CanExecuteChanged;
     public Func<object, bool>? CanExecution {  get; set; }
     public Action<object>? DoExecte { get; set; }

     //IsEnabled="False",就不会发起操作
     public bool CanExecute(object? parameter)
     {
         if(CanExecution != null)
         {
             CanExecute(parameter);
         }
         return true;
     }


     //正在执行命令的方法
     public void Execute(object? parameter)
     {
         DoExecte!.Invoke(parameter!);
     }
 }
2.NotifyBase
cs 复制代码
public class NotifyBase:INotifyPropertyChanged
{
    //数值更改进行界面的同时
    public event PropertyChangedEventHandler? PropertyChanged;
    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
3.MainWindowModel
cs 复制代码
 public class MainWindowModel : NotifyBase
 {
     private int _number1 = 10;
     public int Number1
     {
         get { return _number1; }
         set { _number1 = value; }
     }

     private int _number2 = 16;
     public int Number2
     {
         get { return _number2; }
         set { _number2 = value; }
     }

     private int _result;
     public int Result
     {
         get { return _result; }
         set { _result = value; OnPropertyChanged(nameof(Result)); }
     }

     private string _time = "8888-88-88 88:88:88";
     public string Time
     {
         get { return _time; }
         set { _time = value; OnPropertyChanged(nameof(Time)); }
     }

 }

4.MainWindowViewModel

cs 复制代码
public class MainWindowViewModel
{
    public MainWindowModel MainWindowModel {get; set;}=new MainWindowModel();

    public MainWindowViewModel()
    {
        Task.Run(async () =>
        {
            while (true)
            {
                await Task.Delay(500);
                MainWindowModel.Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            }
        });
    }


    public CommandBase CalcCommand 
    {
        get => new CommandBase 
        { 
            DoExecte = new Action<object>(CalcResult)
        };
    }
    //真正需要执行的计算逻辑
    private void CalcResult(object obj)
    {
        MainWindowModel.Result = MainWindowModel.Number1 + MainWindowModel.Number2;
        Debug.Write("AAA");
    }

}
5.MainWindow.xaml
cs 复制代码
<Grid>
    <StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0 5 0 0">
            <TextBlock Text="第一个数:" Margin="0 0 10 0"/>
            <TextBox  Width="150" Text="{Binding MainWindowModel.Number1}"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
            <TextBlock Text="第二个数:" Margin="0 0 10 0"/>
            <TextBox  Width="150" Text="{Binding MainWindowModel.Number2}"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
            <TextBlock Text="结       果:" Margin="0 0 10 0"/>
            <TextBox Name="Tresult"  Width="150" Text="{Binding MainWindowModel.Result}"/>
            <!--同步文本框中的文本-->
            <TextBlock Text="{Binding ElementName=Tresult ,Path=Text}" Margin="10 0 0 0"/>
            
        </StackPanel>

        <Button x:Name="btnResult" Content="计算结果" Width="150" HorizontalAlignment="Left" Margin="10 15 0 0" Command="{Binding CalcCommand}"/>
        <TextBlock Text="{Binding MainWindowModel.Time}" Width="150" Height="20" Margin="10"/>
    </StackPanel>

</Grid>
6.MainWindow.xaml.cs
cs 复制代码
   public MainWindow()
   {
       InitializeComponent();
       //把视图模型对象给到界面绑定
       this.DataContext = new MainWindowViewModel(); 
   }
相关推荐
懒大王爱吃狼40 分钟前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
瓜牛_gn42 分钟前
mysql特性
数据库·mysql
秃头佛爷2 小时前
Python学习大纲总结及注意事项
开发语言·python·学习
奶糖趣多多2 小时前
Redis知识点
数据库·redis·缓存
待磨的钝刨2 小时前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
CoderIsArt3 小时前
Redis的三种模式:主从模式,哨兵与集群模式
数据库·redis·缓存
△曉風殘月〆3 小时前
WPF MVVM入门系列教程(二、依赖属性)
c#·wpf·mvvm
XiaoLeisj4 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
励志成为嵌入式工程师5 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
逐·風5 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#