WPF MVVMの完全なケースを見る

WPF MVVMの完全なケースを見る

MVVMなしでWPFを学ぶことは魂が欠けているようです。MVVMとは何か?MVVMを学ぶ理由は、簡単な追加、削除、変更の小さな例を使って、MVVMの基本知識とMVVMアーキテクチャのプログラム開発を通じて学ぶ方法を簡単に説明し、共有するためにのみ使用します。

最后更新 2022/02/21 13:43
小六公子
预计阅读 9 分钟
分类
WPF
专题
WPF MVVMフレームワークPrismシリーズ
标签
.NET WPF MVVM 建築設計の構造

MVVMなしでWPFを学ぶことは魂が欠けているようです。MVVMとは何か?MVVMを学ぶ理由は、簡単な追加、削除、変更の小さな例を使って、MVVMの基本知識とMVVMアーキテクチャのプログラム開発を通じて学ぶ方法を簡単に説明し、共有するためにのみ使用します。

MVVMとは?

MVVMはModel-View-ViewModelの略です。基本的にはMVC (Model-View-Controller)の改良版です。モデル-ビュー-ビューモデルです。それぞれ定義は以下の通り。

  • モデルはバックエンドに渡されるデータを指します。
  • “ビュー”は、表示されているページを指します。
  • ビューモデル:MVVMパターンの心臓部であり、ビューとモデルをつなぐブリッジです。2つの方向性がある。
    • 一つは、“モデル”を“ビュー”に変換することです。つまり、バックエンドから渡されたデータを見ているページに変換します。実装方法はデータバインディングです。
    • 2つ目は、“ビュー”を“モデル”に変換することです。つまり、見たページをバックエンドのデータに変換します。これはDOMイベントリスニングです。両方の方向が実装され、データの双方向バインディングと呼ばれています。

MVVMの概略図を以下に示します。

MvvmLightプラグインをインストールする

プロジェクト名右クリック-> NuGetパッケージの管理-> MvvmLight->インストール。以下の通り。

ライセンスの承認ウィンドウがポップアップし、次のように“承認”をクリックします。

MvvmLightのインストールが成功すると、必要なサードパーティライブラリを自動的に参照し、サンプルコンテンツをデフォルトで生成します。不要なものは以下のように削除する必要があります。

MVVMサンプルスクリーンショット

データのCRUD [追加削除変更]の基本操作は、主にMVVMによって実現されます。

MVVMの開発手順

  1. Modelレイヤーの作成

この例は主に学生情報の追加削除を目的としているので、以下のようにStudentモデルクラスを作成します。

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. DALレイヤの作成

例を簡単にするために、データベース操作をシミュレートし、基礎となるデータを次のように構築します。

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. Viewレイヤの作成

ビューレイヤーは、ユーザーとのインタラクション、ユーザーデータの表示、およびイベントへの応答を行います。この例では、ビューレイヤーには主にデータクエリ表示、新規および編集ページがあります。

ビュー層では、主にコマンドのバインディングとデータバインディングです。

  • DataGridTextColumnで、表示する列プロパティ名をBinding=""の形式でバインドします。
  • ButtonボタンでCommand=""の形式で応答するコマンドをバインドします。
  • TextBoxテキスト·ボックス内で、“Text=”という形式でクエリー条件プロパティーをバインドします。

次のようなデータ表示ウィンドウ

<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>

次のようにページを追加および編集します。

<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. ViewModelレイヤの作成

ViewModel層はMVVMの中核であり、その役割を果たします。ViewModelは、GalaSoft.MvvmLight.ViewModelBaseベースクラスを継承する必要があります。

ViewModelのプロパティはデータバインディングを実装し、コマンドはユーザインタラクションの応答を実装します。以下の通り。

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. データコンテキスト#データコンテキスト#

各レイヤーが別々に作成されると、どのように関連付けられますか?その答えはDataContext(データコンテキスト)です。

次のように、ページコンテキストを照会します。

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

次のように、ページコンテキストを追加します。

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;
        }
    }
}

まとめまとめまとめ

MVVMは低結合、再利用、テスト、独立した開発の利点があり、コア要素は2つです。

  • 属性の変化の通知は、データのリアルタイム更新を可能にします。
  • コマンドは、ユーザーとプログラム間のデータとアルゴリズムのブリッジです。

備考:コメント

この記事はMVVMの簡単な入門例として、一緒に学び、一緒に進歩することを目的としています。WPFの他の入門についてあまり知らない場合は、他のブログ記事を参照してください。

玉楼春·別后不知道君近

欧陽修[宋代]

あなたの近くには、どのくらい退屈ですか?徐々に離れて、本がなく、魚はどこに沈むか尋ねた。

夜の風竹ノック秋韻、万葉千の声はすべて憎しみです。したがって、単一の枕の夢は、ランプや炎にはなりません。注:ああ、うん。

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2024/01/25

C#WPFにおけるFluentValidationの使用

この記事では、C#WPFプロジェクトでFluentValidationをプロパティ検証に使用する方法を詳しく見て、MVVMパターンでこれを実装する方法を示します。

继续阅读
同分类 / 同标签 2023/06/11

7/7

NET Core3環境でMVVMフレームワークPrismのダイアログサービスを使用する方法は、prismシリーズの最後の記事です。

继续阅读
同分类 / 同标签 2023/06/11

6/7

NET Core3でMVVMフレームワークを使用する方法Prism地域ベースのナビゲーションシステム

继续阅读
同分类 / 同标签 2023/06/11

5/7

NET Core3環境でMVVMフレームワークを使用する方法Prismのゾーンマネージャを使用したビューの管理

继续阅读