その2で、簡単なNameプロパティをもつPersonクラスを作って色々してみた。
前回:http://blogs.wankuma.com/kazuki/archive/2008/01/29/119892.aspx
前回までのコード。
public class Person : DependencyObject
{
#region Nameプロパティ
// デフォルト値匿名希望のNameプロパティ
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(Person),
new PropertyMetadata("匿名 希望", NameChanged, CoerceNameValue),
ValidateName);
private static void NameChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine(e.Property + " Changed");
Console.WriteLine(" NewValue: " + e.NewValue);
Console.WriteLine(" OldValue: " + e.OldValue);
}
// 名前には様をつけないといけないです
private static object CoerceNameValue(DependencyObject target, object baseValue)
{
Console.WriteLine("CoerceNameValue");
string name = baseValue as string;
// 名前入力されてなかったら仕方ない
if (string.IsNullOrEmpty(name))
{
return string.Empty;
}
// 様がついてるか、デフォルト値のときはそのまま
if (name.EndsWith("様") || name == NameProperty.DefaultMetadata.DefaultValue as string)
{
return name;
}
// そうじゃなければ様をつける
return name + "様";
}
// 名前は、姓と名の間に全角スペースが入るとです
private static bool ValidateName(object value)
{
Console.WriteLine("ValidateName(" + value + ")");
string name = value as string;
if (string.IsNullOrEmpty(name))
{
return true;
}
return name.IndexOf(' ') != -1;
}
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
#endregion
}
Nameプロパティしかない分際で異様に長いです。
今回は、読み取り専用の依存プロパティをやってみようと思う。
読み取り専用の依存プロパティといっても、普通のC#のreadonlyキーワードで作るやつとは違う。
readonlyは、コンストラクタや変数初期化子とかでのみ代入をできる。
依存プロパティでの読み取り専用は、もうちょっとゆる~い感じ。設定は何処でもできちゃう。
普通のDependencyPropertyと違うのは、プロパティに値を設定するときは、DependencyPropertyKeyを使うというところと、登録の際にDependencyProperty.RegisterReadOnlyを使うところだけ。
早速試してみよう。
Personクラスに読み取り専用のBirthdayプロパティをつける。
public class Person : DependencyObject
{
#region Nameプロパティ
// 省略
#endregion
#region Birthdayプロパティ
// RegisterReadOnlyメソッドの戻り値はDependencyPropertyKeyになる
internal static readonly DependencyPropertyKey BirthdayPropertyKey =
DependencyProperty.RegisterReadOnly("Birthday", typeof(DateTime),
typeof(Person),
new PropertyMetadata());
// KeyのDependencyPropertyプロパティからDependencyPropertyのインスタンスを取得
public static readonly DependencyProperty BirthdayProperty = BirthdayPropertyKey.DependencyProperty;
#endregion
}
Mainからさくっと使ってみる。
class Program
{
static void Main(string[] args)
{
var p = new Person();
Console.WriteLine(p.GetValue(Person.BirthdayProperty));
}
}
実行結果
0001/01/01 0:00:00
とりあえず、何処でも値をセットしてないのでDateTimeのデフォルト値?かなんかが表示されてる。
こいつの値をDependencyPropertyのノリで設定しようとすると例外が発生してしまう。
class Program
{
static void Main(string[] args)
{
var p = new Person();
try
{
p.SetValue(Person.BirthdayProperty, DateTime.Now);
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.Message);
}
}
}
実行結果
'Birthday' プロパティは、読み取り専用として登録されており、承認キーがないと変更できません。
認証キーが必要なのね。ということで認証キーとはDependencyPropertyKeyのことです。
こいつを使ってSetValueを呼び出すと値を設定することができる。
一般的には、書き込みを行うためのDependencyPropertyKeyは外部に公開しないように管理して使う。
外部にはDependencyPropertyを公開することで、プロパティへのアクセスを提供するという形になる。
これまでどおり普通のプロパティもセットで提供すると下のような感じになる。
public class Person : DependencyObject
{
#region Nameプロパティ
// 省略
#endregion
#region Birthdayプロパティ
// RegisterReadOnlyメソッドの戻り値はDependencyPropertyKeyになる
internal static readonly DependencyPropertyKey BirthdayPropertyKey =
DependencyProperty.RegisterReadOnly("Birthday", typeof(DateTime),
typeof(Person),
new PropertyMetadata());
// KeyのDependencyPropertyプロパティからDependencyPropertyのインスタンスを取得
public static readonly DependencyProperty BirthdayProperty = BirthdayPropertyKey.DependencyProperty;
public DateTime Birthday
{
get { return (DateTime)GetValue(BirthdayProperty); }
protected set { SetValue(BirthdayPropertyKey, value); }
}
#endregion
public Person()
{
// 生まれた日が誕生日ですよ
Birthday = DateTime.Now;
}
}
これで読み取り専用の依存プロパティが完成だ。
因みに、読み取り専用の依存プロパティにも普通の依存プロパティと同じようにメタデータをつけることができるようになっている。
下みたいなメソッドをつけることで、読み取り専用依存プロパティの値を変更することも可能。
変更可能な読み取り専用って変な気分もするけどまぁそういうもんなんだろう。
public void Rebone()
{
Birthday = DateTime.Now;
}
この読み取り専用プロパティは、何がいいかっていうとTriggerで監視することができるっぽい。
まだ未検証なので噂です。