INotifyPropertyChangedを使ってプロパティ変更を自動反映!C#Formアプリの作り方

この記事は約10分で読めます。

管理人
管理人

こんにちは、上様です!今回は、C#のFormアプリでINotifyPropertyChangedを使う方法を解説します。

サンプルだけ欲しい人はここからダウンロードして下さい!

INotifyPropertyChangedは、データバインディングを行う際に非常に便利なインターフェイスで、プロパティの変更を通知するために使います。WPFやXamarinなどでもよく使われますが、Windows Formsアプリケーションでも役立ちます。

この記事では、INotifyPropertyChangedの基本的な使い方をサンプルコードを交えて紹介します。これからFormアプリを作りたいと思っている方や、データバインディングをもっと簡単に行いたい方におすすめの内容です。

次に、それぞれのクラス毎にサンプルコードと解説を載せますので、ぜひ参考にしてみてください!

1. BindableInfoBase クラス

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace FormINotifyPropertyChangedSampleApp
{
    /// <summary>
    /// バインディング可能なプロパティを提供するための基底クラス。
    /// INotifyPropertyChangedを実装し、プロパティの変更通知機能を提供します。
    /// このクラスを継承することで、プロパティの変更通知に関するコードを繰り返し書く必要がなくなり、コードの再利用性と可読性が向上します。
    /// </summary>
    public class BindableInfoBase : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged

        /// <summary>
        /// プロパティの変更が発生したときに発生するイベント。
        /// バインドされたUI要素はこのイベントを通じて変更を検知します。
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion INotifyPropertyChanged

        /// <summary>
        /// PropertyChangedイベントを発生させ、指定されたプロパティの変更を通知します。
        /// </summary>
        /// <param name="propertyName">変更が発生したプロパティの名前。省略可能で、呼び出し元のプロパティ名が自動的に使用されます。</param>
        protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            // PropertyChangedイベントを発生させ、UIにプロパティの変更を通知します。
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// プロパティの値を設定し、変更があった場合にはPropertyChangedイベントを発生させます。
        /// </summary>
        /// <typeparam name="T">プロパティの型</typeparam>
        /// <param name="storage">プロパティの現在の値を保持する変数</param>
        /// <param name="value">設定する新しい値</param>
        /// <param name="propertyName">プロパティの名前</param>
        /// <returns>値が変更された場合はtrue、変更がない場合はfalse</returns>
        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(storage, value)) return false;

            storage = value;
            this.RaisePropertyChanged(propertyName);

            return true;
        }
    }
}

解説

BindableInfoBase クラスは、プロパティの変更通知を簡単に実装するための基底クラスです。このクラスを継承することで、プロパティの変更通知に関連するコードを各クラスで重複して書く必要がなくなり、保守性と再利用性が向上します。また、SetProperty メソッドを使用することで、プロパティの値が変更されたときに自動的に通知が発生する仕組みが提供されています。

Person クラス

namespace FormINotifyPropertyChangedSampleApp
{
    /// <summary>
    /// 人の情報を表すクラス。
    /// 名前と年齢のプロパティを持ち、プロパティ変更時に通知を行います。
    /// </summary>
    public class Person : BindableInfoBase
    {
        ///<summary>名前を保持します。</summary>
        private string name;

        ///<summary>名前を取得または設定します。</summary>
        public string Name
        {
            get => this.name;
            set => this.SetProperty(ref this.name, value);
        }

        ///<summary>年齢を保持します。</summary>
        private int age;

        ///<summary>年齢を取得または設定します。</summary>
        public int Age
        {
            get => this.age;
            set => this.SetProperty(ref this.age, value);
        }
    }
}

解説

Person クラスは BindableInfoBase クラスを継承しており、NameAge のプロパティを持っています。これらのプロパティが変更されたとき、SetProperty メソッドを使用してプロパティ変更通知が発生します。これにより、バインドされたUI要素に対してプロパティの変更が自動的に反映されます。

Formクラス

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace FormINotifyPropertyChangedSampleApp
{
    /// <summary>
    /// フォームアプリケーションのメイン画面。
    /// 複数の人の情報を表示し、データバインディングを使用してプロパティ変更をUIに反映させます。
    /// </summary>
    public partial class Form1 : Form
    {
        /// <summary>
        /// 人のリストを保持し、データバインディングの対象とします。
        /// </summary>
        public BindingList<Person> People { get; set; }

        /// <summary>
        /// フォームの初期化を行います。
        /// </summary>
        public Form1()
        {
            InitializeComponent();

            this.People = new BindingList<Person>
            {
                new Person { Name = "Alice", Age = 30 },
                new Person { Name = "Bob", Age = 25 },
                new Person { Name = "Charlie", Age = 35 }
            };

            BindControls();
        }

        /// <summary>
        /// コントロールにデータをバインドします。
        /// </summary>
        private void BindControls()
        {
            // nameofでプロパティ名を取得するために使用
            var c = this.People.First();

            // ListBoxにバインド
            listBoxPeople.DataSource = this.People;
            listBoxPeople.DisplayMember = nameof(c.Name);

            // ComboBoxにバインド
            comboBoxPeople.DataSource = this.People;
            comboBoxPeople.DisplayMember = nameof(c.Name);

            // DataGridViewにバインド
            dataGridViewPeople.AutoGenerateColumns = false;     // 勝手に列を追加されないようにする
            dataGridViewPeople.DataSource = this.People;
            this.Column1.DataPropertyName = nameof(c.Name);
            this.Column2.DataPropertyName = nameof(c.Age);
        }

        /// <summary>
        /// 「年齢を追加」ボタンがクリックされたときに、Aliceの年齢を1つ増やします。
        /// </summary>
        private void BtnAddAge_Click(object sender, EventArgs e)
        {
            this.People.Where(x => x.Name == "Alice").First().Age++;
        }
    }
}

解説

Form1 クラスはWindows Formsのメインフォームを表しており、People という BindingList<Person> を持っています。このリストはUIコントロールにバインドされており、プロパティが変更されるとUIに自動的に反映されます。例えば、「年齢を追加」ボタンをクリックすると、Aliceの年齢が1つ増加し、リストにバインドされたすべてのコントロールが自動的に更新されます。

これにより、INotifyPropertyChangedを使ったデータバインディングの効果を実際に体感することができます。ぜひ実際に試してみてください!

サンプルコード

コメント

タイトルとURLをコピーしました