2010-01-14 2 views
4

У меня есть приложение WTL, которое использует расширенный элемент управления combobox (класс Win32 ComboBoxEx32) с типом CBS_DROPDOWNLIST. Он работает хорошо (у меня могут быть изображения против каждого элемента в поле), но поведение клавиатуры отличается от обычного combobox - нажатие клавиши не переходит к первому элементу в комбо, который начинается с этой буквы.ComboBoxEx32 (CComboBoxEx) поведение клавиатуры

Например, если я добавлю строки «Арнольд», «Боб» и «Чарли» в комбо, если я затем выберу комбо и нажмите «В», тогда «Боб» не будет выбран.

Кто-нибудь знает, как сделать эту работу? В настоящее время единственной идеей, о которой я могу думать, является как-то подкласс «фактического» combobox (я могу получить дескриптор этого сообщения CBEM_GETCOMBOCONTROL) и обработать WM_CHARTOITEM. Это PITA, поэтому я подумал, что попрошу, если кто-нибудь еще столкнется с этой проблемой раньше.

ответ

3

В конце концов, я зацепил контроль COMBOBOX (полученный с CBEM_GETCOMBOCONTROL) и ловушкой WM_CHARTOITEM сообщение и выполнил мои Погляди. Я могу отправить код, если кому-то еще интересно.

0

Мое предложение заключается в канаве CComboBoxEx и нарисовать иконку с регулярным со списком владелец-дро. CComboBoxEx немного отличается от «нормального» combobox, но достаточно, насколько я подозреваю, что это полная повторная реализация. Обратите внимание, что выбранный элемент выглядит немного отличным от выбранного в нормальном поле со списком.

Владелец начертить управления в WTL довольно легко реализовать с Mixin COwnerDraw.

не ответ на ваш вопрос, просто давая вам знать, что это, как я занимаюсь CComboBoxEx ныне :)

1

Я создал рабочее решение и хочу поделиться этим:

ComboBoxExKeyboardSupport.h

#pragma once 

class CComboBoxExKeyboardSupport 
{ 
// Construction 
public: 
    CComboBoxExKeyboardSupport(void); 
    ~CComboBoxExKeyboardSupport(void); 

// Attributes 
private: 
    static CSimpleMap<HWND, CComboBoxExKeyboardSupport*> responsibleMap; 

    HWND hComboBoxHwnd; 
    WNDPROC fpOriginalWndProc; 

// Operations 
private: 
    static LRESULT CALLBACK StaticWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 
    LRESULT WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 
    LRESULT HandleCharToItemMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 

    bool IsWindowsXPPlatform(void); 
    bool InputMatches(CString inputChar, CString& itemText); 

public: 
    void Attach(CComboBoxEx& comboBoxEx); 
    void Detach(void); 
}; 

ComboBoxExKeyboardSupport.cpp

#include "StdAfx.h" 
#include "ComboBoxExKeyboardSupport.h" 

// Static member 
CSimpleMap<HWND, CComboBoxExKeyboardSupport*> CComboBoxExKeyboardSupport::responsibleMap; 

CComboBoxExKeyboardSupport::CComboBoxExKeyboardSupport(void) 
{ 
    hComboBoxHwnd = nullptr; 
    fpOriginalWndProc = nullptr; 
} 

CComboBoxExKeyboardSupport::~CComboBoxExKeyboardSupport(void) 
{ 
    Detach(); 
} 

void CComboBoxExKeyboardSupport::Attach(CComboBoxEx& comboBoxEx) 
{ 
    ATLASSERT(hComboBoxHwnd == nullptr); 
    if(hComboBoxHwnd != nullptr) 
     return; 

    if(!IsWindowsXPPlatform()) 
     return; 

    LONG_PTR lpNewWndProc = reinterpret_cast<LONG_PTR>(StaticWndProc); 
    LONG_PTR lpOldWndProc = 0; 

    //---- 
    hComboBoxHwnd = comboBoxEx.GetComboBoxCtrl()->GetSafeHwnd(); 
    ATLASSERT(hComboBoxHwnd != nullptr); 

    // Exchange the WndProc 
    lpOldWndProc = SetWindowLongPtr(hComboBoxHwnd, GWLP_WNDPROC, lpNewWndProc); 
    ATLASSERT(lpOldWndProc != 0); 
    fpOriginalWndProc = reinterpret_cast<WNDPROC>(lpOldWndProc); 

    // Remember the handle and the old WndProc 
    responsibleMap.Add(hComboBoxHwnd, this); 
} 

