Visit WPF's complete MVVM case

Visit WPF's complete MVVM case

Learning WPF without learning MVVM seems to lack a soul. So what is MVVM? Why should I learn MVVM? This will use a simple small example of additions, deletions and changes to briefly describe the basic knowledge of MVVM and how to develop the program of the MVVM architecture. It is only for learning and sharing. If there are any shortcomings, please correct them.

最后更新 2/21/2022 1:43 PM
小六公子
预计阅读 12 分钟
分类
WPF
专题
WPF MVVM Framework Prism Series
标签
.NET WPF MVVM architecture design

Learning WPF without learning MVVM seems to lack a soul. So what is MVVM? Why should I learn MVVM? This will use a simple small example of additions, deletions and changes to briefly describe the basic knowledge of MVVM and how to develop the program of the MVVM architecture. It is only for learning and sharing. If there are any shortcomings, please correct them.

What is MVVM?

MVVM is short for Model-View-ViewModel. It is essentially an improved version of MVC (Model-View-Controller). That is, model-view-view model. They are defined as follows:

  • "Model" refers to the data passed on the back end.
  • [View] refers to the page you see.
  • [View Model] The core of the mvvm model is the bridge connecting view and model. It has two directions:
    • The first is to convert the "model" into the "view", that is, convert the data passed by the backend into the page you see. The way to achieve this is: data binding.
    • The second is to convert [View] into [Model], that is, convert the pages you see into back-end data. The way to achieve this is: DOM event monitoring. Achieving both directions is achieved, which we call two-way binding of data.

The schematic diagram of MVVM is as follows:

Install the MvvmLight plugin

Right click on the project name--> Manage NuGet Packages--> Search for MvvmLight--> Install. As follows:

The Accept License window pops up and click [Accept] as follows:

After MvvmLight is successfully installed, it automatically references the required third-party libraries and generates sample content by default. Some unnecessary ones need to be deleted, as shown below:

Screenshot of MVVM example

The basic operations of CRUD [Add, Delete, Change and Check] of data are mainly realized through MVVM, as follows:

MVVM development steps

  1. Create a Model layer

This example is mainly about adding, deleting, and revising student information, so a Student model class is created as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfApp3.Models
{
    /// <summary>
    /// 学生类
    /// </summary>
    public class Student
    {
        /// <summary>
        /// 唯一标识
        /// </summary>
        public int Id { get; set; }

        /// <summary>
        /// 学生姓名
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 年龄
        /// </summary>
        public int Age { get; set; }

        /// <summary>
        /// 班级
        /// </summary>
        public string Classes { get; set; }
    }
}
  1. Create a DAL layer

To simplify the example, simulate database operations to build the underlying data as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApp3.Models;

namespace WpfApp3.DAL
{
    public class LocalDb
    {
        private List<Student> students;

        public LocalDb() {
            init();
        }

        /// <summary>
        /// 初始化数据
        /// </summary>
        private void init() {
            students = new List<Student>();
            for (int i = 0; i < 30; i++)
            {
                students.Add(new Student()
                {
                    Id=i,
                    Name=string.Format("学生{0}",i),
                    Age=new Random(i).Next(0,100),
                    Classes=i%2==0?"一班":"二班"
                });
            }
        }

        /// <summary>
        /// 查询数据
        /// </summary>
        /// <returns></returns>
        public List<Student> Query()
        {
            return students;
        }

        /// <summary>
        /// 按名字查询
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public List<Student> QueryByName(string name)
        {
            return students.Where((t) => t.Name.Contains(name)).ToList();//FindAll((t) => t.Name.Contains(name));
        }

        public Student QueryById(int Id)
        {
            var student = students.FirstOrDefault((t) => t.Id == Id);
            if (student != null)
            {
                return new Student() {
                    Id=student.Id,
                    Name=student.Name,
                    Age=student.Age,
                    Classes=student.Classes
                };
            }
            return null;
        }


        /// <summary>
        /// 新增学生
        /// </summary>
        /// <param name="student"></param>
        public void AddStudent(Student student)
        {
            if (student != null)
            {
                students.Add(student);
            }
        }

        /// <summary>
        /// 删除学生
        /// </summary>
        /// <param name="Id"></param>
        public void DelStudent(int Id)
        {
            var student = students.FirstOrDefault((t) => t.Id == Id); //students.Find((t) => t.Id == Id);
            if (student != null)
            {
                students.Remove(student);
            }

        }
    }


}
  1. Create View Layer

The View layer interacts with users, displays user data, and responds to events. In this example, the View layer mainly includes data query display, adding and editing pages.

At the View layer, it is mainly the binding of commands and the binding of data.

  • Bind the column attribute name to be displayed in the DataGridTextColumn with the form Binding ="".
  • Bind the command to respond to on the Button button with the form Command ="".
  • Bind the query condition properties in the TextBox text box with the form Text ="".

The data display window is as follows:

<Window
  x:Class="WpfApp3.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:WpfApp3"
  mc:Ignorable="d"
  Title="MainWindow"
  Height="450"
  Width="800"
