プロパティグリッドのコマンドを使う

## プロパティグリッドとは

- `System.Windows.Forms.PropertyGrid`クラス
  - 指定したオブジェクトをグリッド表示・編集できる
- 参考 : https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.propertygrid

![PropertyGrid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298163/b38f8567-74fa-080d-0aa5-395fa6cc6429.png)

### プロパティグリッドのコマンドとは

- `PropertyGrid`の下部に表示されるペイン内のアクティブリンクのこと
- `System.ComponentModel.Design.DesignerVerb`クラスで表される
  - 任意の処理を実行可能

### プロパティグリッドのコマンドを使うには

- 対象クラス（`PropertyGrid`に表示するクラス）に`IComponent`インターフェイスを実装すればよい


## 実装手順

1. [処理クラスを準備](#1-処理クラスを準備)
2. [対象クラスにインターフェイスを実装](#2-対象クラスにインターフェイスを実装)
3. [プロパティグリッドにオブジェクトを設定](#3-プロパティグリッドにオブジェクトを設定)

### 1. 処理クラスを準備

- 下記インターフェイスを実装した処理用のクラスを用意する
  - `ISite`
  - `IMenuCommandService`
- 外部から`DesignerVerb`を指定できる汎用クラスとして定義
  - 再利用可能

```C#
class DesignerVerbSite : ISite, IMenuCommandService
{
  private readonly DesignerVerb[] verbs;

  public DesignerVerbSite(params DesignerVerb[] verbs)
  {
    this.verbs = verbs ?? throw new ArgumentNullException(nameof(verbs));
  }

  #region ISite
  /// <inheritdoc/>
  IComponent ISite.Component => throw new NotImplementedException();

  /// <inheritdoc/>
  IContainer ISite.Container => null;

  /// <inheritdoc/>
  bool ISite.DesignMode => true;

  /// <inheritdoc/>
  string ISite.Name { get; set; }
  #endregion

  #region IMenuCommandService
  /// <inheritdoc/>
  DesignerVerbCollection IMenuCommandService.Verbs => new DesignerVerbCollection(this.verbs);

  /// <inheritdoc/>
  void IMenuCommandService.AddCommand(MenuCommand command) => throw new NotImplementedException();

  /// <inheritdoc/>
  void IMenuCommandService.AddVerb(DesignerVerb verb) => throw new NotImplementedException();

  /// <inheritdoc/>
  MenuCommand IMenuCommandService.FindCommand(CommandID commandID) => throw new NotImplementedException();

  /// <inheritdoc/>
  object IServiceProvider.GetService(Type serviceType) => (serviceType == typeof(IMenuCommandService)) ? this : null;

  /// <inheritdoc/>
  bool IMenuCommandService.GlobalInvoke(CommandID commandID) => throw new NotImplementedException();

  /// <inheritdoc/>
  void IMenuCommandService.RemoveCommand(MenuCommand command) => throw new NotImplementedException();

  /// <inheritdoc/>
  void IMenuCommandService.RemoveVerb(DesignerVerb verb) => throw new NotImplementedException();

  /// <inheritdoc/>
  void IMenuCommandService.ShowContextMenu(CommandID menuID, int x, int y) => throw new NotImplementedException();
  #endregion
}
```

### 2. 対象クラスにインターフェイスを実装

- `PropertyGrid`に表示する対象クラスに、`IComponent`インターフェイスを実装

```C#
class Target : IComponent
{
  #region IComponent
  /// <inheritdoc/>
  ISite IComponent.Site { get; set; }

  /// <inheritdoc/>
  public event EventHandler Disposed;

  /// <inheritdoc/>
  public void Dispose() => this.Disposed?.Invoke(this, EventArgs.Empty);
  #endregion

  public Target()
  {
    (this as IComponent).Site = new DesignerVerbSite(this.CreateVerb1());
  }

  private DesignerVerb CreateVerb1()
  {
    return new DesignerVerb("処理 1", (sender, e) =>
    {
      throw new NotImplementedException(); //TODO  処理内容を実装
    });
  }
}
```

### 3. プロパティグリッドにオブジェクトを設定

```C#
var pg = new PropertyGrid();
pg.SelectedObject = new Target();
```