1

.NET TreeViewコントロールから派生したC#クラスでここにリストされているツリービュー通知を処理するにはどうすればよいですか?

たとえば、次のようにクリック通知を処理しようとしました。

class ExtendedTreeView : TreeView
{
    private const Int32 NM_FIRST = (Int32)(0U - 0U);
    private const Int32 NM_CLICK = unchecked((Int32)((UInt32)NM_FIRST - 2U));

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == NM_CLICK)
        {
            MessageBox.Show("NM_CLICK");
        }
        base.WndProc(ref m);
    }
}

ただし、メッセージボックスは表示されません。Win32 APIを使用して.NETコントロールの動作を変更しようとするのはこれが初めてなので、何が問題になるのかわかりません。

これは、これらの通知を処理するための正しいアプローチですか?

参考:.NETTreeViewコントロールにクリックイベントがあることは知っています。これは最初のテストです。TVS_EX_MULTISELECT後でスタイルを有効にしたい。.NET TreeViewコントロールは、が有効になっているAfterSelect場合はイベントを発生させないため、後で通知の動作をTVS_EX_MULTISELECT調査したいと思います。TVN_SELCHANGEDTVN_ITEMCHANGED

4

3 に答える 3

5

それほど単純ではありません。MSDNの記事を確認してください。NM_CLICK通知はWM_NOTIFYメッセージとして配信されます。そして、それはツリービューのに送信されます。Winformsには、メッセージをTreeViewから派生したクラスで処理し、イベント処理をカスタマイズできるように、元のコントロールにエコーバックするための配管が用意されています。これは、メッセージに0x2000、WinformsソースコードのWM_REFLECTの値を追加することによって行われます。

したがって、コードは次のようになります。

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class ExtendedTreeView : TreeView {
    protected override void WndProc(ref Message m) {
        if (m.Msg == WM_REFLECT + WM_NOFITY) {
            var notify = (NMHDR)Marshal.PtrToStructure(m.LParam, typeof(NMHDR));
            if (notify.code == NM_CLICK) {
                MessageBox.Show("yada");
                m.Result = (IntPtr)1;
                return;
            }

        }
        base.WndProc(ref m);
    }
    private const int NM_FIRST = 0;
    private const int NM_CLICK = NM_FIRST - 2;
    private const int WM_REFLECT = 0x2000;
    private const int WM_NOFITY = 0x004e;

    [StructLayout(LayoutKind.Sequential)]
    private struct NMHDR {
        public IntPtr hwndFrom;
        public IntPtr idFrom;
        public int code;
    }
}

TreeViewはすでにこれらすべてを実行していることに注意してください。これにより、NodeMouseClick、Click、およびMouseClickイベントが生成されます。これを行うコードは、ネイティブコントロールのいくつかの癖も回避するため、使用する前に、これが本当に必要であることを確認してください。何が起こっているのかを知りたい場合は、リファレンスソースを確認してください。

于 2012-05-16T12:18:56.633 に答える
2

通知はコントロールの親に送信されます。

ツリービューコントロールの親ウィンドウに、ユーザーがコントロール内でマウスの左ボタンをクリックしたことを通知します。

これはWM_NOITIFYメッセージで行われます。幸いなことに、作成者は、ツリービューのサブクラスも通知を受信できるようにするために、リフレクションと呼ばれるメカニズムも含めました。メッセージは&H2000 | WM_NOTIFY、正確にとして扱うことができるものですWM_NOTIFY

NM_CLICKまた、これはメッセージではなく、NMHDR構造内にラップされた通知であることに注意してください

この通知コードは、WM_NOTIFYメッセージの形式で送信されます。

于 2012-05-16T12:14:22.773 に答える
0

MSDNで言及されている2つの重要なことがあります:1)msg.lparamはNMHDR構造へのポインターです2)通知は親コントロールに送信されます

したがって、動作するコードは次のとおりです(コンソールアプリとしてコンパイルします-そこでメッセージを出力します):

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class MyTreeView : TreeView
{
    public TreeView RealTreeView;
    public MyTreeView()
    {
        RealTreeView = new TreeView();
        RealTreeView.Dock = DockStyle.Fill;
        Controls.Add(RealTreeView);
    }
    enum WM
    {
        NOTIFY = 78
    }
    enum NM : uint
    {
        FIRST = 0,
        NM_CLICK = unchecked(FIRST - 2),
        NM_CUSTOMDRAW = unchecked(FIRST - 12),
        NM_DBLCLK = unchecked(FIRST - 3),
        NM_KILLFOCUS = unchecked(FIRST - 8),
        NM_RCLICK = unchecked(FIRST - 5),
        NM_RDBLCLK = unchecked(FIRST - 6),
        NM_RETURN = unchecked(FIRST - 4),
        NM_SETCURSOR = unchecked(FIRST - 17),
        NM_SETFOCUS = unchecked(FIRST - 7)
    }

    [StructLayout(LayoutKind.Sequential)]
    struct NMHDR {
        public IntPtr hwndFrom;
        public UIntPtr idFrom;
        public uint code;
    }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (m.Msg == (int)WM.NOTIFY)
        {
            uint code;
            unsafe
            {
                var nmhdr = (NMHDR*)m.LParam.ToPointer();
                code = nmhdr->code;
            }
            NM nmCode = (NM)code;
            Console.WriteLine("WM_NOTIFY " + nmCode);
        }
    }
}

public class MyGuiClass
{
    public static void Main()
    {
        Form f = new Form();
        var tv = new MyTreeView();
        tv.RealTreeView.Nodes.Add("zero").Nodes.Add("sub-zero");
        tv.RealTreeView.Nodes.Add("one");
        tv.RealTreeView.Nodes.Add("two");
        tv.RealTreeView.Nodes.Add("three");
        tv.Dock = DockStyle.Fill;
        f.Controls.Add(tv);
        Application.Run(f);
    }
}

編集:そしてもちろん/unsafeでコンパイルすることを忘れないでください。

于 2012-05-16T12:35:30.327 に答える