トップ回答者
PropertyGridコントロールで、カテゴリ文字列を自由に変更したい。

質問
-
はじめまして、Ken1_Wと申します。
標記の件について、質問させてください。開発環境は、Visual Studio 2005、C#で、Windowsアプリケーションを開発しています。
PropertyGridコントロールを使用して、自作クラス(下記、MyClass)のオブジェクト
内にあるプロパティ(下記、Prop1)の取得/設定を行う際に、
カテゴリ名称の文字列をプログラムで自由に変更できるようにしたいのですが、
上手く変更できない場合が発生しています(一部変更できていますが、
条件によって変更できない場合があります)。現在私が確認している、カテゴリ名称の文字列を変更する実装コードを
以下に説明します。
カテゴリ名称を指定可能な、System.Component.CategoryAttributeクラスを
継承したクラス(MyCategory)を新規に作成し、GetLocalizedStringメソッドを
オーバーライドして、ローカライズされた文字列を変更することで、
カテゴリ文字列を変更するようにしています。<実装コード>
// 自作クラス
class MyClass
{
// 対象のプロパティ
[LocalizedCategoryAttribute("Prop1")]
public property int Prop1
{
:
}
}// CategoryAttributeクラスを継承したクラス
class MyCategory : CategoryAttribute
{
protected override string GetLocalizedString(string sValue)
{
// 条件に応じて、カテゴリ文字列を切り換えたい。
if(条件)
{
return "カテゴリ";
}
else
{
return "Category";
}
}
}ただし、上記のコードですと、GetLocalizedStringメソッドが、
指定クラスのインスタンスを最初に生成した時点でしか呼ばれないため、
途中で別の文字列へ変更することができない状況になってしまいます。カテゴリ名称の文字列を変更する方法をご存知の方がおられましたら、
ご教示いただきたく、宜しくお願い致します。
回答
-
PropertyGridのデフォルトのプロパティ表示では更新できないようですが、プロパティ表示の仕方をカスタマイズしてやれば更新可能です。
PropertyGridにPropertyTabを追加して選択。
PropertyGridをRefreshすると、そのPropertyTabにプロパティの表示の仕方に関する情報が要求されるので、属性を更新した情報を返してやります。
属性の変更はPropertyDescriptorに隠されていてできないので、PropertyDescriptorを継承したクラスで変更します。
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Reflection; 5 using System.Windows.Forms; 6 using System.Windows.Forms.Design; 7 using System.Windows.Forms.PropertyGridInternal; 8 9 namespace CustumPropertyGrid 10 { 11 /// <summary>プロパティの属性の再読み込みを可能にするPropertyTab</summary> 12 public class AttributeReloadPropertyGridTab : PropertyTab 13 { 14 /// <summary></summary><remarks>Bitmapが定義されていないとPropertyGridに表示されないらしい</remarks> 15 public override System.Drawing.Bitmap Bitmap 16 { 17 get {return new System.Drawing.Bitmap(16 , 16);} 18 } 19 20 private PropertiesTab defatultTab = new PropertiesTab(); 21 22 public override PropertyDescriptorCollection GetProperties(object component , Attribute[] attributes) 23 { 24 PropertyDescriptorCollection descriptors = new PropertyDescriptorCollection(null , false); 25 PropertyDescriptorCollection defaultDescriptors = defatultTab.GetProperties(component); 26 27 foreach (PropertyDescriptor descriptor in defaultDescriptors) 28 { 29 // 属性を更新したPropertyDescriptorを作る 30 descriptors.Add(new AttributeReloadPropertyDescripter(descriptor)); 31 } 32 33 return descriptors; 34 } 35 36 /// <summary>あとで入れ替えるので適当な名前</summary> 37 public override string TabName 38 { 39 get { return DefaultTabName; } 40 } 41 private static string DefaultTabName 42 { 43 get { return "#DumyTabName"; } 44 } 45 46 /// <summary>属性を更新するためにPropertyDescriptorをラップ</summary> 47 private class AttributeReloadPropertyDescripter : PropertyDescriptor 48 { 49 public AttributeReloadPropertyDescripter(PropertyDescriptor pd) : base(pd) 50 { 51 innerDescriptor = pd; 52 UpdateAttribute(); 53 } 54 55 /// <summary>最新の属性に入れ替える</summary> 56 private void UpdateAttribute() 57 { 58 PropertyInfo pi = this.ComponentType.GetProperty(this.Name , BindingFlags.Instance | BindingFlags.Public); 59 if (pi != null) 60 { 61 List<Type> list = new List<Type>(); 62 foreach (Attribute atri in this.AttributeArray) 63 { 64 list.Add(atri.GetType()); 65 } 66 foreach (Attribute atri in System.Attribute.GetCustomAttributes(pi , true)) 67 { 68 int index = list.IndexOf(atri.GetType()); 69 if (index >= 0) 70 { 71 this.AttributeArray[index] = atri; 72 } 73 } 74 } 75 } 76 77 #region "PropertyDescriptorのabstract" 78 private PropertyDescriptor innerDescriptor; 79 80 public override bool CanResetValue(object component) 81 { 82 return innerDescriptor.CanResetValue(component); 83 } 84 85 public override Type ComponentType 86 { 87 get { return innerDescriptor.ComponentType; } 88 } 89 90 public override object GetValue(object component) 91 { 92 return innerDescriptor.GetValue(component); 93 } 94 95 public override bool IsReadOnly 96 { 97 get { return innerDescriptor.IsReadOnly; } 98 } 99 100 public override Type PropertyType 101 { 102 get { return innerDescriptor.PropertyType; } 103 } 104 105 public override void ResetValue(object component) 106 { 107 innerDescriptor.ResetValue(component); 108 } 109 110 public override void SetValue(object component , object value) 111 { 112 innerDescriptor.SetValue(component , value); 113 } 114 115 public override bool ShouldSerializeValue(object component) 116 { 117 return innerDescriptor.ShouldSerializeValue(component); 118 } 119 #endregion 120 } 121 122 /// <summary>PropertyTabをPropertyGridに追加してデフォルトのPropertyTabを消します</summary> 123 public static void SetAttributeReloadPropertyGridTab(PropertyGrid grid) 124 { 125 foreach (PropertiesTab tab in grid.PropertyTabs) 126 { 127 if (tab.GetType() == typeof(AttributeReloadPropertyGridTab)) 128 { 129 return; 130 } 131 } 132 133 grid.PropertyTabs.AddTabType(typeof(AttributeReloadPropertyGridTab), PropertyTabScope.Global); 134 135 string propTabName = grid.PropertyTabs[0].TabName; 136 string custumTabName = AttributeReloadPropertyGridTab.DefaultTabName; 137 ToolStripItem propButton = null; 138 ToolStripItem custumButton = null; 139 140 ToolStrip strip = GetToolStrip(grid ); 141 if (strip != null) 142 { 143 foreach (ToolStripItem item in strip.Items) 144 { 145 if (item.Text == propTabName) 146 { 147 propButton = item; 148 } 149 else if (item.Text == custumTabName) 150 { 151 custumButton = item; 152 } 153 } 154 } 155 156 if (propButton != null && custumButton != null) 157 { 158 strip.Items.Remove(propButton); 159 custumButton.Image = propButton.Image; 160 custumButton.Text = propButton.Text; 161 custumButton.ToolTipText = propButton.ToolTipText; 162 custumButton.PerformClick(); 163 } 164 else 165 { 166 throw new ApplicationException("PropertyGridの構成が変更されているようです"); 167 } 168 } 169 170 /// <summary>Control内のToolStripを探します</summary> 171 private static ToolStrip GetToolStrip(Control ctl) 172 { 173 ToolStrip strip = ctl as ToolStrip; 174 if(strip == null) 175 { 176 foreach(Control child in ctl.Controls) 177 { 178 strip = GetToolStrip(child); 179 if(strip != null) 180 { 181 break; 182 } 183 } 184 } 185 186 return strip; 187 } 188 } 189 }
- 回答としてマーク sk7474 2009年2月26日 5:58
すべての返信
-
CategoryAttributeクラスには
ローカリゼーションに独自のロジックを採用する場合は、Categoryプロパティをオーバーライドできます。
とありますので、こちらで期待する動作をしなかった場合には、PropertyGrid側で何かしらする必要が? -
PropertyGridコントロールのカテゴリの表示文字列はCategoryAttributeクラスのCategoryプロパティにアクセスします。
CategoryAttributeクラスのCategoryプロパティは、初回のみGetLocalizedStringメソッドを呼び出して、その値をキャッシュするロジックになっています。
そのほか、PropertyGridコントロール側にもキャッシュする仕組みがあるようですね。
そのあたりの挙動まで深くは追いかけきれませんでした。
表示した後に動的に変えるのは難しいかもしれませんね。
# 昨晩書いたつもりだったのに書けてなかった。orz
解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。 -
PropertyGridのデフォルトのプロパティ表示では更新できないようですが、プロパティ表示の仕方をカスタマイズしてやれば更新可能です。
PropertyGridにPropertyTabを追加して選択。
PropertyGridをRefreshすると、そのPropertyTabにプロパティの表示の仕方に関する情報が要求されるので、属性を更新した情報を返してやります。
属性の変更はPropertyDescriptorに隠されていてできないので、PropertyDescriptorを継承したクラスで変更します。
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Reflection; 5 using System.Windows.Forms; 6 using System.Windows.Forms.Design; 7 using System.Windows.Forms.PropertyGridInternal; 8 9 namespace CustumPropertyGrid 10 { 11 /// <summary>プロパティの属性の再読み込みを可能にするPropertyTab</summary> 12 public class AttributeReloadPropertyGridTab : PropertyTab 13 { 14 /// <summary></summary><remarks>Bitmapが定義されていないとPropertyGridに表示されないらしい</remarks> 15 public override System.Drawing.Bitmap Bitmap 16 { 17 get {return new System.Drawing.Bitmap(16 , 16);} 18 } 19 20 private PropertiesTab defatultTab = new PropertiesTab(); 21 22 public override PropertyDescriptorCollection GetProperties(object component , Attribute[] attributes) 23 { 24 PropertyDescriptorCollection descriptors = new PropertyDescriptorCollection(null , false); 25 PropertyDescriptorCollection defaultDescriptors = defatultTab.GetProperties(component); 26 27 foreach (PropertyDescriptor descriptor in defaultDescriptors) 28 { 29 // 属性を更新したPropertyDescriptorを作る 30 descriptors.Add(new AttributeReloadPropertyDescripter(descriptor)); 31 } 32 33 return descriptors; 34 } 35 36 /// <summary>あとで入れ替えるので適当な名前</summary> 37 public override string TabName 38 { 39 get { return DefaultTabName; } 40 } 41 private static string DefaultTabName 42 { 43 get { return "#DumyTabName"; } 44 } 45 46 /// <summary>属性を更新するためにPropertyDescriptorをラップ</summary> 47 private class AttributeReloadPropertyDescripter : PropertyDescriptor 48 { 49 public AttributeReloadPropertyDescripter(PropertyDescriptor pd) : base(pd) 50 { 51 innerDescriptor = pd; 52 UpdateAttribute(); 53 } 54 55 /// <summary>最新の属性に入れ替える</summary> 56 private void UpdateAttribute() 57 { 58 PropertyInfo pi = this.ComponentType.GetProperty(this.Name , BindingFlags.Instance | BindingFlags.Public); 59 if (pi != null) 60 { 61 List<Type> list = new List<Type>(); 62 foreach (Attribute atri in this.AttributeArray) 63 { 64 list.Add(atri.GetType()); 65 } 66 foreach (Attribute atri in System.Attribute.GetCustomAttributes(pi , true)) 67 { 68 int index = list.IndexOf(atri.GetType()); 69 if (index >= 0) 70 { 71 this.AttributeArray[index] = atri; 72 } 73 } 74 } 75 } 76 77 #region "PropertyDescriptorのabstract" 78 private PropertyDescriptor innerDescriptor; 79 80 public override bool CanResetValue(object component) 81 { 82 return innerDescriptor.CanResetValue(component); 83 } 84 85 public override Type ComponentType 86 { 87 get { return innerDescriptor.ComponentType; } 88 } 89 90 public override object GetValue(object component) 91 { 92 return innerDescriptor.GetValue(component); 93 } 94 95 public override bool IsReadOnly 96 { 97 get { return innerDescriptor.IsReadOnly; } 98 } 99 100 public override Type PropertyType 101 { 102 get { return innerDescriptor.PropertyType; } 103 } 104 105 public override void ResetValue(object component) 106 { 107 innerDescriptor.ResetValue(component); 108 } 109 110 public override void SetValue(object component , object value) 111 { 112 innerDescriptor.SetValue(component , value); 113 } 114 115 public override bool ShouldSerializeValue(object component) 116 { 117 return innerDescriptor.ShouldSerializeValue(component); 118 } 119 #endregion 120 } 121 122 /// <summary>PropertyTabをPropertyGridに追加してデフォルトのPropertyTabを消します</summary> 123 public static void SetAttributeReloadPropertyGridTab(PropertyGrid grid) 124 { 125 foreach (PropertiesTab tab in grid.PropertyTabs) 126 { 127 if (tab.GetType() == typeof(AttributeReloadPropertyGridTab)) 128 { 129 return; 130 } 131 } 132 133 grid.PropertyTabs.AddTabType(typeof(AttributeReloadPropertyGridTab), PropertyTabScope.Global); 134 135 string propTabName = grid.PropertyTabs[0].TabName; 136 string custumTabName = AttributeReloadPropertyGridTab.DefaultTabName; 137 ToolStripItem propButton = null; 138 ToolStripItem custumButton = null; 139 140 ToolStrip strip = GetToolStrip(grid ); 141 if (strip != null) 142 { 143 foreach (ToolStripItem item in strip.Items) 144 { 145 if (item.Text == propTabName) 146 { 147 propButton = item; 148 } 149 else if (item.Text == custumTabName) 150 { 151 custumButton = item; 152 } 153 } 154 } 155 156 if (propButton != null && custumButton != null) 157 { 158 strip.Items.Remove(propButton); 159 custumButton.Image = propButton.Image; 160 custumButton.Text = propButton.Text; 161 custumButton.ToolTipText = propButton.ToolTipText; 162 custumButton.PerformClick(); 163 } 164 else 165 { 166 throw new ApplicationException("PropertyGridの構成が変更されているようです"); 167 } 168 } 169 170 /// <summary>Control内のToolStripを探します</summary> 171 private static ToolStrip GetToolStrip(Control ctl) 172 { 173 ToolStrip strip = ctl as ToolStrip; 174 if(strip == null) 175 { 176 foreach(Control child in ctl.Controls) 177 { 178 strip = GetToolStrip(child); 179 if(strip != null) 180 { 181 break; 182 } 183 } 184 } 185 186 return strip; 187 } 188 } 189 }
- 回答としてマーク sk7474 2009年2月26日 5:58