4

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

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

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

4

4 回答 4

3

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

于 2010-01-26T09:35:53.917 回答
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;
}
于 2012-09-19T10:39:35.317 回答
0

我的建议是放弃 CComboBoxEx 并使用所有者绘制的常规组合框绘制图标。CComboBoxEx 与“普通”组合框略有不同,但我怀疑它是完全重新实现的。请注意,所选项目看起来与在普通组合框中选择的项目略有不同。

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

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

于 2010-01-14T13:03:51.053 回答
0

在我们的应用程序中,您描述的键盘行为在版本之间丢失了。事实证明,我们删除了一个额外的清单依赖,这导致依赖于旧版本的 comctl32.dll (5.82)。项目设置中的这一行,Configuration Properties -> Linker -> Manifest File -> Additional Manifest Dependencies:

类型='win32' 名称='Microsoft.Windows.Common-Controls' 版本='6.0.0.0' 处理器架构=' ' publicKeyToken='6595b64144ccf1df' 语言=' '

为我们修好了。

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

于 2013-07-05T10:27:10.793 回答