void CComboBoxExKeyboardSupport::Detach(void) 
{ 
    if(hComboBoxHwnd == nullptr) 
     return; 

    //---- 
    LONG_PTR lpResult = 0; 

    // Reset original WndProc 
    lpResult = SetWindowLongPtr(hComboBoxHwnd, GWLP_WNDPROC, 
     reinterpret_cast<LONG_PTR>(fpOriginalWndProc)); 
    ATLASSERT(lpResult != 0); 

    // Remove handle and WndProc from map 
    responsibleMap.Remove(hComboBoxHwnd); 

    //---- 
    hComboBoxHwnd = nullptr; 
    fpOriginalWndProc = nullptr; 
} 

bool CComboBoxExKeyboardSupport::IsWindowsXPPlatform(void) 
{ 
    OSVERSIONINFO osvi = {0}; 
    bool bResult = false; 

    //---- 
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 
    if(GetVersionEx(&osvi)) 
    { 
     // 5.1 = Windows XP 
     // 5.2 = Windows Server 2003, Windows Server 2003 R2 
     bResult = (osvi.dwMajorVersion == 5 && 
      (osvi.dwMinorVersion == 1 || osvi.dwMinorVersion == 2)); 
    } 

    return bResult; 
} 

LRESULT CComboBoxExKeyboardSupport::StaticWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    CComboBoxExKeyboardSupport* pResponsibleClass = nullptr; 

    // Get responsible class from map 
    pResponsibleClass = responsibleMap.Lookup(hwnd); 
    ATLASSERT(pResponsibleClass != nullptr); 

    //---- 
    return pResponsibleClass->WndProc(hwnd, uMsg, wParam, lParam); 
} 

LRESULT CComboBoxExKeyboardSupport::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    // Save originalWndProc because after WM_DESTROY/Detach the member variable is nullptr. 
    WNDPROC fpOriginalWndProc = this->fpOriginalWndProc; 

    //---- 
    if(uMsg == WM_DESTROY) 
    { 
     Detach(); 
    } 
    else if(uMsg == WM_CHARTOITEM) 
    { 
     return HandleCharToItemMessage(hwnd, uMsg, wParam, lParam); 
    } 

    //---- 
    return ::CallWindowProc(fpOriginalWndProc, hwnd, uMsg, wParam, lParam); 
} 

LRESULT CComboBoxExKeyboardSupport::HandleCharToItemMessage(
    HWND hwnd, 
    UINT uMsg, 
    WPARAM wParam, 
    LPARAM lParam) 
{ 
    //---- 
    LRESULT lResult = CB_ERR; 
    CComboBox* pComboBox = nullptr; 
    int itemCount = 0; 
    int itemSelected = 0; 
    CString itemText; 
    TCHAR inputCharacter = 0; 

    //---- 
    pComboBox = (CComboBox*)CComboBox::FromHandle(hwnd); 

    //---- 
    itemCount = pComboBox->GetCount(); 
    itemSelected = pComboBox->GetCurSel(); 
    inputCharacter = static_cast<TCHAR>(LOWORD(wParam)); 

    // Search from the current selected item plus one to the end 
    for(int i = (itemSelected + 1); i < itemCount; i++) 
    { 
     pComboBox->GetLBText(i, itemText); 
     if(InputMatches(inputCharacter, itemText)) 
     { 
      lResult = i; 
      break; 
     } 
    } 

    if(lResult == CB_ERR) 
    { 
     // Search from the beginning to the selected item minus one. 
     for(int i = 0; i < itemSelected; i++) 
     { 
      pComboBox->GetLBText(i, itemText); 
      if(InputMatches(inputCharacter, itemText)) 
      { 
       lResult = i; 
       break; 
      } 
     } 
    } 

    //---- 
    return lResult; 
} 

bool CComboBoxExKeyboardSupport::InputMatches(CString inputChar, CString& itemText) 
{ 
    CString firstCharString; 
    bool bInputMatches = false; 

    //---- 
    firstCharString = itemText; 
    firstCharString.Left(1); 

    //---- 
    bInputMatches = firstCharString.CompareNoCase(inputChar) == 0; 

    //---- 
    return bInputMatches; 
} 
0

В нашем приложении, поведение клавиатуры вы описали потерялось между версиями. Как оказалось, мы удалили дополнительную зависимость манифеста, которая привела к зависимости от более старой версии comctl32.dll (5.82). Эта строка в настройках проекта, Свойства конфигурации -> Линкера -> Файл манифеста -> Дополнительные манифестные зависимости:

Тип = 'win32' name = 'Microsoft.Windows.Common-Controls' version = '6.0.0.0' processorArchitecture = '' publicKeyToken = '6595b64144ccf1df' language = ''

исправлено оно для нас.

Использование Dependency Walker, можно проверить, что приложение теперь зависит только от comctl32.dll версии 6.10, которая имеет правильное поведение.

Смежные вопросы