ComboBoxEx32(CComboBoxEx)键盘行为

时间:2010-01-14 10:51:39

标签: c++ mfc winapi atl wtl

我有一个WTL应用程序,它使用具有ComboBoxEx32样式的扩展组合框控件(Win32类CBS_DROPDOWNLIST)。它运行良好(我可以对框中的每个项目都有图像)但键盘行为与普通组合框不同 - 按键不会跳转到以该字母开头的组合中的第一项。

例如,如果我将字符串'Arnold','Bob'和'Charlie'添加到组合中,如果我然后选择组合并按'B',则不会选择'Bob'。

有谁知道如何使这项工作?目前,我能想到的唯一想法是以某种方式将“实际”组合框子类化(我可以使用CBEM_GETCOMBOCONTROL消息获取此处理)并处理WM_CHARTOITEM。这是一个PITA所以我想我会问其他人是否曾经遇到过这个问题。

4 个答案:

答案 0 :(得分:3)

最后,我连接了组合框控件(使用CBEM_GETCOMBOCONTROL获得)并捕获了WM_CHARTOITEM消息并执行了我自己的查找。如果其他人有兴趣,我可以发布代码。

答案 1 :(得分: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;
}

答案 2 :(得分:0)

我的建议是放弃CComboBoxEx并使用所有者绘制常规组合框绘制图标。 CComboBoxEx与'普通'组合框略有不同,但我认为它是一个完全重新实现的方式。请注意所选项目与普通组合框中选择的项目略有不同。

使用COwnerDraw mixin可以很容易地实现WTL中的所有者绘制控件。

不是你问题的答案,只是让你知道我现在如何处理CComboBoxEx:)

答案 3 :(得分:0)

在我们的应用程序中,您描述的键盘行为在版本之间丢失了。事实证明,我们删除了一个额外的清单依赖项,这导致依赖于旧版本的comctl32.dll(5.82)。这一行在项目设置中,配置属性 - &gt;链接器 - &gt;清单文件 - &gt;额外的清单依赖:

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

为我们修好了。

使用Dependency Walker,可以检查应用程序现在是否仅依赖于具有正确行为的comctl32.dll版本6.10。