YKToolkit.Bindings

 MVVM パターンの要となるデータバインディング機能を実現するためには、 INotifyPropertyChanged インターフェースおよび ICommand インターフェースの実装が欠かせません。 しかし、いつも自前で実装するのは手間がかかるため、 YKToolkit.Controls.dll ではこれらのインターフェースの実装を済ませてあるクラスを YKToolkit.Bindings 名前空間で公開しています。

ページ内リンク

NotificationObject クラス

 INotifyPropertyChanged インターフェースを実装している NotificationObject クラスは 次のように定義しています。

namespace YKToolkit.Bindings
{
    using System.ComponentModel;
    using System.Runtime.CompilerServices;

    /// <summary>
    /// INotifyPropertyChanged インターフェースを実装したプロパティ値変更通知機能を備えた抽象クラス
    /// </summary>
    public abstract class NotificationObject : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged のメンバ
        /// <summary>
        /// プロパティ変更時に発生します。
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion  // INotifyPropertyChanged のメンバ

        /// <summary>
        /// PropertyChanged イベントを発行します。
        /// </summary>
        /// <param name="propertyName">プロパティ名を指定します。省略した場合、所有するすべてのプロパティに対して変更通知をおこないます。</param>
        protected virtual void RaisePropertyChanged([CallerMemberName]string propertyName = null)
        {
            var h = PropertyChanged;
            if (h != null)
                h(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// プロパティ値に変更があった場合、プロパティ値を変更し、 PropertyChanged イベントを発行します。
        /// </summary>
        /// <typeparam name="T">プロパティの型</typeparam>
        /// <param name="target">変更するプロパティを指定します。</param>
        /// <param name="value">変更後のプロパティ値を指定します。</param>
        /// <param name="propertyName">プロパティ名を指定します。省略した場合、所有するすべてのプロパティに対して変更通知をおこないます。</param>
        /// <returns>プロパティ値に変更があった場合に true を返します。</returns>
        protected virtual bool SetProperty<T>(ref T target, T value, string propertyName = null)
        {
            if (Equals(target, value))
                return false;
            target = value;
            RaisePropertyChanged(propertyName);
            return true;
        }
    }
}
Code 1 : NotificationObject クラスの定義
NotificationObject クラスは抽象クラスなので、必ず継承して使うようにします。
using YKToolkit.Bindings;

public class MainViewModel : NotificationObject
{
}
Code 2 : NotificationObject クラスの継承
NotificationObject クラスの SetProperty() メソッドは、プロパティ変更のためのヘルパです。 プロパティの set アクセサでは、値の代入と RaisePropertyChanged() メソッドのコールをしなければなりません。 こういった決まったコードは、プロパティの数が増えれば増えるほど省略したくなります。 そんなとき、SetProperty() メソッドを使用することで値の代入とメソッドのコールを 1 行で書くことができます。 具体的には次のように使用します。
private string name;
public string Name
{
    get { return name; }
    set { SetProperty(ref name, value); }
}

private int age;
public int Age
{
    get { return age; }
    set
    {
        if (SetProperty(ref age, value))
            RaisePropertyChanged("CheatAge");
    }
}

public int CheatAge
{
    get { return Age > 40 ? Age - 10 : Age; }
}
Code 3 : SetProperty() メソッドの使用例
SetProperty() メソッドにプロパティの実体を ref 修飾子を付けて渡すことで、 内側でプロパティ値の更新をおこないます。 また、RaisePropertyChanged() メソッドを内側でコールしていますが、 プロパティ名を省略することで、すべてのプロパティについて変更通知をおこなうため、 上記のようにかなり簡略化することができます。 また、SetProperty() メソッドは、プロパティ値に変更があったとき true を返します。 これを利用することで、上記のように他のプロパティ変更通知を同時におこなうことができます。

DelegateCommand クラス

 ICommand インターフェースを実装している DelegateCommand クラスは 次のように定義しています。

namespace YKToolkit.Bindings
{
    using System;
    using System.Windows.Input;

    public class DelegateCommand : ICommand
    {
        /// <summary>
        /// コマンドの実体を保持します。
        /// </summary>
        private Action<object> _execute;

        /// <summary>
        /// コマンドの実行可能判別処理の実態を保持します。
        /// </summary>
        private Func<object, bool> _canExecute;

        /// <summary>
        /// 新しいインスタンスを生成します。
        /// </summary>
        /// <param name="execute">コマンドの実体を指定します。</param>
        public DelegateCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        /// <summary>
        /// 新しいインスタンスを生成します。
        /// </summary>
        /// <param name="execute">コマンドの実体を指定します。</param>
        /// <param name="canExecute">コマンドの実行可能判別処理の実体を指定します。</param>
        public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        /// <summary>
        /// CanExecuteChanged イベントを発行します。
        /// </summary>
        public static void RaiseCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested();
        }

        #region ICommand のメンバ
        /// <summary>
        /// コマンドの実行可能判別処理を実行します。
        /// </summary>
        /// <param name="parameter">コマンドパラメータを指定します。</param>
        /// <returns>コマンドが実行可能であるとき true を返します。</returns>
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        /// <summary>
        /// コマンドの実行可能判別条件が変更されたときに発生します。
        /// </summary>
        public event System.EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested += value; }
        }

        /// <summary>
        /// コマンドを実行します。
        /// </summary>
        /// <param name="parameter">コマンドパラメータを指定します。</param>
        public void Execute(object parameter)
        {
            if (_execute != null)
                _execute(parameter);
        }
        #endregion ICommand のメンバ
    }
}
Code 4 : DelegateCommand クラスの定義
使うときはコンストラクタでコマンドの実行処理を指定します。 実行可能判別処理は省略することができます。省略した場合はいつでもコマンド実行可能となります。 使用例を次に示します。
private int counter;

private DelegateCommand buttonCommand;
public DelegateCommand ButtonCommand
{
    get
    {
        return buttonCommand ?? (buttonCommand = new DelegateCommand(
        _ =>
        {
            counter++;
        },
        _ =>
        {
            return counter < 10;
        }));
    }
}
Code 5 : DelegateCommand クラスの使用例
このように、DelegateCommand クラスはコンストラクタで一般的にラムダ式を使います。 上記の例では、コンストラクタの第 1 引数に counter をインクリメントするコマンドを指定し、 第 2 引数に counter が 10 未満であれば true を返す実行可能判別処理を指定しています。 したがって、上記のコードは、初期状態から 10 回連続で ButtonCommand が実行可能で、 それ以降は counter が 10 未満にならない限り実行できなくなるということになります。

 YKToolkit.Controls.dll を導入したサンプルプログラムを公開しています。こちらを参考にして下さい。

Designed by CSS.Design Sample