読者です 読者をやめる 読者になる 読者になる

プログラミング C# - 翔ソフトウェア (Sho's)

C#/.NET/ソフトウェア開発など

INotifyPropertyChanged の実装に便利なクラスとコードスニペット

WPFSilverlightWindows 8 や Windows RT の Windows ストア アプリでは、UI の記述に XAML を使うことが多い。

そして、データバインドするために INotifyPropertyChanged をしょっちゅう実装することになる。

これが結構面倒なので、開発者は普通、少しでも楽になるような工夫をしている。

そのような「有り勝ちな工夫」について。

■ 工夫する前のコード

「工夫」する前のコードは、こんな感じだ。

using System.ComponentModel;

class MyViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    int id = 0;

    public int Id
    {
        get { return id; }
        set {
            if (value != id) {
                id = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Id"));
            }
        }
    }

    string name = string.Empty;

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Name"));
            }
        }
    }
}

プロパティ毎の記述が、結構煩雑だ。

上記ではプロパティが2つしかないが、プロパティが多くなれば、この繰り返しはそれに比例して増えることになる。

■ ヘルパー クラス

Expression や Caller Info を使ったヘルパー クラスを用意するともう少し記述が楽になる。

※ 参考:

こんな感じのヘルパー クラスだ。

using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;

public static class PropertyChangedEventHandlerExtensions
{
    // Caller Info を使っているので C# 5.0 以降
    public static void Raise(this PropertyChangedEventHandler onPropertyChanged, object sender, [CallerMemberName] string propertyName = "")
    {
        if (onPropertyChanged != null)
            onPropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
    }

    public static void Raise<PropertyType>(this PropertyChangedEventHandler onPropertyChanged, object sender, Expression<Func<PropertyType>> propertyExpression)
    { onPropertyChanged.Raise(sender, propertyExpression.GetMemberName()); }

    static string GetMemberName<MemberType>(this Expression<Func<MemberType>> expression)
    { return *1;
        }
    }</Description>
			<Author></Author>
			<SnippetTypes>
				<SnippetType>Expansion</SnippetType>
			</SnippetTypes>
		</Header>
		<Snippet>
			<Declarations>
				<Literal>
					<ID>type</ID>
					<ToolTip>type name</ToolTip>
					<Default>int</Default>
				</Literal>
				<Literal>
					<ID>field</ID>
					<ToolTip>field name</ToolTip>
					<Default>myField</Default>
				</Literal>
				<Literal>
					<ID>property</ID>
					<ToolTip>property name</ToolTip>
					<Default>MyProperty</Default>
				</Literal>
			</Declarations>
			<Code Language="csharp"><![CDATA[ $type$ $field$ = default($type$);

		public $type$ $property$
		{
			get { return $field$; }
			set {
				if (!value.Equals($field$)) {
					$field$ = value;
					PropertyChanged.Raise(this);
				}
			}
		}
$end$]]></Code>
		</Snippet>
	</CodeSnippet>
</CodeSnippets>

これを、"propin.snippet" のような名前で、テキスト ファイルとして任意の場所に保存する。

次に、Visual Studio で、メニューから 「ツール」 - 「コード スニペット マネージャー」を開く。

コード スニペット マネージャー
コード スニペット マネージャー

「インポート」ボタンを押す。先ほどのテキスト ファイル "propin.snippet" を選択する。

コード スニペットのインポート
コード スニペットのインポート

"My Code Snippets" にチェックをいれて、「完了」ボタンを押す。

これでコード スニペットが使えるようになる。

例えば、先程の MyViewModel クラスの Name プロパティの下にカーソルを合わせて、propin とタイプして[TAB]キーを2回押してみよう。

新しいプロパティが簡単に挿入でき、型名、フィールド名、プロパティ名が楽に設定できるだろう。

コード スニペットによるプロパティの追加
コード スニペットによるプロパティの追加

*1:MemberExpression)expression.Body).Member.Name; } }

これを使うことで、先のコードは「少しだけ」簡潔になる。また、プロパティ名の変更に対して安全になった。

using System.ComponentModel;

class MyViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    int id = 0;

    public int Id
    {
        get { return id; }
        set {
            if (value != id) {
                id = value;
                PropertyChanged.Raise(this);
            }
        }
    }

    string name = string.Empty;

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                PropertyChanged.Raise(this);
            }
        }
    }
}

■ コード スニペット

更に Visual Studio でコード スニペットを利用することで、タイピングの手間を減らすことができる。

このコード スニペットは、例えば次のような XML だ。

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
	<CodeSnippet Format="1.0.0">
	<Header>
		<Title>Property for INotifyPropertyChanged</Title>
		<Shortcut>propin</Shortcut>
		<Description>Code snippet for property for INotifyPropertyChanged.
Use this class (for C# 5.0 or later):
    using System.ComponentModel;
    using System.Runtime.CompilerServices;

    public static class PropertyChangedEventHandlerExtensions 
    {
        public static void Raise(this PropertyChangedEventHandler onPropertyChanged, object sender, [CallerMemberName] string propertyName = "")
        {
            if (onPropertyChanged != null)
                onPropertyChanged(sender, new PropertyChangedEventArgs(propertyName