Pergunta

Eu tenho um aplicativo WTL que usa um controle de ComboBox estendido (a classe Win32 ComboBoxEx32) com o CBS_DROPDOWNLIST estilo. Funciona bem (eu posso ter imagens em cada item na caixa), mas o comportamento do teclado é diferente de um ComboBox normal - pressionar uma tecla não pulará para o primeiro item no combo que começa com essa letra.

Por exemplo, se eu adicionar as cordas 'Arnold', 'Bob' e 'Charlie' ao combo, se eu selecionar o combo e pressionar 'B', então 'Bob' não será selecionado.

Alguém sabe como fazer isso funcionar? Atualmente, a única idéia que consigo pensar é de alguma forma subclasse o combinação 'real' (eu posso obter o identificador para isso usando o CBEM_GETCOMBOCONTROL mensagem) e processo WM_CHARTOITEM. Este é um pita, então pensei em perguntar se mais alguém já havia encontrado esse problema antes.

Foi útil?

Solução

No final, enganchi o controle de ComboBox (obtido com CBEM_GETCOMBOCONTROL) e preso o WM_CHARTOITEM mensagem e executei minha própria pesquisa. Posso postar código se mais alguém estiver interessado.

Outras dicas

Eu criei uma solução de trabalho e quero compartilhar isso:

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;
}

Minha sugestão é abandonar o CombOBoxEx e desenhar o ícone com uma caixa de combinação regular do proprietário. O CCOMBOBoxEx é um pouco diferente do ComboBox 'normal', mas de maneiras suficientes que eu suspeito que seja uma reimplementação completa. Observe como um item selecionado parece um pouco diferente do que é selecionado em uma caixa de combinação normal também.

Os controles de desenho do proprietário no WTL são bastante fáceis de implementar com o COWNERDRAW Mixin.

Não é uma resposta para sua pergunta, apenas informando que é assim que eu lido com o CombOBoxEx hoje em dia :)

Em nossa aplicação, o comportamento do teclado que você descreveu foi perdido entre as versões. Como se vê, removemos uma dependência manifesta adicional, que resultou em uma dependência de uma versão mais antiga do COMCTL32.DLL (5.82). Esta linha nas configurações do projeto, propriedades de configuração -> Linker -> Arquivo de manifesto -> Dependências de manifesto adicionais:

type = 'win32' nome = 'microsoft.windows.common-controls' versão = '6.0.0.0' ProcessOarquitetura = ''PublicKeyToken =' 6595b64144ccf1df 'idioma =''

Corrigido para nós.

Usando Walker dependência, pode -se verificar se o aplicativo agora depende apenas do COMCTL32.DLL versão 6.10, que tem o comportamento correto.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top