ListViewのSubItemを編集

環境
C# + .net framework2.0

ListViewのプロパティにLabelEditというのがあって,これをtrueにすると一列目の項目は編集できるようになるのだが,二列目・三列目のアイテムは編集できない.これらのサブアイテムを編集するための方法として,該当するアイテムの上にぴったり重なるTextBoxを載せていかにも編集できているように見せるのが一般的らしいのでそのとおりやってみた.

使い方

// フォームに貼り付けたリストビューがダブルクリックされたときに
// 呼び出されるように指定してある(Visual Studioのデザイナで編集)
private void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
{
  ListViewHitTestInfo info = listView1.HitTest(e.X, e.Y);
  if (info.SubItem != null && e.Button == MouseButtons.Left)
  {
    ListViewInputBox input = new ListViewInputBox(listView1, info.Item, 1);
    input.FinishInput += new ListViewInputBox.InputEventHandler(input_FinishInput);
    input.Show();
  }
}

void input_FinishInput(object sender, ListViewInputBox.InputEventArgs e)
{
  Console.WriteLine(e.Path);
  Console.WriteLine(e.NewName);
}

テキストボックス

/// <summary>
/// リストビュー上でアイテムを編集するためのテキストボックス
/// </summary>
public class ListViewInputBox : TextBox
{
  public class InputEventArgs : EventArgs
  {
    public string Path = "";
    public string NewName = "";
  }

  public delegate void InputEventHandler(object sender, InputEventArgs e);

  //イベントデリゲートの宣言
  public event InputEventHandler FinishInput;

  private InputEventArgs args = new InputEventArgs();
  private bool finished = false;

  /// <summary>
  /// 
  /// </summary>
  /// <param name="parent">対象となるListViewコントロール</param>
  /// <param name="item">編集対象のアイテム</param>
  /// <param name="subitem_index">編集する対象の列</param>
  public ListViewInputBox(ListView parent, ListViewItem item, int subitem_index) : base()
  {
    args.Path = item.SubItems[0].Text;
    args.NewName = item.SubItems[1].Text;

    int left = 0;
    for (int i = 0; i < subitem_index; i++)
    {
      left += parent.Columns[i].Width;
    }
    int width = item.SubItems[subitem_index].Bounds.Width;
    int height = item.SubItems[subitem_index].Bounds.Height - 4;

    this.Parent = parent;
    this.Size = new Size(width, height);
    this.Left = left;
    this.Top = item.Position.Y - 1;
    this.Text = item.SubItems[subitem_index].Text;
    this.LostFocus += new EventHandler(textbox_LostFocus);
    this.ImeMode = ImeMode.NoControl;
    this.Multiline = false;
    this.KeyDown += new KeyEventHandler(textbox_KeyDown);
    this.Focus();
  }

  void Finish(string new_name)
  {
    // Enterで入力を完了した場合はKeyDownが呼ばれた後に
    // さらにLostFocusが呼ばれるため,二回Finishが呼ばれる
    if (!finished)
    {
      // textbox.Hide()すると同時にLostFocusするため,
      // finished=trueを先に呼び出しておかないと,
      // このブロックが二回呼ばれてしまう.
      finished = true;
      this.Hide();
      args.NewName = new_name;
      FinishInput(this, args);
    }
  }

  void textbox_KeyDown(object sender, KeyEventArgs e)
  {
    if (e.KeyCode == Keys.Enter)
    {
      Finish(this.Text);
    }
    else if (e.KeyCode == Keys.Escape)
    {
      Finish(args.NewName);
    }
  }

  void textbox_LostFocus(object sender, EventArgs e)
  {
    Finish(this.Text);
  }
}