LoginSignup
0
0

More than 5 years have passed since last update.

ListView(Details)のカラムを直接編集したいっ! 巻之弐

Last updated at Posted at 2016-08-22

前口上

前稿を受けて、サブクラス化にてカラム直接編集を実現してみたいと思います。

序なので、ふっかちゃんバージョンも紹介しときましょう。
ただ、実はふっかちゃん、MSのサイトからうまくコピーできないパターンがあります。(改行がぐちゃぐちゃで…)
copyしてくる時はちょっと古めの下記サイトを参照してください。(両方必要です)

あ、後 Win32 の中に
[DllImport("coredll.dll")]
ってのが11ヶ所ありますが、何か宜しくないので、
[DllImport("user32.dll")]
に変えて下さい。問題ない筈です。

ふっかちゃん

前稿のコードに前出ふたつのコードを合わせた上で、以下の様にコードを追加すればOKです。

public Form1() {
    InitializeComponent();

    //    次の2行を追加する
    WndProcHooker.HookWndProc(listView1, new WndProcHooker.WndProcCallback(WM_Hook_Handler), WM_HSCROLL);
    WndProcHooker.HookWndProc(listView1, new WndProcHooker.WndProcCallback(WM_Hook_Handler), WM_VSCROLL);
}

const int WM_HSCROLL = 0x114;
const int WM_VSCROLL = 0x115;

int WM_Hook_Handler(IntPtr hwnd, uint msg, uint wParam, int lParam, ref bool handled) {
    Control.FromHandle(hwnd).Focus();
    return -1;
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
    WndProcHooker.UnhookWndProc(listView1, WM_VSCROLL);
    WndProcHooker.UnhookWndProc(listView1, WM_HSCROLL);
}

ListView のスクロールバーを触ると、直接編集ちっくモード抜けたでしょ?
ふっかちゃん、中々やるなー

サブクラス化

いきなりソース!
のみっ!

using System;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

public class ListViewEx : ListView {
    public ListViewEx() : base() {
        this.SetStyle(ControlStyles.EnableNotifyMessage, true);

        this.EditBox = new TextBox();
        this.EditBox.Parent = this;
        this.EditBox.Visible = false;
        this.EditBox.BorderStyle = BorderStyle.FixedSingle;
        this.EditBox.Leave += EditBox_Leave;
        this.EditBox.KeyPress += EditBox_KeyPress;
    }

    protected override void Dispose(bool disposing) {
        if(disposing) {
            this.EditBox.KeyPress -= EditBox_KeyPress;
            this.EditBox.Leave -= EditBox_Leave;
            this.EditBox.Dispose();
        }
        base.Dispose(disposing);
    }

    TextBox EditBox;

    private void EditBox_Leave(object sender, EventArgs e) {
        CurrentColumn.Text = EditBox.Text;
        EditBox.Visible = false;
    }

    private void EditBox_KeyPress(object sender, KeyPressEventArgs e) {
        switch(e.KeyChar) {
        case (char)Keys.Enter:
            this.Focus();
            e.Handled = true;
            break;

        case (char)Keys.Escape:
            EditBox.Text = CurrentColumn.Text;
            this.Focus();
            e.Handled = true;
            break;
        }
    }

    [Browsable(false)]
    public ListViewItem CurrentRow { get; set; }

    [Browsable(false)]
    public ListViewItem.ListViewSubItem CurrentColumn { get; set; }

    [Browsable(false)]
    public int CurrentRowIndex { get { return (CurrentRow == null) ? -1 : CurrentRow.Index; } }

    [Browsable(false)]
    public int CurrentColumnIndex { get { return (CurrentColumn == null) ? -1 : CurrentRow.SubItems.IndexOf(CurrentColumn); } }

    protected override void OnItemCheck(ItemCheckEventArgs ice) {
        if(CurrentColumn != null && CurrentColumnIndex != 0)
            ice.NewValue = ice.CurrentValue;

        base.OnItemCheck(ice);
    }

    protected override void OnMouseDown(MouseEventArgs e) {
        Point loc = this.PointToClient(Cursor.Position);

        CurrentRow = null;
        CurrentColumn = null;

        CurrentRow = this.GetItemAt(loc.X, loc.Y);
        if(CurrentRow == null || !CurrentRow.Bounds.Contains(loc))
            CurrentRow = null;
        else {
            CurrentColumn = CurrentRow.GetSubItemAt(loc.X, loc.Y);
            if(CurrentColumn == null || !CurrentColumn.Bounds.Contains(loc))
                CurrentColumn = null;
        }

        base.OnMouseDown(e);
    }

    protected override void OnResize(EventArgs e) {
        this.Focus();
        base.OnResize(e);
    }

    protected override void OnColumnWidthChanging(ColumnWidthChangingEventArgs e) {
        this.Focus();
        base.OnColumnWidthChanging(e);
    }

    protected override void OnNotifyMessage(Message m) {
        switch(m.Msg) {
        case WM_HSCROLL:
        case WM_VSCROLL:
            this.Focus();
            break;
        }
        base.OnNotifyMessage(m);
    }

    const int WM_HSCROLL = 0x114;
    const int WM_VSCROLL = 0x115;

    public void EditColumn() {
        if(CurrentColumn == null || CurrentColumnIndex == 0) return;

        Rectangle rect = CurrentColumn.Bounds;
        rect.Intersect(this.ClientRectangle);
        rect.Y -= 1;

        EditBox.Bounds = rect;
        EditBox.Text = CurrentColumn.Text;
        EditBox.Visible = true;
        EditBox.BringToFront();
        EditBox.Focus();
    }
}

いやいや、使用説明だけはしなくては…

使いたいプロジェクトの中に ListVIewEx.cs のクラスファイルを追加して、上記ソースをまるっと貼り付けちゃって下さい。
一旦全体ビルドしたら、ツールボックスに ListViewEx が増えている筈なのでデザイナ画面で使えます。(勿論ビルドエラーがあったらダメですよ)



プロパティ

CurrentRowとCurrentRowIndexプロパティが増えてます

CurrentColumnとCurrentColumnIndexプロパティも増えてます

メソッド

EditColumn()が定義されています

機能は名前から推し量って下さい。(投げやり)

EditColumn メソッドはフォームに張り付けた ListViewExMouseDoubleClick イベント辺りで必要性を確認の上呼びだす仕様です。


コードで説明すると、こんな感じ↓
これで前稿と同じく、カラム2だけが編集対象となります。

private void listView1_MouseDoubleClick(object sender, MouseEventArgs e) {
    if(listView1.CurrentColumnIndex == 1)
        listView1.EditColumn();
}

一つ忘れものがありました。
カラムサイズを調整した時にもモード抜けないといけないですね。サブクラスバージョンでは織り込んであります。

次への布石

今回のサブクラス化では、どのカラムが編集対象であるかは、外部からコントロールしなければなりません。
カラム位置で判定していたりすると、コードの可読性は落ちますしメンテナンス性も悪くなります。

ここはひとつListViewItemのカラムヘッダ辺りに Editable みたいなプロパティがあると良いのになー

とか思っちゃったのが運の尽き。
更に泥沼にはまっていく…

ちゅー事で次はColumnHeaderのサブクラス対応かっ?
請うご期待!


※ 一旦脳味噌クールダウン図って別ネタを挟むかも…

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0