2014-10-21 21 views
0

在用C++編寫的Windows應用程序中使用TreeView Common Control時,遇到了一個奇怪的問題,並且通過Windows API(無MFC或其他!): 如果加載了ComCtl32.dll的6.0版本(通過使用清單來說服鏈接器這樣做),則僅發送兩個重要通知TVN_ITEMCHANGED和TVN_ITEMCHANGING(自Windows Vista以來可用)如果使用Unicode字符集。 使用多字節字符集會導致上述兩個通知消失。 使用Unicode和版本5.82 ComCtl32.dll產生相同的結果。順便說一下,我採用了Windows 7 x64和Visual Studio 2010。Windows TreeView常見控件通知和多字節字符集的問題

下面,您會找到一個「最小」(> 180行代碼:/)工作示例。使用Unicode字符集(配置屬性>常規>字符集)在Visual Studio 2010下編譯可使程序按預期工作,但使用多字節字符集會使TVN_ITEMCHANGED和TVN_ITEMCHANGING消失。無論如何還有其他通知。

我忽略了一些事情還是遇到了公共控件實現中的錯誤?我真誠地希望這是我以前的猜測,我非常感謝你對此事的回答和想法!

最好的問候, D.費爾德曼

#include <Windows.h> 
#include <CommCtrl.h> 

#include <iostream> 

#pragma comment(linker, "\"/manifestdependency:type='win32'\ 
    name='Microsoft.Windows.Common-Controls'\ 
    version='6.0.0.0'\ 
    processorArchitecture='*'\ 
    publicKeyToken='6595b64144ccf1df'\ 
    language='*'\"") 

HINSTANCE g_hInst = 0; 
HWND hwndTV_ = 0; 

bool setupTreeView(HWND hwnd) 
{ 
    RECT rc = {0}; 
    GetClientRect(hwnd, &rc); 

    DWORD style = WS_CHILD | WS_VISIBLE | WS_OVERLAPPED | TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT; 
    hwndTV_ = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, 0, style, 
     10, 10, (rc.right - rc.left) - 20, (rc.bottom - rc.top) - 20, 
     hwnd, (HMENU) 0xDF, g_hInst, 0); 

    if (! hwndTV_) 
     return false; 

    style |= TVS_CHECKBOXES; 
    SetWindowLong(hwndTV_, GWL_STYLE, style); 

    HIMAGELIST hil = ImageList_Create(24, 24, ILC_COLOR | ILC_COLOR32, 2, 0); 
    const int img1 = ImageList_AddIcon(hil, LoadIcon(0, IDI_QUESTION)); 
    const int img2 = ImageList_AddIcon(hil, LoadIcon(0, IDI_INFORMATION)); 
    SendMessage(hwndTV_, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM) hil); 

    TVINSERTSTRUCT tvis = {0}; 
    tvis.hParent = TVI_ROOT; 
    tvis.hInsertAfter = TVI_ROOT; 
    tvis.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; 
    tvis.item.cchTextMax = 5; 
    tvis.item.pszText = TEXT("root\0"); 
    tvis.item.state = (2 << 12) | TVIS_EXPANDED; 
    tvis.item.stateMask = TVIS_STATEIMAGEMASK | TVIS_EXPANDED; 
    tvis.item.iImage = img1; 
    tvis.item.iSelectedImage = img2; 
    HTREEITEM hRoot = (HTREEITEM) SendMessage(hwndTV_, TVM_INSERTITEM, 0, (LPARAM) &tvis); 

    tvis.hParent = hRoot; 
    tvis.hInsertAfter = TVI_LAST; 
    tvis.item.cchTextMax = 7; 
    tvis.item.pszText = TEXT("item 1\0"); 
    SendMessage(hwndTV_, TVM_INSERTITEM, 0, (LPARAM) &tvis); 

    tvis.item.pszText = TEXT("item 2\0"); 
    SendMessage(hwndTV_, TVM_INSERTITEM, 0, (LPARAM) &tvis); 

    return true; 
} 

LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{ 
    LRESULT res = 0u; 
    bool handled = true; 
    switch (msg) 
    { 
     case WM_CLOSE: 
      PostQuitMessage(0); 
      break; 

     case WM_CREATE: 
      setupTreeView(hwnd); 
      break; 

     case WM_NOTIFY: 
     { 
      NMHDR* const nmhdr = (NMHDR*)lParam; 
      switch (nmhdr->code) 
      { 
       case NM_CUSTOMDRAW: 
        std::cout << "NM_CUSTOMDRAW\n"; 
        break; 

       case NM_CLICK: 
        std::cout << "NM_CLICK\n"; 
        break; 

       case TVN_ITEMCHANGING: 
        std::cout << "!!! TVN_ITEMCHANGING !!!\n"; 
        break; 

       case TVN_ITEMCHANGED: 
        std::cout << "!!! TVN_ITEMCHANGED !!!\n"; 
        break; 

       case TVN_SELCHANGED: 
        std::cout << "TVN_SELCHANGED\n"; 
        break; 

       case TVN_SELCHANGING: 
        std::cout << "TVN_SELCHANGING\n"; 
        break; 

       case TVN_ITEMEXPANDED: 
        std::cout << "TVN_ITEMEXPANDED\n"; 
        break; 

       case TVN_ITEMEXPANDING: 
        std::cout << "TVN_ITEMEXPANDING\n"; 
        break; 

       default: 
        break; 
      } // switch (
      break; 
     } 

     default: 
      handled = false; 
      break; 
    } // switch (msg 

    if (! handled) 
     res = DefWindowProc(hwnd, msg, wParam, lParam); 
    return res; 
} 

