2013-01-09 30 views
0

我試圖從TreeView中捕獲TVN_SELCHANGING消息。我知道還有BeforeSelect事件,但我想了解爲什麼我無法捕捉到消息...c#中的TVN_SELCHANGING消息#

我讀過msdn TVN_SELCHANG(ED)(ING) LParam是一個指向NMTREEVIEW結構的指針。此外,代碼以WM_NOTIFY消息的形式發送。

所以我一直試圖實現它: (this helped me)

public partial class TreeviewEx : TreeView 
{ 
    [StructLayout(LayoutKind.Sequential)] 
    public struct POINT 
    { 
     public int X; 
     public int Y; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    private struct TVITEM 
    { 
     public uint mask; 
     public IntPtr hItem; 
     public uint state; 
     public uint stateMask; 
     public IntPtr pszText; 
     public int cchTextMax; 
     public int iImage; 
     public int iSelectedImage; 
     public int cChildren; 
     public IntPtr lParam; 
    } 

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

    [StructLayout(LayoutKind.Sequential)] 
    private struct NMTREEVIEW 
    { 
     public NMHDR hdr; 
     public int action; 
     public TVITEM itemOld; 
     public TVITEM itemNew; 
     public POINT ptDrag; 
    } 

    private const int TVN_FIRST = -400; 
    private const int TVN_SELCHANGINGA = (TVN_FIRST - 1); 
    private const int TVN_SELCHANGINGW = (TVN_FIRST - 50); 
    private const int TVN_SELCHANGEDA = (TVN_FIRST - 2); 
    private const int TVN_SELCHANGEDW = (TVN_FIRST - 51); 

    private const int WM_NOFITY = 0x004e; 

    protected override void WndProc(ref Message m) 
    { 
     if (m.Msg == WM_NOFITY) 
     { 
      var notify = (NMTREEVIEW)Marshal.PtrToStructure(m.LParam, typeof(NMTREEVIEW)); 
      if (notify.action == TVN_SELCHANGINGA) 
      { 
       MessageBox.Show("changing"); 
      } 
     } 
     base.WndProc(ref m); 
    } 

我已經嘗試了所有的動作,但他們都不工作。我究竟做錯了什麼?

回答

2

對,這是行不通的。背後有許多歷史,原生Windows控件被設計用於C程序。使用Petzold的「編程Windows」風格編碼,您可以在窗口過程中放置​​窗口的自定義邏輯。並且按照原樣使用了像TreeView這樣的控件。因此,這些控件將其通知消息發送給父級窗口。因爲那是你放置代碼的地方。

這與現代GUI代碼編寫的方式不太兼容。特別是繼承控制以賦予其新行爲的概念。就像你用你的TreeViewEx類一樣。你真的想首先在你自己的課堂上得到這些通知。所以你可以用OnBeforeSelect()做一些有趣的事情來定製控件的行爲。現在把這個消息發送給父節點是一個很大的問題,一個控件永遠不應該意識到它的父節點的實現。

Winforms修復此問題,它反映消息從父窗口回到原始窗口。改變信息是必要的,所以它完全清楚它是一個反映的信息。它通過向消息號WM_REFLECT添加一個常量來實現,該值可以硬編碼爲0x2000。所以修正它是這樣的:

private const int WM_REFLECT = 0x2000; 

protected override void WndProc(ref Message m) { 
    if (m.Msg == WM_REFLECT + WM_NOTIFY) { 
     var nmhdr = (NMHDR)Marshal.PtrToStructure(m.LParam, typeof(NMHDR)); 
     if (nmhdr.code == TVN_SELCHANGINGW) { 
      var notify = (NMTREEVIEW)Marshal.PtrToStructure(m.LParam, typeof(NMTREEVIEW)); 
      // etc.. 
     } 
    } 
    base.WndProc(ref m); 
} 
+0

非常感謝漢斯的解釋!但是我確實已經嘗試添加WM_REFLECT,但它仍然不起作用...還有其他什麼可能是錯誤的? – VincentC

+0

我在發佈之前對其進行了測試,只是爲了確保。你需要擺脫這些消息的A版本,Winforms是純Unicode。 –

+0

再次感謝漢斯告訴我有關Unicode的東西!經過一些調試後,我真的不確定爲什麼這不適合我。我正在使用Visual Studio 2012並嘗試使用32位和64位。 我也嘗試打印出行動代碼,我沒有看到任何預定義的常量值(我確實擺脫了A行爲)。所以我想也許我的常值TVN _...是錯誤的? 然後我試圖打印出ptDrag POINT結構,這個也是錯誤的,它給了我453628288的X和0的Y.難道是我的一些結構被錯誤地定義了嗎? – VincentC