none
PropertyGridコントロールで、カテゴリ文字列を自由に変更したい。 RRS feed

  • 質問

  • はじめまして、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メソッドが、
    指定クラスのインスタンスを最初に生成した時点でしか呼ばれないため、
    途中で別の文字列へ変更することができない状況になってしまいます。

    カテゴリ名称の文字列を変更する方法をご存知の方がおられましたら、
    ご教示いただきたく、宜しくお願い致します。

    2009年2月18日 10:48

回答

  • 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
    2009年2月21日 7:26

すべての返信

  • CategoryAttributeクラスには
    ローカリゼーションに独自のロジックを採用する場合は、Categoryプロパティをオーバーライドできます。
    とありますので、こちらで期待する動作をしなかった場合には、PropertyGrid側で何かしらする必要が?
    2009年2月18日 12:13
  • PropertyGridコントロールのカテゴリの表示文字列はCategoryAttributeクラスのCategoryプロパティにアクセスします。
    CategoryAttributeクラスのCategoryプロパティは、初回のみGetLocalizedStringメソッドを呼び出して、その値をキャッシュするロジックになっています。

    そのほか、PropertyGridコントロール側にもキャッシュする仕組みがあるようですね。
    そのあたりの挙動まで深くは追いかけきれませんでした。
    表示した後に動的に変えるのは難しいかもしれませんね。


    # 昨晩書いたつもりだったのに書けてなかった。orz


    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    2009年2月19日 15:23
    モデレータ
  • 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
    2009年2月21日 7:26
  • 佐祐理さん、Azuleanさん、gekkaさん、
    ご回答ありがとうございます。
    また、返信が遅くなってしまいまして、すいません。

    gekkaさんに教えていただきました方法で、PropertyGridをRefresh
    した際に、GetLocalizedStringメソッドが呼び出されることで、
    カテゴリ文字列を自由に変更することができるようになりました。

    ご教示、ありがとうございました。

    2009年2月23日 3:05
  • こんにちは。中川俊輔 です。

    佐祐理さん、Azuleanさん、gekkaさん回答ありがとうございます。

    Ken1_Wさん、はじめまして。フォーラムのご利用ありがとうございます。
    有用な情報と思われたため、gekkaさんの回答へ回答マークをつけさせていただきました。

    今後ともフォーラムをよろしくお願いします。
    それでは!
    マイクロソフト株式会社 フォーラム オペレータ 中川 俊輔
    2009年2月26日 6:15