int run(HINSTANCE hInst) 
{ 
    g_hInst = hInst; 

    WNDCLASSEX wndCls = {0}; 
    wndCls.cbSize = sizeof(WNDCLASSEX); 
    wndCls.cbClsExtra = 0; 
    wndCls.cbWndExtra = 0; 
    wndCls.hbrBackground = GetSysColorBrush(COLOR_3DFACE); 
    wndCls.hCursor = LoadCursor(0, IDC_HAND); 
    wndCls.hIcon = LoadIcon(0, IDI_WINLOGO); 
    wndCls.hIconSm = LoadIcon(0, IDI_WINLOGO); 
    wndCls.hInstance = g_hInst; 
    wndCls.lpfnWndProc = WindowProc; 
    wndCls.lpszClassName = TEXT("TestWindowClass"); 
    wndCls.lpszMenuName = 0; 
    wndCls.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 
    RegisterClassEx(&wndCls); 

    INITCOMMONCONTROLSEX cc = {0}; 
    cc.dwSize = sizeof(INITCOMMONCONTROLSEX); 
    cc.dwICC = ICC_TREEVIEW_CLASSES; 
    InitCommonControlsEx(&cc); 

    HWND hwnd = CreateWindowEx(0, TEXT("TestWindowClass"), TEXT("Test TreeView"), 
     WS_VISIBLE | WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION, CW_USEDEFAULT, 
     CW_USEDEFAULT, 400, 300, NULL, NULL, hInst, 0); 

    MSG msg = {0}; 
    for (BOOL res = TRUE; res != 0;) 
    { 
     res = GetMessage(&msg, 0, 0, 0); 
     if (res != -1) 
     { 
      TranslateMessage(&msg); 
      DispatchMessage(&msg); 
     } 

     if (msg.message == WM_QUIT) 
      res = 0; 
    } 

    HIMAGELIST hil = (HIMAGELIST) SendMessage(hwndTV_, TVM_GETIMAGELIST, (WPARAM) TVSIL_NORMAL, 0); 
    if (hil) 
     ImageList_Destroy(hil); 

    UnregisterClass(TEXT("TestWindowClass"), g_hInst); 

    return 0; 
} 

// Please build using /subsystem:console 
int main(int /*argc*/, char** /*argv*/) 
{ 
    return run(GetModuleHandle(0)); 
} 

回答

0

他們不消失,你根本就沒有在你的WindowProc()尋找他們。像許多API一樣,通知也可以包含Ansi和Unicode版本,但是您只是在尋找其中一種,這取決於您是在編譯MBCS還是Unicode。 Ansi TreeView可以接收Unicode通知,反之亦然。你應該尋找既AW版本的通知,並相應地處理它們:

switch (nmhdr->code) 
{ 
    ... 

    case TVN_ITEMCHANGINGA: 
     std::cout << "!!! TVN_ITEMCHANGINGA !!!\n"; 
     break; 

    case TVN_ITEMCHANGINGW: 
     std::cout << "!!! TVN_ITEMCHANGINGW !!!\n"; 
     break; 

    case TVN_ITEMCHANGEDA: 
     std::cout << "!!! TVN_ITEMCHANGEDA !!!\n"; 
     break; 

    case TVN_ITEMCHANGEDW: 
     std::cout << "!!! TVN_ITEMCHANGEDA !!!\n"; 
     break; 

    case TVN_SELCHANGEDA: 
     std::cout << "TVN_SELCHANGEDA\n"; 
     break; 

    case TVN_SELCHANGEDW: 
     std::cout << "TVN_SELCHANGEDW\n"; 
     break; 

    case TVN_SELCHANGINGA: 
     std::cout << "TVN_SELCHANGINGA\n"; 
     break; 

    case TVN_SELCHANGINGW: 
     std::cout << "TVN_SELCHANGINGW\n"; 
     break; 

    case TVN_ITEMEXPANDEDA: 
     std::cout << "TVN_ITEMEXPANDEDA\n"; 
     break; 

    case TVN_ITEMEXPANDEDW: 
     std::cout << "TVN_ITEMEXPANDEDW\n"; 
     break; 

    case TVN_ITEMEXPANDINGA: 
     std::cout << "TVN_ITEMEXPANDINGA\n"; 
     break; 

    case TVN_ITEMEXPANDINGW: 
     std::cout << "TVN_ITEMEXPANDINGW\n"; 
     break; 

    ... 
} 
+0

唉唉,有時我不知道如何設法讓我的鞋子在早晨!非常感謝你,這非常解決它!儘管我不明白爲什麼剩餘的消息會像往常一樣出現,而TVN_ITEMCHANG(ED/ING)的行爲不同並且需要區分... – feldmann 2014-10-21 23:32:52

+0

請參閱['TVM_GETUNICODEFORMAT'](http://msdn.microsoft.com/zh-cn/ us/library/windows/desktop/bb773730.aspx)和['TVM_SETUNICODEFORMAT'](http://msdn.microsoft.com/en-us/library/windows/desktop/bb773775.aspx)。 – 2014-10-21 23:53:13

+0

我實際上已經發現了這兩條消息,並在發佈之前嘗試過,但它們不適用於TVN_ITEMCHANG(ED/ING)。經過睡眠並再次嘗試,我發現TVM_SETUNICODEFORMAT僅影響其他通知,如TVN_SELCHANG(ED/ING),但不會影響導致我的問題的兩個通知。雖然我會認爲這是一個錯誤,但它至少是一個(非常煩人的)不一致。無論如何,你的回答讓我找到解決方案,所以再次感謝! – feldmann 2014-10-22 09:16:18