>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="80"></RowDefinition>
      <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <StackPanel
      Orientation="Horizontal"
      Grid.Row="0"
      Margin="5"
      VerticalAlignment="Center"
    >
      <TextBlock Text="姓名:" Margin="10" Padding="5"></TextBlock>
      <TextBox
        x:Name="sname"
        Text="{Binding Search}"
        Width="120"
        Margin="10"
        Padding="5"
      ></TextBox>
      <button
        x:Name="btnQuery"
        Content="查询"
        Margin="10"
        Padding="5"
        Width="80"
        Command="{Binding QueryCommand}"
      ></button>
      <button
        x:Name="btnReset"
        Content="重置"
        Margin="10"
        Padding="5"
        Width="80"
        Command="{Binding ResetCommand}"
      ></button>
      <button
        x:Name="btnAdd"
        Content="创建"
        Margin="10"
        Padding="5"
        Width="80"
        Command="{Binding AddCommand}"
      ></button>
    </StackPanel>
    <DataGrid
      x:Name="dgInfo"
      Grid.Row="1"
      AutoGenerateColumns="False"
      CanUserAddRows="False"
      CanUserSortColumns="False"
      Margin="10"
      ItemsSource="{Binding GridModelList}"
    >
      <DataGrid.Columns>
        <DataGridTextColumn
          Header="Id"
          Width="100"
          Binding="{Binding Id}"
        ></DataGridTextColumn>
        <DataGridTextColumn
          Header="姓名"
          Width="100"
          Binding="{Binding Name}"
        ></DataGridTextColumn>
        <DataGridTextColumn
          Header="年龄"
          Width="100"
          Binding="{Binding Age}"
        ></DataGridTextColumn>
        <DataGridTextColumn
          Header="班级"
          Width="100"
          Binding="{Binding Classes}"
        ></DataGridTextColumn>
        <DataGridTemplateColumn Header="操作" Width="*">
          <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
              <StackPanel
                Orientation="Horizontal"
                VerticalAlignment="Center"
                HorizontalAlignment="Center"
              >
                <button
                  x:Name="edit"
                  Content="编辑"
                  Width="60"
                  Margin="3"
                  Height="25"
                  CommandParameter="{Binding Id}"
                  Command="{Binding DataContext.EditCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"
                ></button>
                <button
                  x:Name="delete"
                  Content="删除"
                  Width="60"
                  Margin="3"
                  Height="25"
                  CommandParameter="{Binding Id}"
                  Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"
                ></button>
              </StackPanel>
            </DataTemplate>
          </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
      </DataGrid.Columns>
    </DataGrid>
  </Grid>
</Window>

Add and edit pages as follows:

<Window
  x:Class="WpfApp3.Views.StudentWindow"
  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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:WpfApp3.Views"
  mc:Ignorable="d"
  Title="StudentWindow"
  Height="440"
  Width="500"
  AllowsTransparency="False"
  WindowStartupLocation="CenterScreen"
  WindowStyle="None"
>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="60"></RowDefinition>
      <RowDefinition></RowDefinition>
      <RowDefinition Height="60"></RowDefinition>
    </Grid.RowDefinitions>
    <TextBlock FontSize="30" Margin="10">修改学生信息</TextBlock>
    <StackPanel Grid.Row="1" Orientation="Vertical">
      <TextBlock FontSize="20" Margin="10" Padding="5">姓名</TextBlock>
      <TextBox
        x:Name="txtName"
        FontSize="20"
        Padding="5"
        Text="{Binding Model.Name}"
      ></TextBox>
      <TextBlock FontSize="20" Margin="10" Padding="5">年龄</TextBlock>
      <TextBox
        x:Name="txtAge"
        FontSize="20"
        Padding="5"
        Text="{Binding Model.Age}"
      ></TextBox>
      <TextBlock FontSize="20" Margin="10" Padding="5">班级</TextBlock>
      <TextBox
        x:Name="txtClasses"
        FontSize="20"
        Padding="5"
        Text="{Binding Model.Classes}"
      ></TextBox>
    </StackPanel>
    <StackPanel
      Grid.Row="2"
      Orientation="Horizontal"
      HorizontalAlignment="Right"
    >
      <button
        x:Name="btnSave"
        Content="保存"
        Margin="10"
        FontSize="20"
        Width="100"
        Click="btnSave_Click"
      ></button>
      <button
        x:Name="btnCancel"
        Content="取消"
        Margin="10"
        FontSize="20"
        Width="100"
        Click="btnCancel_Click"
      ></button>
    </StackPanel>
  </Grid>
</Window>
  1. Create a ViewModel layer

ViewModel layer is the core of MVVM and plays the role of connecting the preceding and the following. ViewModel needs to inherit the GalaSoft. MvmLight. ViewModelBase base class.

Properties in ViewModel implement data binding, commands implement user interaction response. As follows:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using WpfApp3.DAL;
using WpfApp3.Models;
using WpfApp3.Views;

