C#でツールを作る その16 -PropertyGridを使ってみる-
超便利ですね。PropertyGridまじパネェっす!
C#を使い始めて以来、多分最も感動したのではないかと。
昨日までC#のプロパティとは、
「get、setメソッドを多少簡単に記述できるようにしただけ」
と勝手に思い込んでいたのですが、
PropertyGridを使ってみて初めてその便利さを実感しました。
単にクラス間のメンバアクセスを簡略化できるだけでなく、
PropertyGridと組み合わせることで、アプリケーションからのアクセスも容易になるわけですね!
ということで、今回はPropertyGridな話です。
基本的な使い方はこちらを参照してください。
- PropertyGridコントロールの使い方 - .NET Tips (VB.NET,C#...)
- http://www.microsoft.com/japan/msdn/net/general/usingpropgrid.aspx
- http://www.microsoft.com/japan/msdn/net/general/vsnetpropbrow.aspx
上の図のようなfloatで設定可能な色クラスColorFを作ってみます。
ColorF構造体
- 0.0f〜1.0fの範囲で色の成分を扱うための構造体
public struct ColorF { public float r, g, b, a; }
ApplicationSettingsクラス
- PropertyGridからアクセスするクラス。
- メンバのcolorをプロパティとして公開。
public class ApplicationSettings { private int hoge; private ColorF color; public ColorF Color { get{ return color; } set{ color = value; } } }
Form1クラス
- PropertyGridとApplicationSettingsを関連付ける。
public partial class Form1 : Form { private ApplicationSettings app_settings = new ApplicationSettings(); public Form1() { InitializeComponent(); propertyGrid1.SelectedObject = app_settings; } }
実行結果
Colorのみ表示されますが、メンバのr, g, b, aが表示されません。
ちなみに、Form1を関連付けるとFormのプロパティが表示されて、値を変えることが出来ます。
propertyGrid1.SelectedObject = this;
関連付けるだけで、アプリケーションからメンバの修正が行えます。すごいです。
話を元に戻して、さらにカスタマイズしていきます。
public class ApplicationSettings { private ColorF color; private Point pos; public ColorF Color { get{ return color; } set{ color = value; } } public Point Pos { get{ return pos; } set{ pos = value; } } }
C#の標準の型はデフォルトでメンバが表示されますが、自分で作ったクラスは表示されません。
メンバを表示させる
- TypeConverterを継承したクラスを用意して、ColorFのメンバを文字列に変換する
- ConvertToをオーバーライドし、変換先が文字列かどうか確認して変換
- ColorFに設定
using System.ComponentModel; class ColorFTypeConverter : TypeConverter { public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(String)) { ColorF c = (ColorF)value; return c.r + " " + c.g + " " + c.b + " " + c.a; } return base.ConvertTo(context, culture, value, destinationType); } } [TypeConverter( typeof( ColorFTypeConverter ) )] public struct ColorF { /*...*/ }
ReadOnlyになっているので、値を変更できるように、オーバーライドを追加します。
- CanConvertFromのオーバーライド
- 値の変更を有効にする
- ConvertFromのオーバーライド
- 文字列からColorFに変換
- 書式が不正など、変換できない場合は例外が発生する。
class ColorFTypeConverter : TypeConverter { /*...*/ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(String)) return true; return base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { if (value is String) { String[] v = ((String)value).Split(new char[] { ' ' }); return new ColorF(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]), float.Parse(v[3])); } return base.ConvertFrom(context, culture, value); } }
変換不能な場合は元の値に戻すようにしてみました。適当です。
class ColorFTypeConverter : TypeConverter { private ColorF current; public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(String)) return true; return base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { if (value is String) { try { String[] v = ((String)value).Split(new char[] { ' ' }); return new ColorF(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]), float.Parse(v[3])); } catch { } //変換できない場合は以前の色に戻す return current; } return base.ConvertFrom(context, culture, value); } public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(String)) { //現在の色をとっておく current = (ColorF)value; ColorF c = (ColorF)value; return c.r + " " + c.g + " " + c.b + " " + c.a; } return base.ConvertTo(context, culture, value, destinationType); } }
現在の色を表示
- UITypeEditorを継承したクラスを用意して、専用の描画領域を塗りつぶす
- GetPaintValueSupportedのオーバーライド
- 描画を有効にする
- PaintValueのオーバーライド
- 塗りつぶし
- ColorFに設定
using System.Drawing; using System.Drawing.Design; using System.Windows.Forms; class ColorFTypeEditor : UITypeEditor { //描画を有効にする public override bool GetPaintValueSupported(ITypeDescriptorContext context) { return true; } //塗りつぶし public override void PaintValue(PaintValueEventArgs e) { Color color = ( ( ColorF )e.Value ).ToColor(); e.Graphics.FillRectangle(new SolidBrush(color), e.Bounds); } } [TypeConverter( typeof( ColorFTypeConverter ) )] [Editor( typeof( ColorFTypeEditor ), typeof( UITypeEditor ) )] public struct ColorF { /*...*/ }
色選択ダイアログから色を設定
- GetEditStyleのオーバーライド
- モーダルでダイアログを表示
- EditValueのオーバーライド
- 色選択ダイアログの表示
class ColorFTypeEditor : UITypeEditor { /*...*/ public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { return UITypeEditorEditStyle.Modal; } public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { ColorDialog d = new ColorDialog(); d.ShowDialog(); return new ColorF( d.Color ); } }
Description
- メンバの説明
- プロパティにDescriptionを指定
public class ApplicationSettings { private ColorF color; private Point pos; [Description( "r, g, b, aを0.0〜1.0で設定します" )] public ColorF Color { get{ return color; } set{ color = value; } } public Point Pos { get{ return pos; } set{ pos = value; } } }
Category
- プロパティの分類
public class ApplicationSettings { private ColorF color; private Point pos; [Category( "Hoge" )] [Description( "r, g, b, aを0.0〜1.0で設定します" )] public ColorF Color { get{ return color; } set{ color = value; } } [Category( "Piyo" )] public Point Pos { get{ return pos; } set{ pos = value; } } }