C#のWinFormsでINotifyPropertyChangedを使ってコントロールとデータバインディングしてみる。

C# コンピュータ
C#

正直MVVMなWPFは小規模なプログラミングだと面倒を感じ、WinFormのアプリケーションを作成していたところ、アプリケーションが肥大化するにつれてMVVMのような構造が欲しくなってきました。
WinFormのコントロールにもDataBindingsプロパティがあり、これに自前で作成したViewModelとバインディングできると便利かと思い調べたところ、WPFで使ったINotifyPropertyChangedから派生させたViewModelクラスのオブジェクトをデータソースとしてバインディングすることが出来るようです。

ソースコード

ファイル名:Form1.Designer.cs

namespace Form1VMSample1;

partial class Form1
{
    /// <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.components = new System.ComponentModel.Container();
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(800, 450);
        this.Text = "Form1";
    }
    TextBox textBox1 = new()
    {
        Location = new Point(10, 10),
        Size = new Size(500, 60),
        Text = "textBox1", // 初期値
    };
    Label label1 = new()
    {
        Location = new Point(10, 70),
        Size = new Size(500, 60),
        Text = "label1", // 初期値
    };
    Button button1 = new()
    {
        Location = new Point(10, 140),
        Size = new Size(500, 60),
        Text = "button1", // 初期値
    };

    #endregion
}

ファイル名:Form1.cs

namespace Form1VMSample1;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        var viewModel = new Form1ViewModel();
        textBox1.DataBindings.Add(new Binding("Text", viewModel, "MyName"));
        label1.DataBindings.Add(new Binding("Text", viewModel, "MyName"));

        this.Load += (sender, e) =>
        {
            Controls.AddRange([textBox1, label1, button1]);
        };
    }
}

ファイル名:Form1ViewModel.cs

using System.ComponentModel;
using System.Diagnostics;

namespace Form1VMSample1;
class Form1ViewModel : INotifyPropertyChanged
{
    public Form1ViewModel()
    {
        PropertyChanged += (o, e) =>
        {
            if (e.PropertyName == "MyName")
            {
                Debug.Print($"{_myName}"); // <= 変更後の値になっている
            }
        };
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    protected virtual void OnPropertyChanged(string name)
    {
        if (PropertyChanged is not null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }

    private string _myName = "Default";

    public string MyName
    {
        get
        {
            return _myName;
        }
        set
        {
            if (_myName == value) return;
            _myName = value;
            OnPropertyChanged("MyName");
        }
    }
}

実行


起動してみると、textBox1label1値がコンストラクタセットした初期値とは異なり、ViewModelのMyNameプロパティの初期値の”Default”に置き換えられていることが確認できます。
ViewModel⇒Viewがバインディングしていることが確認できたので、次にtextBox1の値を”Default”から”AAA”へ変更してみます。

lavel1の値も連動して”AAA”に変化しています。これはtextBox1とlabel1のTextプロパティを同じviewModel.MyNameとバインディングしているため連動したのだと思われます。
値を変更した際Form1ViewModelのPropertyChangedが呼び出され、_myNameの値がtextBox1で入力した値に変更されたことが出来ました。

コメント