Поведение клавиатуры ComboBoxEx32 (CComboBoxEx) - PullRequest
4 голосов
/ 14 января 2010

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

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

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

Ответы [ 4 ]

3 голосов
/ 26 января 2010

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

1 голос
/ 19 сентября 2012

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

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 голосов
/ 05 июля 2013

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

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

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

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

0 голосов
/ 14 января 2010

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

Средства управления рисованием в WTL довольно легко реализовать с помощью миксина COwnerDraw.

Не ответ на ваш вопрос, просто сообщаю вам, что именно так я сейчас и поступаю с CComboBoxEx:)

...