MFC中如何阻止无文本输入控件唤起触摸键盘?
MFC下阻止无文本输入控件唤起触摸键盘的解决方案
针对Windows 10触屏平板上的MFC应用,选中CComboBox派生类、原生CListBox这类无文本输入功能的控件时触发触摸键盘,且点击CEdit后问题临时消失的情况,以下是几个根源性的解决方法:
方案1:重写控件WM_GETOBJECT消息,修正UI自动化属性
Windows触摸键盘通过UI自动化框架识别控件类型,默认情况下部分非输入控件会被误判为可输入组件。我们可以通过自定义Accessible对象,明确标记控件的角色和状态:
针对CComboBox派生类
在你的派生类中添加消息映射与处理:
BEGIN_MESSAGE_MAP(CMyComboBox, CComboBox) ON_WM_GETOBJECT() END_MESSAGE_MAP() HRESULT CMyComboBox::OnGetObject(REFIID riid, LPVOID* ppvObject) { if (riid == IID_IAccessible) { HRESULT hr = CComboBox::OnGetObject(riid, ppvObject); if (SUCCEEDED(hr) && *ppvObject) { CComQIPtr<IAccessible> pAcc = static_cast<IAccessible*>(*ppvObject); if (pAcc) { // 明确标记为下拉列表控件,而非可编辑文本控件 VARIANT varRole; VariantInit(&varRole); pAcc->get_accRole(CHILDID_SELF, &varRole); varRole.lVal = ROLE_SYSTEM_COMBOBOX; // 设置为只读状态,强化非输入属性 VARIANT varState; VariantInit(&varState); pAcc->get_accState(CHILDID_SELF, &varState); varState.lVal |= STATE_SYSTEM_READONLY; } } return hr; } return CComboBox::OnGetObject(riid, ppvObject); }
针对CListBox控件
子类化原生CListBox并处理WM_GETOBJECT:
class CMyListBox : public CListBox { DECLARE_MESSAGE_MAP() public: HRESULT OnGetObject(REFIID riid, LPVOID* ppvObject); }; BEGIN_MESSAGE_MAP(CMyListBox, CListBox) ON_WM_GETOBJECT() END_MESSAGE_MAP() HRESULT CMyListBox::OnGetObject(REFIID riid, LPVOID* ppvObject) { if (riid == IID_IAccessible) { HRESULT hr = CListBox::OnGetObject(riid, ppvObject); if (SUCCEEDED(hr) && *ppvObject) { CComQIPtr<IAccessible> pAcc = static_cast<IAccessible*>(*ppvObject); if (pAcc) { // 标记为列表控件,排除输入类角色 VARIANT varRole; VariantInit(&varRole); pAcc->get_accRole(CHILDID_SELF, &varRole); varRole.lVal = ROLE_SYSTEM_LIST; // 设置只读状态 VARIANT varState; VariantInit(&varState); pAcc->get_accState(CHILDID_SELF, &varState); varState.lVal |= STATE_SYSTEM_READONLY; } } return hr; } return CListBox::OnGetObject(riid, ppvObject); }
方案2:利用ITextInputPanel主动隐藏触摸键盘
通过Windows提供的ITextInputPanel接口,在控件获得焦点时强制隐藏触摸键盘:
- 先在应用初始化时初始化COM(若未初始化):
// 在CWinApp::InitInstance中添加 CoInitialize(NULL);
- 在控件的WM_SETFOCUS消息处理中添加隐藏逻辑:
// CComboBox派生类的焦点处理 void CMyComboBox::OnSetFocus(CWnd* pOldWnd) { CComboBox::OnSetFocus(pOldWnd); CComPtr<ITextInputPanel> pTIP; HRESULT hr = pTIP.CoCreateInstance(CLSID_TextInputPanel); if (SUCCEEDED(hr)) { pTIP->SetWindow(m_hWnd); pTIP->SetInPlaceVisibility(IVF_OFF); } } // CListBox子类的焦点处理 void CMyListBox::OnSetFocus(CWnd* pOldWnd) { CListBox::OnSetFocus(pOldWnd); CComPtr<ITextInputPanel> pTIP; HRESULT hr = pTIP.CoCreateInstance(CLSID_TextInputPanel); if (SUCCEEDED(hr)) { pTIP->SetWindow(m_hWnd); pTIP->SetInPlaceVisibility(IVF_OFF); } }
- 应用退出时记得释放COM资源:
// 在CWinApp::ExitInstance中添加 CoUninitialize();
方案3:正确设置INPUT_SCOPE_NONE属性
若之前尝试InputScope方案无效,可能是设置方式有误,正确的做法是在控件创建后调用以下函数:
HRESULT SetInputScopeNone(HWND hWnd) { CComPtr<IInputScope> pInputScope; HRESULT hr = CoCreateInstance(CLSID_InputScope, NULL, CLSCTX_INPROC_SERVER, IID_IInputScope, (void**)&pInputScope); if (SUCCEEDED(hr)) { hr = pInputScope->SetInputScope(IS_NONE); if (SUCCEEDED(hr)) { hr = SetProp(hWnd, L"InputScope", pInputScope); } } return hr; } // 在OnInitDialog等控件初始化完成的位置调用 SetInputScopeNone(m_myComboBox.m_hWnd); SetInputScopeNone(m_myListBox.m_hWnd);
关于"点击CEdit后问题消失"的说明
这个现象是因为CEdit控件的UI自动化属性完全符合文本输入控件标准,系统会自动调整当前应用的触摸键盘触发规则,直到应用重启。上述方案从根源修正了非输入控件的属性,无需依赖CEdit的临时修复效果。
内容的提问来源于stack exchange,提问作者PizzaToCode
相关产品推荐
相关产品推荐

