し上を行く PropertyGrid (未使用ツールバー・ボタン消去)を表示したい,の巻
2004/11/25 新規

PropertyGrid は、使い方を理解すればするほど 便利なGUI を提供してくれるクラスです。
・・・というのを、少し上を行く PropertyGrid (フォルダ選択)を表示したい,の巻のときに書きました。 もちろん今でもそう思っています。

カテゴリ別/アルファベット順でソートしてくれたり、設定項目のヘルプ表示欄があったり。 int などの数値型を対象にすれば入力値のチェックをしてくれるし、Enum 型だったらListBox を勝手に用意してくれるし。 何かと便利。

ただ。 使っているうちに、ふと気づきます。 PropertyGrid には独自のツールバーが付いていて、そこにはソート機能を行使するための「カテゴリ別」ボタンと「アルファベット順」ボタンがあります。 問題は、その横に「プロパティ ページ」ボタンという、用途不明なものが付いているコト。

いつ使って良いのか分からず、しかも使用不可(グレー表示)のまま。 ちなみに、VisualStudio .net 2003 でもこのボタンは使われていないっぽく。 使いもしないのに堂々と居座られては、とってもジャマです。

PropertyGrid には「ツールバー表示/非表示」を設定できるプロパティは提供されていますが、ツールバー自体はアクセスできません。 逆に言えば、それに触れてしまえば問題解決の糸口になります。

 

何かクラスを作成するときは、フィールドはprivate 属性で隠蔽し、public 属性のアクセサ(get/set)を用意しておくのが常套手段です。 PropertyGrid も例に漏れず、ツールバー自体はprivate で保持されており、そしてアクセサがないためにそれを外から利用することができません。

「private なモノを外から利用できるわけがない」というのは、当たり前のことです。 ですが、リフレクションという機能を用いることで、公開属性や静的/動的や変数/関数に関わらず参照することができてしまいます。 ただし、参照するためには実際に定義されている名前を知っていることが絶対条件となります。

目的の変数名を知るためには、いちばん手っ取り早いのがVisualStudio .net を利用することです。 適当な場所でブレーク・ポイントを張って停止させ、対象インスタンスをウォッチ機能で覗くだけです。 このウォッチ機能も恐らくリフレクションを使っているため、公開属性に関係なくフィールドやアクセサの値を見ることができます。
また、VS.net がない場合には、Lutz Roeder's Programming.NETにある「Reflector for .NET」というツールを利用する手もあります。

 

早速PropertyGrid を見てみると、そのまんまtoolbar というprivate フィールドがあるのが分かります。 このフィールド自体はPropertyGrid.GridToolBar クラスで定義されていますが、単にToolBar クラスとしてしまっても大差はありません。 あとはToolBar のButtons プロパティで、ソート用ボタン2個以外を非表示にしてしまえば完了となります。

以下に、実際のコード例を載せます。 面倒な人は、このメソッドを自分のプログラム中に埋め込んでしまえばそのまま使えます。
ソース1

private void HidePropertyGridToolbarButton(PropertyGrid pg)
{
  const string FieldName="toolbar";
  FieldInfo info;
  ToolBar bar;

  try
  {
    info=pg.GetType().GetField(FieldName,BindingFlags.NonPublic|BindingFlags.Instance);
    bar=(ToolBar)info.GetValue(pg);
  }
  catch
  {
    bar=null;
  }

  if(bar==null)
  {
    return;
  }

  foreach(ToolBarButton b in bar.Buttons)
  {
    b.Visible=b.Enabled&&b.Visible&&(b.Style!=ToolBarButtonStyle.Separator);
  }
}

ただ「ソート用ボタン2個以外を非表示」とするのは、チョイ強引すぎると思うかもしれません。 まぁ、リフレクションを使うこと自体が裏技っぽくて、かなり強引のような気がしますが・・・。

もう一度PropertyGrid をよく見ると、btnViewPropertyPages という名前のprivate フィールドがあることも分かります。 ToolBarButton として定義されているので、これが「プロパティ ページ」ボタンのことだと断言して間違いなさそうです。 ということで、このボタンだけを非表示にしてしまうこともできます。
ソース2

private void HidePropertyGridToolbarButton(PropertyGrid pg)
{
  const string FieldName="btnViewPropertyPages";
  FieldInfo info;
  ToolBarButton button;

  try
  {
    info=pg.GetType().GetField(FieldName,BindingFlags.NonPublic|BindingFlags.Instance);
    button=(ToolBarButton)info.GetValue(pg);
  }
  catch
  {
    button=null;
  }

  if(button!=null)
  {
    button.Visible=false;
  }
}

ただし、この場合にはセパレータの表示までは消えません。


戻る