Я создал рабочее решение и хочу поделиться этим:
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;
}