- 追加された行はこの色です。
- 削除された行はこの色です。
#title(32)
#title(.NETプログラミング研究 第32号)
#navi(.NETプログラミング研究)
#contents
*32 [#k336c4af]
*.NETプログラミング研究 第32号 [#we2fc89d]
**.NET Tips [#n3b218dd]
**PropertyGridコントロールの使い方 [#z10dcb4d]
**コメント [#t3b63913]
#column(注意){{
この記事の最新版は「[[PropertyGridコントロールの使い方>https://dobon.net/vb/dotnet/control/propertygrid.html]]」で公開しています。
}}
***「PropertyGridコントロール」とは? [#m4f5002a]
.NET Frameworkには、PropertyGridコントロールというコンポーネントが標準で用意されています。このPropertyGridコントロールは、Microsoft Visual Studio .NETのプロパティウィンドウと同等の機能を提供します。つまり、オブジェクトのプロパティをリスト表示し、その値をユーザーが変更できるようになっており、さらに、上部には並び方の指定等を行うツールバーが、下部には選択されているプロパティの説明の表示スペース(説明ペイン)が用意されています。
PropertyGridコントロールは、ただオブジェクトを指定するだけで、そのオブジェクトのプロパティを自動的に取得し、リスト表示してくれます。さらに、PropertyGridコントロールでプロパティの値が変更されると、すぐにオブジェクトにその変更が反映されます。例えば、アプリケーションの設定をクラスで行っている場合、PropertyGridコントロールを使えば驚くほど簡単にアプリケーションの設定画面を作成することができるため、今後PropertyGridコントロールを使用したアプリケーションが増えることは間違いないでしょう。(少なくとも、この記事を読んでいただいた方はきっと使いたくなるでしょう。)
補足:PropertyGridコントロールの使い方に関しては、MSDNにある「.NET Framework の PropertyGrid コントロールの高度な活用」や「Visual Studio .NET プロパティ ブラウザによるコンポーネントの本格的な RAD 化」などで詳しく説明されています。この記事の内容がこれらの記事とかなりの部分でかぶっていることをご了承ください。
-[[.NET Framework の PropertyGrid コントロールの高度な活用>http://www.microsoft.com/japan/msdn/net/general/usingpropgrid.asp]]
-[[Visual Studio .NET プロパティ ブラウザによるコンポーネントの本格的な RAD 化>http://www.microsoft.com/japan/msdn/net/general/vsnetpropbrow.asp]]
***とりあえず使ってみよう [#oe4c6c66]
PropertyGridクラスは、System.Windows.Forms名前空間、System.Windows.Forms.dllアセンブリにあるため、特に特別なことをすることなく、普通のコントロールと同じようにPropertyGridコントロールを使用することができます。ところがVisual Studio .NETでは、デフォルトで「ツールボックス」の「Windowsフォーム」タブ(あるいは「コンポーネント」タブ)にPropertyGridコントロールが用意されていないため、フォームデザイナを使ってPropertyGridコントロールを配置できません。(PropertyGridコントロールがあまり知られていないのはこのためでしょう。)
「Windowsフォーム」タブにPropertyGridコントロールを追加するには、「ツールボックス」の「Windowsフォーム」タブを右クリックし、表示されるメニューから「アイテムの追加と削除」を選択します。表示される「ツールボックスのカスタマイズ」ダイアログの「.NET Frameworkコンポーネント」タブ内のリストから、「PropertyGrid」という名前の項目を探し、左端のチェックボックスにチェックを入れ、「OK」をクリックしてください。これで「Windowsフォーム」タブに「PropertyGrid」が追加されたはずです。
それでは早速PropertyGridコントロールを使ってみましょう。ここでは、フォーム"Form1"にPropertyGridコントロール"PropertyGrid1"を配置したとします。
まずはPropertyGridコントロールのすばらしさを実感していただくために、次のような一行をForm1のLoadイベントハンドラに記述してみてください。
#code(vbnet){{
PropertyGrid1.SelectedObject = Me
}}
#code(csharp){{
PropertyGrid1.SelectedObject = this;
}}
ビルドし実行すると、PropertyGridコントロールにForm1のプロパティがリスト表示されることが分かります。さらに面白いことに、PropertyGridコントロールでプロパティの値を変更すると、その変更がすぐにForm1に反映されます。例えば、"ControlBox"を"False"にすると、コントロールボックスが消えますし、"BackColor"で背景色を変更できます。
PropertyGrid.SelectedObjectプロパティを設定しただけで、これだけのことができてしまいます。
***PropertyGridコントロールの基本的なプロパティ [#d3f885a7]
PropertyGridコントロールで使用される基本的なプロパティ、イベントについてごく簡単に説明しておきます。
PropertyGridコントロールのツールバーを表示しないようにするには、ToolbarVisibleプロパティをFalseにします。LargeButtonsプロパティをTrueにすることにより、ツールバーのボタンの大きさを倍(32X32)にすることができます(アイコンが荒くなり、見た目はよくありません)。説明ペインを表示しないようにするには、HelpVisibleプロパティをFalseにします。
プロパティの並べ方は、PropertySortプロパティで指定します。アルファベット順(Alphabetical)、項目別(Categorized)、項目別でアルファベット順(CategorizedAlphabetical)、並び替えをしない(NoSort)を指定できます。
選択されている項目は、SelectedGridItemプロパティにより取得できます。また、選択されている項目が変更された時、SelectedGridItemChangedイベントが発生します。プロパティの値が変更された時には、PropertyValueChangedイベントが発生します。
***自作したクラスに使ってみよう [#r24d76c4]
次に、クラスを自作し、そのプロパティをPropertyGridコントロールに表示されてみましょう。
まずは、次のようなクラスを作成します。
#code(vbnet){{
Public Class TestClass
Public Enum TestEnum
One
Two
Tree
End Enum
Private _integerValue As Integer = 0
Private _stringValue As String = "こんにちは"
Private _booleanValue As Boolean = False
Private _enumValue As TestEnum = TestEnum.One
Private _colorValue As System.Drawing.Color = _
System.Drawing.Color.Red
Public Property IntegerValue() As Integer
Get
Return _integerValue
End Get
Set(ByVal Value As Integer)
_integerValue = Value
End Set
End Property
Public Property StringValue() As String
Get
Return _stringValue
End Get
Set(ByVal Value As String)
_stringValue = Value
End Set
End Property
Public Property BooleanValue() As Boolean
Get
Return _booleanValue
End Get
Set(ByVal Value As Boolean)
_booleanValue = Value
End Set
End Property
Public Property EnumValue() As TestEnum
Get
Return _enumValue
End Get
Set(ByVal Value As TestEnum)
_enumValue = Value
End Set
End Property
Public Property ColorValue() As System.Drawing.Color
Get
Return _colorValue
End Get
Set(ByVal Value As System.Drawing.Color)
_colorValue = Value
End Set
End Property
End Class
}}
#code(csharp){{
public class TestClass
{
public enum TestEnum
{
One,
Two,
Tree
}
private int _integerValue = 0;
private string _stringValue = "こんにちは";
private bool _booleanValue = false;
private TestEnum _enumValue = TestEnum.One;
private System.Drawing.Color _colorValue =
System.Drawing.Color.Red;
public int IntegerValue
{
get {return _integerValue;}
set {_integerValue = value;}
}
public string StringValue
{
get {return _stringValue;}
set {_stringValue = value;}
}
public bool BooleanValue
{
get {return _booleanValue;}
set {_booleanValue = value;}
}
public TestEnum EnumValue
{
get {return _enumValue;}
set {_enumValue = value;}
}
public System.Drawing.Color ColorValue
{
get {return _colorValue;}
set {_colorValue = value;}
}
}
}}
このTestClassオブジェクトを次のようにしてPropertyGrid.SelectedObjectプロパティに設定してみます。PropertyGridコントロールにTestClassのパブリックプロパティがリスト表示されるでしょう。
#code(vbnet){{
Dim cls As New TestClass
PropertyGrid1.SelectedObject = cls
}}
#code(csharp){{
TestClass cls = new TestClass();
PropertyGrid1.SelectedObject = cls;
}}
何もしなくてもPropertyGridコントロールでは、プロパティの型に応じて、適切な値の編集方法が提供されます。例えば、bool(Boolean)型のBooleanValueプロパティは、TrueかFalseかを選択するためにコンボボックスが使用され、同様に、列挙型のEnumValueプロパティもコンボボックスが使用されます。
それ以外では、Color型のプロパティではリストにより色の選択が、Font型のプロパティではフォント選択ダイアログを使用しての選択が、Image型のプロパティでは「ファイルを開く」ダイアログによる画像ファイルの選択と、画像の縮小表示ができるようになっています。
***プロパティの表示方法を変更する [#p77a5e06]
このようにPropertyGridコントロールは実に簡単に使えますので、これだけの知識でもそれなりの事は行えますが、実際に使うとなると、更なる知識が必要となるでしょう。
ここから以下は、特にプロパティの表示方法に関するテクニックを説明していきます。これらのテクニックのほとんどが属性を使ったものであり、System.ComponentModel名前空間にあるクラスを使用しているため、以下のサンプルでは、C#では
using System.ComponentModel;
VB.NETでは
Imports System.ComponentModel
が宣言されているものとします。
***プロパティのデフォルト値を指定する [#v5f2741c]
VS.NETの場合、プロパティがデフォルト値(規定値)でないときに、値が太字で表示されます。上記の"TestClass"の例では、すべての値が太字で表示されます。プロパティがデフォルト値でないときだけ値が太字で表示するには、DefaultValueAttributeを使用して、プロパティのデフォルト値を決めておきます。
次の例では、IntegerValueプロパティのデフォルト値を0にしています。
#code(vbnet){{
<DefaultValue(0)> _
Public Property IntegerValue() As Integer
Get
Return _integerValue
End Get
Set(ByVal Value As Integer)
_integerValue = Value
End Set
End Property
}}
#code(csharp){{
[DefaultValue(0)]
public int IntegerValue
{
get {return _integerValue;}
set {_integerValue = value;}
}
}}
ところで、Color型、Font型、Size型などのデフォルト値は上記の方法では指定できません。これらのデフォルトを指定するには、"ShouldSerializeMyProperty"メソッドを使用します。クラスに"ShouldSerializeMyProperty"という名前でbool(Boolean)を返すメソッドを作り、デフォルト値ならFalse、そうでなければTrueを返すようにします。(詳しくはヘルプの「PropertyDescriptor.ShouldSerializeValue メソッド」等をご覧ください。)
-[[PropertyDescriptor.ShouldSerializeValue メソッド>http://www.microsoft.com/japan/msdn/library/ja/cpref/html/frlrfsystemcomponentmodelpropertydescriptorclassshouldserializevaluetopic.asp]]
次の例では、ColorValueプロパティのデフォルト値をColor.Redにしています。
#code(vbnet){{
Private _colorValue As System.Drawing.Color = _
System.Drawing.Color.Red
Public Property ColorValue() As System.Drawing.Color
Get
Return _colorValue
End Get
Set(ByVal Value As System.Drawing.Color)
_colorValue = Value
End Set
End Property
Private Function ShouldSerializeColorValue() As Boolean
Return Not ColorValue.Equals(System.Drawing.Color.Red)
End Function
}}
#code(csharp){{
private System.Drawing.Color _colorValue =
System.Drawing.Color.Red;
public System.Drawing.Color ColorValue
{
get {return _colorValue;}
set {_colorValue = value;}
}
private bool ShouldSerializeColorValue()
{
return ColorValue != System.Drawing.Color.Red;
}
}}
***クラスのデフォルトプロパティを指定する [#x2b4780d]
PropertyGridコントロールで一番初めに選択されるプロパティを指定するには、クラスにDefaultPropertyAttributeを追加します。
次の例では、"TestClass"クラスのデフォルトプロパティを"StringValue"プロパティにしています。
#code(vbnet){{
<DefaultProperty("StringValue")> _
Public Class TestClass
'(省略)
End Class
}}
#code(csharp){{
[DefaultProperty("StringValue")]
public class TestClass
{
//(省略)
}
}}
***プロパティの説明を表示する [#ld1f6ff8]
PropertyGridコントロールの説明ペインに、選択されているプロパティの説明を表示するには、DescriptionAttributeを使用します。
次の例では、StringValueプロパティの説明を設定しています。
#code(vbnet){{
<Description("ここにStringValueの説明を書きます。")> _
Public Property StringValue() As String
Get
Return _stringValue
End Get
Set(ByVal Value As String)
_stringValue = Value
End Set
End Property
}}
#code(csharp){{
[Description("ここにStringValueの説明を書きます。")]
public string StringValue
{
get {return _stringValue;}
set {_stringValue = value;}
}
}}
***プロパティの項目を指定する [#h0d542cd]
PropertyGridコントロールではプロパティを項目(カテゴリ)別に表示できます。項目別に表示したとき、プロパティはデフォルトで「その他」に分類されますが、CategoryAttributeにより、プロパティの項目を指定することができます。
次の例では、ColorValueプロパティの項目を"表示"にしています。
#code(vbnet){{
<Category("表示")> _
Public Property ColorValue() As System.Drawing.Color
Get
Return _colorValue
End Get
Set(ByVal Value As System.Drawing.Color)
_colorValue = Value
End Set
End Property
}}
#code(csharp){{
[Category("表示")]
public System.Drawing.Color ColorValue
{
get {return _colorValue;}
set {_colorValue = value;}
}
}}
***プロパティを表示しない [#cc46e292]
PropertyGridコントロールに表示したくないプロパティには、Falseを指定したBrowsableAttributeを追加します。
次の例では、BooleanValueプロパティをPropertyGridコントロールに表示しないようにしています。
#code(vbnet){{
<Browsable(False)> _
Public Property BooleanValue() As Boolean
Get
Return _booleanValue
End Get
Set(ByVal Value As Boolean)
_booleanValue = Value
End Set
End Property
}}
#code(csharp){{
[Browsable(false)]
public bool BooleanValue
{
get {return _booleanValue;}
set {_booleanValue = value;}
}
}}
***プロパティの値が編集できないようにする [#a57e5c9d]
プロパティの値をユーザーが編集できないようにするには、Trueを指定したReadOnlyAttributeを使用します。
次の例では、IntegerValueプロパティをPropertyGridコントロールでユーザーが編集できないようにしています。
#code(vbnet){{
<ReadOnlyAttribute(True)> _
Public Property IntegerValue() As Integer
Get
Return _integerValue
End Get
Set(ByVal Value As Integer)
_integerValue = Value
End Set
End Property
}}
#code(csharp){{
[ReadOnly(true)]
public int IntegerValue
{
get {return _integerValue;}
set {_integerValue = value;}
}
}}
***「ファイルを開く」ダイアログを表示してファイルを選択できるようにする [#cc48f4e9]
右側にボタンを表示し、このボタンをクリックすることにより「ファイルを開く」ダイアログを表示して、プロパティの値を設定できるようにするには、FileNameEditorをエディタに指定したEditorAttributeを使用します。なお、System.Designアセンブリを参照に追加する必要があります。
次の例では、StringValueプロパティにボタンを表示し、「ファイルを開く」ダイアログにより、ファイルを選択できるようにしています。
#code(vbnet){{
<Editor(GetType(System.Windows.Forms.Design.FileNameEditor), _
GetType(System.Drawing.Design.UITypeEditor))> _
Public Property StringValue() As String
Get
Return _stringValue
End Get
Set(ByVal Value As String)
_stringValue = Value
End Set
End Property
}}
#code(csharp){{
[Editor(typeof(System.Windows.Forms.Design.FileNameEditor),
typeof(System.Drawing.Design.UITypeEditor))]
public string StringValue
{
get {return _stringValue;}
set {_stringValue = value;}
}
}}
***プロパティを展開できるようにする [#i7883981]
例えば次のようなSize型のプロパティは、PropertyGridコントロールでは展開してSizeクラスのHeightとWidthプロパティが表示できます。
#code(vbnet){{
Public Class TestClass
Private _size As New Size(10, 10)
Public Property Size() As Size
Get
Return _size
End Get
Set(ByVal Value As Size)
_size = value
End Set
End Property
End Class
}}
#code(csharp){{
public class TestClass
{
private Size _size = new Size(10, 10);
public Size Size
{
get {return _size;}
set {_size = value;}
}
}
}}
しかし次のような自作のクラスの場合は、展開ができず、値を変更することができません。
#code(vbnet){{
Public Class CustomClass
Private _number As Integer = 0
Private _message As String = "hello"
Public Property Number() As Integer
Get
Return _number
End Get
Set(ByVal Value As Integer)
_number = Value
End Set
End Property
Public Property Message() As String
Get
Return _message
End Get
Set(ByVal Value As String)
_message = Value
End Set
End Property
End Class
Public Class TestClass
Private _custom As New CustomClass
Public Property Custom() As CustomClass
Get
Return _custom
End Get
Set(ByVal Value As CustomClass)
_custom = Value
End Set
End Property
End Class
}}
#code(csharp){{
public class CustomClass
{
private int _number = 0;
private string _message = "hello";
public int Number
{
get {return _number;}
set {_number = value;}
}
public string Message
{
get {return _message;}
set {_message = value;}
}
}
public class TestClass
{
private CustomClass _custom = new CustomClass();
public CustomClass Custom
{
get {return _custom;}
set {_custom = value;}
}
}
}}
このような自作のクラス型のプロパティでもPropertyGridコントロールで展開ができるようにするには、TypeConverterAttributeを"CustomClass"クラスに追加し、使用する型コンバータ(TypeConverter)としてExpandableObjectConverterクラスを指定します。具体的には、次のようになります。
#code(vbnet){{
<TypeConverter(GetType(ExpandableObjectConverter))> _
Public Class CustomClass
'(省略)
End Class
}}
#code(csharp){{
[TypeConverter(typeof(ExpandableObjectConverter))]
public class CustomClass
{
//(省略)
}
}}
これで"CustomClass"型のプロパティが展開できるようになりました。しかし、Size型やFont型のプロパティと違い、プロパティそのものの値を表示する部分には、クラス名が表示され、編集できません。ここに表示される文字列を制御し、さらに編集できるようにするには、ExpandableObjectConverterクラスの派生クラスを作成し、CanConvertTo、CanConvertFrom、ConvertTo、ConvertFromメソッドをそれぞれオーバーライドします。(型コンバータにより、オブジェクトを文字列に、また、文字列をオブジェクトに変換する方法を提供します。)
次のような型コンバータクラスを使用することにより、CustomClassのNumberとMessageプロパティがコンマで区切られた文字列として表示されるようになります(編集もできるようになります)。
#code(vbnet){{
Public Class CustomClassConverter
Inherits ExpandableObjectConverter
'コンバータがオブジェクトを指定した型に変換できるか
'(変換できる時はTrueを返す)
'ここでは、CustomClass型のオブジェクトには変換可能とする
Public Overloads Overrides Function CanConvertTo( _
ByVal context As ITypeDescriptorContext, _
ByVal destinationType As Type) As Boolean
If destinationType Is GetType(CustomClass) Then
Return True
End If
Return MyBase.CanConvertTo(context, destinationType)
End Function
'指定した値オブジェクトを、指定した型に変換する
'CustomClass型のオブジェクトをString型に変換する方法を提供する
Public Overloads Overrides Function ConvertTo( _
ByVal context As ITypeDescriptorContext, _
ByVal culture As System.Globalization.CultureInfo, _
ByVal value As Object, _
ByVal destinationType As Type) As Object
If destinationType Is GetType(String) And TypeOf value Is CustomClass Then
Dim cc As CustomClass = CType(value, CustomClass)
Return cc.Number.ToString() + "," + cc.Message
End If
Return MyBase.ConvertTo(context, culture, value, destinationType)
End Function
'コンバータが特定の型のオブジェクトをコンバータの型に変換できるか
'(変換できる時はTrueを返す)
'ここでは、String型のオブジェクトなら変換可能とする
Public Overloads Overrides Function CanConvertFrom( _
ByVal context As ITypeDescriptorContext, _
ByVal sourceType As Type) As Boolean
If sourceType Is GetType(String) Then
Return True
End If
Return MyBase.CanConvertFrom(context, sourceType)
End Function
'指定した値をコンバータの型に変換する
'String型のオブジェクトをCustomClass型に変換する方法を提供する
Public Overloads Overrides Function ConvertFrom( _
ByVal context As ITypeDescriptorContext, _
ByVal culture As System.Globalization.CultureInfo, _
ByVal value As Object) As Object
If TypeOf value Is String Then
Dim ss As String() = value.ToString().Split(New Char() {","c}, 2)
Dim cc As New CustomClass
cc.Number = Integer.Parse(ss(0))
cc.Message = ss(1)
Return cc
End If
Return MyBase.ConvertFrom(context, culture, value)
End Function
End Class
}}
#code(csharp){{
public class CustomClassConverter : ExpandableObjectConverter
{
//コンバータがオブジェクトを指定した型に変換できるか
//(変換できる時はTrueを返す)
//ここでは、CustomClass型のオブジェクトには変換可能とする
public override bool CanConvertTo(
ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(CustomClass))
return true;
return base.CanConvertTo(context, destinationType);
}
//指定した値オブジェクトを、指定した型に変換する
//CustomClass型のオブジェクトをString型に変換する方法を提供する
public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) &&
value is CustomClass)
{
CustomClass cc = (CustomClass) value;
return cc.Number.ToString() + "," + cc.Message;
}
return base.ConvertTo(context, culture, value, destinationType);
}
//コンバータが特定の型のオブジェクトをコンバータの型に変換できるか
//(変換できる時はTrueを返す)
//ここでは、String型のオブジェクトなら変換可能とする
public override bool CanConvertFrom(
ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom (context, sourceType);
}
//指定した値をコンバータの型に変換する
//String型のオブジェクトをCustomClass型に変換する方法を提供する
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value is string)
{
string[] ss = value.ToString().Split(new char[] {','}, 2);
CustomClass cc = new CustomClass();
cc.Number = int.Parse(ss[0]);
cc.Message = ss[1];
return cc;
}
return base.ConvertFrom(context, culture, value);
}
}
}}
使い方は前と同じです。
#code(vbnet){{
<TypeConverter(GetType(CustomClassConverter))> _
Public Class CustomClass
'(省略)
End Class
}}
#code(csharp){{
[TypeConverter(typeof(CustomClassConverter))]
public class CustomClass
{
//(省略)
}
}}
***もっと勉強したい方は... [#f77594a6]
MSDNにある「.NET Framework の PropertyGrid コントロールの高度な活用」及び「Visual Studio .NET プロパティ ブラウザによるコンポーネントの本格的な RAD 化」では、さらに、「簡単なドロップダウンプロパティを提供する方法」、「プロパティのカスタムUIを提供する方法」などが紹介されています。
参考:
-[[.NET Framework の PropertyGrid コントロールの高度な活用>http://www.microsoft.com/japan/msdn/net/general/usingpropgrid.asp]]
-[[Visual Studio .NET プロパティ ブラウザによるコンポーネントの本格的な RAD 化>http://www.microsoft.com/japan/msdn/net/general/vsnetpropbrow.asp]]
**コメント [#f494856a]
#comment
//これより下は編集しないでください
#pageinfo([[:Category/.NET]] [[:Category/ASP.NET]],2010-03-21 (日) 02:08:03,DOBON!,2010-03-21 (日) 02:08:03,DOBON!)
#pageinfo([[:Category/.NET]],2004-05-04 (火) 06:00:00,DOBON!,2010-03-21 (日) 02:08:13,DOBON!)