namespace WpfApp3.ViewModel
{
    /// <summary>
    ///
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        #region 属性及构造函数

        private LocalDb localDb;

        private ObservableCollection<Student> gridModelList;

        public ObservableCollection<Student> GridModelList
        {
            get { return gridModelList; }
            set
            {
                gridModelList = value;
                RaisePropertyChanged();
            }
        }

        /// <summary>
        /// 查询条件
        /// </summary>
        private string search;

        public string Search
        {
            get { return search; }
            set
            {
                search = value;
                RaisePropertyChanged();
            }
        }


        /// <summary>
        ///
        /// </summary>
        public MainViewModel()
        {
            localDb = new LocalDb();
            QueryCommand = new RelayCommand(this.Query);
            ResetCommand = new RelayCommand(this.Reset);
            EditCommand = new RelayCommand<int>(this.Edit);
            DeleteCommand = new RelayCommand<int>(this.Delete);
            AddCommand = new RelayCommand(this.Add);
        }

        #endregion

        #region command

        /// <summary>
        /// 查询命令
        /// </summary>
        public RelayCommand QueryCommand { get; set; }

        /// <summary>
        /// 重置命令
        /// </summary>
        public RelayCommand ResetCommand { get; set; }

        /// <summary>
        /// 编辑
        /// </summary>
        public RelayCommand<int> EditCommand { get; set; }

        /// <summary>
        /// 删除
        /// </summary>
        public RelayCommand<int> DeleteCommand { get; set; }

        /// <summary>
        /// 新增
        /// </summary>
        public RelayCommand AddCommand { get; set; }

        #endregion

        public void Query()
        {
            List<Student> students;
            if (string.IsNullOrEmpty(search))
            {
                students = localDb.Query();
            }
            else
            {
                students = localDb.QueryByName(search);
            }

            GridModelList = new ObservableCollection<Student>();
            if (students != null)
            {
                students.ForEach((t) =>
                {
                    GridModelList.Add(t);
                });
            }
        }

        /// <summary>
        /// 重置
        /// </summary>
        public void Reset()
        {
            this.Search = string.Empty;
            this.Query();
        }

        /// <summary>
        /// 编辑
        /// </summary>
        /// <param name="Id"></param>
        public void Edit(int Id)
        {
            var model = localDb.QueryById(Id);
            if (model != null)
            {
                StudentWindow view = new StudentWindow(model);
                var r = view.ShowDialog();
                if (r.Value)
                {
                    var newModel = GridModelList.FirstOrDefault(t => t.Id == model.Id);
                    if (newModel != null)
                    {
                        newModel.Name = model.Name;
                        newModel.Age = model.Age;
                        newModel.Classes = model.Classes;
                    }
                    this.Query();
                }
            }
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="Id"></param>
        public void Delete(int Id)
        {
            var model = localDb.QueryById(Id);
            if (model != null)
            {
                var r = MessageBox.Show($"确定要删除吗【{model.Name}】?","提示",MessageBoxButton.YesNo);
                if (r == MessageBoxResult.Yes)
                {
                    localDb.DelStudent(Id);
                    this.Query();
                }
            }
        }

        /// <summary>
        /// 新增
        /// </summary>
        public void Add()
        {
            Student model = new Student();
            StudentWindow view = new StudentWindow(model);
            var r = view.ShowDialog();
            if (r.Value)
            {
                model.Id = GridModelList.Max(t => t.Id) + 1;
                localDb.AddStudent(model);
                this.Query();
            }
        }
    }
}
  1. data context

When each layer is created separately, how can it be related? The answer is DataContext.

Query the page context, as follows:

namespace WpfApp3
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            MainViewModel viewModel = new MainViewModel();
            viewModel.Query();
            this.DataContext = viewModel;
        }
    }
}

Add a new page context as follows:

namespace WpfApp3.Views
{
    /// <summary>
    /// StudentWindow.xaml 的交互逻辑
    /// </summary>
    public partial class StudentWindow : Window
    {
        public StudentWindow(Student student)
        {
            InitializeComponent();
            this.DataContext = new
            {
                Model = student
            };
        }

        private void btnSave_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = true;
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false;
        }
    }
}

summary

MVVM has the advantages of low coupling, reusable, testable, and independent development. There are only two core elements:

  • Notification when attributes change can achieve real-time data updates.
  • Commands are the bridge between the user and the program to implement data and algorithms.

remarks

As a simple introduction example of MVVM, this article aims to attract valuable information, learn together, and make progress together. If you don't know much about other introductory knowledge of WPF, you can refer to other blog posts.

Yulouchun·I don't know where you are after parting

Ouyang Xiu [Song Dynasty]

After parting, I don't know how far you are, but I feel sad and boring. As we walk farther and farther, there is no book. Where is the fish sinking in the water?

At night, the wind and bamboo beat the charm of autumn, and thousands of leaves and thousands of voices are filled with hatred. Therefore, I lay my head in my dream and searched for it, but the dream did not turn into a lamp and burned out. Note: (y)

Keep Exploring

延伸阅读

更多文章