我创建了一个可行的解决方案并想分享它:
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;
}