コレクションエディタの拡張

# はじめに

コレクションエディタは簡単にリスト要素のプロパティを変更することが出来て便利なものですが、カスタマイズしようと思うとなかなか思うようにできません。ここでは次の３つのカス
タマイズについて紹介したいと思います。

- リストアイテムの追加、削除、移動の禁止
- プロパティラベルの文字列変更
- フォームクローズ時にタイトルが重複していればエラーメッセージを通知する

![CollectionEditorEx.png](https://qiita-image-store.s3.amazonaws.com/0/41649/3bac9fa9-1b16-176e-7dd0-2800565c5930.png)

なお、コードは抜粋したものです。コード全体は下記を参照ください。
https://github.com/kyamawaki/CollectionEditorEx

#　準備

コレクションフォーム作成時にイベントハンドラを追加しています。
フォームロード時にコントロールをテーブルに格納しています。

```antlr-c#
        /// <summary>
        /// コレクションフォーム作成
        /// </summary>
        /// <returns></returns>
        protected override CollectionForm CreateCollectionForm()
        {
            _collectionForm = base.CreateCollectionForm();
            _collectionForm.Load += Form_Load;
            _collectionForm.FormClosing += Form_FormClosing;
            return _collectionForm;
        }

        /// <summary>
        /// フォームロード時に呼ばれる
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void OnLoad(object sender, EventArgs e)
        {
            InitFormControlTable();
            CustomizeFormControlBehavior();
        }

        /// <summary>
        /// フォームコントロールの振る舞いをカスタマイズする
        /// </summary>
        protected virtual void CustomizeFormControlBehavior()
        {
            foreach (KeyValuePair<string, Control> kvp in _formControlTable)
            {
                kvp.Value.TextChanged += Control_TextChanged;
                kvp.Value.Click += Control_Click;
            }
        }
```

#　リストアイテムの追加、削除、移動の禁止

関連コントロールを無効状態にすることで、アイテムの追加、削除などを禁止しています。コントロールを非表示にする方法も試してみましたが、レイアウトが崩れるのでこの方法にしました。

```antlr-c#

        /// <summary>
        /// コントロール有効状態変更イベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Control_EnabledChanged(object sender, EventArgs e)
        {
            Control control = sender as Control;
            if (control == null)
            {
                return;
            }
            OnControlEnableChanged(control, e);
        }

        /// <summary>
        /// コントロール有効状態変更時に呼ばれる
        /// </summary>
        /// <param name="control"></param>
        /// <param name="e"></param>
        protected virtual void OnControlEnableChanged(Control control, EventArgs e)
        {
            switch (control.Name)
            {
                case "addButton":
                case "removeButton":
                case "upButton":
                case "downButton":
                    control.Enabled = false;
                    break;
            }
        }
```

# プロパティラベルの文字列変更

画面右上の「プロパティ(P)」が、「hogehoge：プロパティ(P)」と表示されるのが嫌で、[
プロパティ(P)」に固定するようにしています。

```antlr-c#

        /// <summary>
        /// プロパティラベルテキスト変更イベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Control_TextChanged(object sender, EventArgs e)
        {
            Control control = sender as Control;
            if (control == null)
            {
                return;
            }
            OnControlTextChanged(control, e);
        }

        /// <summary>
        /// プロパティラベルテキスト変更時に呼ばれる
        /// </summary>
        /// <param name="control"></param>
        /// <param name="e"></param>
        protected virtual void OnControlTextChanged(Control control, EventArgs e)
        {
            switch (control.Name)
            {
                case "propertiesLabel":
                    control.Text = "プロパティ(&P):";
                    break;
            }
        }
```

# フォームクローズ時に重複プロパティがあればエラーメッセージを通知する

フォームのクローズ時にプロパティの内容をチェックして重複があればエラーメッセージを表示するようにしました。本当にしたかったことは重複があればフォームを閉じないようにしたかったのですが、クローズをキャンセルしてもリストボックスの要素が消えてしまうという問題を解決できなかったので、メッセージだけ出してクローズするようにしています。

```antlr-c#
        /// <summary>
        /// 指定した配列をコレクション内の項目として設定します。
        /// </summary>
        /// <param name="editValue"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        protected override object SetItems(object editValue, object[] value)
        {
            // 編集結果を取得してクローズ時に検証する
            _editedItems.Clear();
            _editedItems.AddRange(value);

            return base.SetItems(editValue, value);
        }

        /// <summary>
        /// フォームクローズ時に呼ばれる
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void OnClosing(object sender, FormClosingEventArgs e)
        {
            List<string> tempItems = new List<string>();
            foreach(object item in _editedItems)
            {
                if (tempItems.Contains(item as string))
                {
                    MessageBox.Show("タイトルが重複しています");
                    break;
                }
                tempItems.Add(item as string);
            }
        }
```