« 構造体/クラスプロパティを展開可能にする(3) | トップページ | Win32APIで発生したエラー処理(GetLastError 処理相当の処理) »

2010年10月 9日 (土)

構造体/クラスプロパティを展開可能にする(4)[最終回]

前回の「構造体/クラスプロパティを展開可能にする(3)」の続きです。前回、テスト用の構造体プロパティ(RectStruct)に関しては、問題無く展開出来るようになりました。しかしクラスプロパティ(RectClass)は、同じ RectConverer を介しても、子プロパティを変更しても親プロパティの文字列は自動更新されない問題がありました。
また、展開された子プロパティの表示順も制御したいと思うでしょう。親プロパティの文字列表示が、Left, Top, Right, Bottom なのに、展開順がプロパティ名のアルファベット順(Bottom, Left, Right, Top)になっているのは不満です。なので、この2点を解決したいと思います。

まず、クラスプロパティのときだけ、子プロパティを変更しても親プロパティの文字列が即時更新されない問題の解決方法ですが、どうも CreateInstance メソッドをオーバーライドして、インスタンスを再作成すればよさそうです。CreateInstance を有効にするには、GetCreateInstanceSupported もオーバーライドする必要があります。問題があるのが、RectClassだけなので、RectStruct のときはデフォルトの処理を行う様にしています。

public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
    if (context.PropertyDescriptor.PropertyType == typeof(RectClass))
        return true;
    return base.GetCreateInstanceSupported (context);
}

public override object CreateInstance(ITypeDescriptorContext context,
    System.Collections.IDictionary propertyValues)
{
    if (context.PropertyDescriptor.PropertyType == typeof(RectClass))
    {
        return new RectClass((int)propertyValues["Left"], (int)propertyValues["Top"],
            (int)propertyValues["Right"], (int)propertyValues["Bottom"]);
    }
    return base.CreateInstance (context, propertyValues);
}

上記の様に、GetCreateInstanceSupported, CreateInstance メソッドをオーバーライドすると、RectClass のメンバプロパティ、例えば Top の値を変更して Enter キーを入力すると、その場で RectClass の項のプロパティ値も変更される様になります。

これで、最初の問題点は解決しました。

次に、プロパティの表示順制御ですが、これには、GetPropertiesSupported, GetProperties メソッドをオーバーライドします。下の例では、RectStruct, RectClass のときだけ、Left, Top, Right, Bottom の順で出す様にしています。

public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
    if (context.PropertyDescriptor.PropertyType == typeof(RectStruct) ||
        context.PropertyDescriptor.PropertyType == typeof(RectClass))
        return true;
    return base.GetPropertiesSupported (context);
}

public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context,
    object value, Attribute[] attributes)
{
    if (context.PropertyDescriptor.PropertyType == typeof(RectStruct) ||
        context.PropertyDescriptor.PropertyType == typeof(RectClass))
    {
        PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(value, attributes);
        string[] propertyOrder = {"Left", "Top", "Right", "Bottom"};
        return pdc.Sort(propertyOrder);
    }
    return base.GetProperties (context, value, attributes);
}

これで、プロパティの表示順が、Left, Top, Right, Bottom になります。

以上で、必要と思われる実装が全て完了しました。これで、4回に分けて書いて来た内容を、まとめてみましょう。

  1. 構造体、クラスともに、そのメンバをきちんとプロパティ定義する事で、プロパティウィンドウで展開可能にする事が可能となる。
  2. 簡易的には、の記述を、親プロパティ定義に付加すれば良い。
  3. プロパティウィンドウの挙動までしっかり制御を行ないたい場合は、ExpandableObjectConverter を継承した TypeConverter クラスを定義する必要がある。
    1. 構造体プロパティの場合、CanConvertTo, ConvertTo, CanConvertFrom, ConvertFrom の各メソッドをオーバーライドする必要がある。
    2. クラスプロパティの場合、上記4メソッドに加え、GetCreateInstanceSupported, CreateInstance メソッドをオーバーライドした方がよい。
    3. 構造体/クラスプロパティともに、展開順を制御したい場合は、更に GetPropertiesSupported, GetProperties メソッドをオーバーライドする。

以上、この様な結論で終わらせたいと思います。でも、Delphi のときには独自クラスのプロパティでも、自動的に展開可能になっていたから、ちょっと面倒ですね。

では、最後に最終形態のサンプルコードのプロジェクト一式を、CABファイルにまとめて置いておきます。

「PropertyTest3.cab」をダウンロード


記事とは直接関係ないのですが...
調査をしていた時に、Bug Catharsis 2009-03-09の記事を見つけたので、ここにメモしておきます。詳細は見てない(理解できないともいう)のではっきり言えませんが、ネストしたクラスプロパティの展開の為に、ExpandableObjectConverter を量産するのを抑えるのに有効な方法らしいです。

« 構造体/クラスプロパティを展開可能にする(3) | トップページ | Win32APIで発生したエラー処理(GetLastError 処理相当の処理) »

C#研究」カテゴリの記事

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック


この記事へのトラックバック一覧です: 構造体/クラスプロパティを展開可能にする(4)[最終回]:

« 構造体/クラスプロパティを展開可能にする(3) | トップページ | Win32APIで発生したエラー処理(GetLastError 処理相当の処理) »