WindowsFormsアプリでのMVVMパターンサンプル
以下はWindowsFormsアプリでMVVMパターンを実装したサンプルコードです
MVVMとは
MVVMは、Model-View-ViewModelの略で、Windows Presentation Foundation (WPF) や Universal Windows Platform (UWP) などのXAMLベースのプラットフォームで広く使用されるデザインパターンの一つです。
MVVMは、View、ViewModel、Modelの3つのコンポーネントによって構成されています。
- View: ユーザーインターフェースの部分で、ユーザーがアプリケーションを操作するためのUI要素を持ちます。
- ViewModel: ViewとModelの仲介役で、Viewに表示するためのデータや操作ロジックを提供し、Viewから受け取ったイベントを処理します。ViewModelは、Viewとは完全に独立しているため、Viewの変更がViewModelに影響を与えず、またViewModelの変更がViewに反映されます。
- Model: アプリケーションのビジネスロジックやデータを持ち、ViewModelからアクセスされます。Modelは、データの取得や保存などの実際の処理を担当します。
MVVMは、ViewとViewModelを分離することで、ビジネスロジックやデータモデルを変更せずにUIを変更できる柔軟性を提供します。また、ViewModelは、テストしやすく、再利用性が高いため、アプリケーションの品質を向上させることができます。
MVVMは、WPFやUWPなどのXAMLベースのプラットフォームでの開発に最適化されていますが、他のプラットフォームでも使用することができます。しかし、MVVMを使用する場合は、コードの複雑さが増すため、より高度な開発スキルが必要になります。
クラス図
サンプル概要
サンプルでは、Modelクラスは単純な文字列を持ち、ViewModelクラスはModelをラップして、Viewに表示するためのプロパティを提供しています。Viewクラスでは、ViewModelを生成して、ViewModelのTextプロパティとViewのTextプロパティをバインドしています。
このように実装することで、ViewとViewModelが疎結合になり、アプリケーションの拡張性やテストのしやすさが向上します。また、ViewModelがINotifyPropertyChangedインターフェースを実装することで、Viewに変更を通知することができます。
注意点として、Windows FormsアプリケーションでMVVMパターンを実装する場合、ViewとViewModelのバインディングにはいくつかの方法がありますが、上記の例ではDataBindingsを使用しています。ただし、DataBindingsはバグがある場合があり、WPFやUWPのような他のフレームワークと比較すると制限があります。また、ViewModelの初期化や状態管理に関する課題もあります。したがって、Windows FormsアプリケーションでMVVMパターンを使用する場合は、よく考慮して実装する必要があります。
実行結果
2つのTextBox(FirstNameとLastName)に文字を入力するイベントで、プロパティが更新されます
フォームデザイン
ラベルとボタンを2つずつ配置します
ソリューション
コード
Model
namespace MVVMSample
{
public class PersonModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
ViewModel
using System.ComponentModel;
namespace MVVMSample
{
public class PersonViewModel : INotifyPropertyChanged
{
private PersonModel _person;
public event PropertyChangedEventHandler PropertyChanged;
public string FirstName
{
get { return _person.FirstName; }
set
{
_person.FirstName = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FirstName)));
}
}
public string LastName
{
get { return _person.LastName; }
set
{
_person.LastName = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LastName)));
}
}
public PersonViewModel()
{
_person = new PersonModel();
}
}
}
View
namespace MVVMSample
{
public partial class MainForm : Form
{
private PersonViewModel _viewModel;
public MainForm()
{
InitializeComponent();
_viewModel = new PersonViewModel();
firstNameTextBox.DataBindings.Add(nameof(firstNameTextBox.Text), _viewModel, nameof(_viewModel.FirstName));
lastNameTextBox.DataBindings.Add(nameof(lastNameTextBox.Text), _viewModel, nameof(_viewModel.LastName));
}
}
}
namespace MVVMSample
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.firstNameTextBox = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.lastNameTextBox = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// firstNameTextBox
//
this.firstNameTextBox.Location = new System.Drawing.Point(129, 55);
this.firstNameTextBox.Name = "firstNameTextBox";
this.firstNameTextBox.Size = new System.Drawing.Size(100, 23);
this.firstNameTextBox.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(63, 58);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(60, 15);
this.label1.TabIndex = 1;
this.label1.Text = "FirstName";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(63, 101);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(59, 15);
this.label2.TabIndex = 3;
this.label2.Text = "LastName";
//
// lastNameTextBox
//
this.lastNameTextBox.Location = new System.Drawing.Point(129, 98);
this.lastNameTextBox.Name = "lastNameTextBox";
this.lastNameTextBox.Size = new System.Drawing.Size(100, 23);
this.lastNameTextBox.TabIndex = 2;
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.label2);
this.Controls.Add(this.lastNameTextBox);
this.Controls.Add(this.label1);
this.Controls.Add(this.firstNameTextBox);
this.Name = "MainForm";
this.Text = "Form1";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private TextBox firstNameTextBox;
private Label label1;
private Label label2;
private TextBox lastNameTextBox;
}
}
Program.cs
namespace MVVMSample
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new MainForm());
}
}
}
ディスカッション
コメント一覧
まだ、コメントがありません