ComboBoxEx32 (CComboBoxEx) keyboard behavior

I have a WTL application that uses an advanced combobox control (Win32 class ComboBoxEx32 ) with the style CBS_DROPDOWNLIST . It works well (I can have images against each element in the field), but the keyboard behavior is different from the usual combobox - pressing a key does not go to the first element in a combo that starts with this letter.

For example, if I add the lines “Arnold”, “Bob” and “Charlie” to the combo, if I then select the combo and press “B”, then “Bob” will not be selected.

Does anyone know how to make this work? Currently, the only idea I can think of is to somehow subclass the "actual" combobox (I can get the handle to this message CBEM_GETCOMBOCONTROL ) and process WM_CHARTOITEM . This is PITA, so I thought I would ask if anyone else had this problem before.

+4
source share
4 answers

In the end, I hooked up a list control (obtained using CBEM_GETCOMBOCONTROL ) and grabbed the WM_CHARTOITEM message and performed my own search. I can send the code if anyone else is interested.

+3
source

I created a working solution and want to share this:

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; } 
+1
source

My suggestion is to cut out CComboBoxEx and draw an icon using the usual combined owner field. CComboBoxEx is a little different than the “normal” combobox, but enough, as far as I suspect, is a complete reimplementation. Please note that the selected item looks slightly different from the one selected in the regular combo box.

WTL owner controls are pretty easy to implement with COwnerDraw mixing.

Not an answer to your question, just letting you know that I'm considering CComboBoxEx :)

0
source

In our application, the keyboard behavior that you described was lost between versions. As it turned out, we removed the additional manifest dependency, which led to a dependency on the older version of comctl32.dll (5.82). This line in the project settings, Configuration properties → Linker → Manifest file → Additional manifest dependencies:

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

fixed it for us.

Using Dependency Walker, you can verify that the application now only depends on the version of comctl32.dll version 6.10, which has the correct behavior.

0
source

All Articles