为何WS_TABSTOP在Rich Edit控件上无法生效?
Rich Edit控件Tab焦点切换失效的解决方案
我来帮你搞定这个Tab键切换焦点的问题——你遇到的情况其实是Rich Edit控件的默认行为导致的:它会把Tab键当成输入字符(插入制表符)来处理,而不是让父窗口的IsDialogMessage函数接管这个消息来完成焦点切换,这也是换成普通WC_EDIT控件就正常的原因。
问题根源
普通的编辑控件(WC_EDIT)在默认情况下,当按下Tab键时会把消息传递给父窗口,让对话框的焦点切换逻辑(也就是IsDialogMessage)处理。但Rich Edit控件为了支持文本编辑中的制表符输入,默认会拦截Tab键,导致焦点切换失效。
最简单的解决方案:设置ECO_NOTABINDING选项
你只需要在创建Rich Edit控件后,发送EM_SETOPTIONS消息,禁用它的Tab键绑定功能,这样Tab键就会被交给父窗口处理焦点切换了。
修改你的代码,在创建richEditControl之后添加这一行:
SendMessage(richEditControl, EM_SETOPTIONS, ECOOP_SET, ECO_NOTABINDING);
修改后的完整代码
#include <windows.h> #include <commctrl.h> #include <richedit.h> #pragma comment(lib, "comctl32.lib") #pragma comment(linker,"\"/manifestdependency:type='win32' \ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { LoadLibrary(TEXT("msftedit.dll")); WNDCLASSEX mainwcex; mainwcex.cbSize = sizeof(WNDCLASSEX); mainwcex.style = CS_HREDRAW | CS_VREDRAW; mainwcex.lpfnWndProc = WindowProc; mainwcex.cbClsExtra = 0; mainwcex.cbWndExtra = 0; mainwcex.hInstance = hInstance; mainwcex.hIcon = NULL; mainwcex.hCursor = (HICON) LoadCursor(NULL, IDC_ARROW); mainwcex.hbrBackground = NULL; mainwcex.lpszMenuName = NULL; mainwcex.lpszClassName = "mainwindow"; mainwcex.hIconSm = NULL; RegisterClassEx(&mainwcex); HWND mainWindow = CreateWindowEx( NULL, "mainwindow", NULL, WS_OVERLAPPEDWINDOW, 100, 100, 600, 400, NULL, NULL, hInstance, NULL); HWND richEditControl = CreateWindowEx( NULL, "RICHEDIT50W", "Rich Edit", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 50, 50, 100, 25, mainWindow, NULL, hInstance, NULL); // 添加这一行,禁用Rich Edit的Tab键绑定 SendMessage(richEditControl, EM_SETOPTIONS, ECOOP_SET, ECO_NOTABINDING); HWND button1 = CreateWindowEx( NULL, WC_BUTTON, "Button1", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 200, 50, 100, 25, mainWindow, NULL, hInstance, NULL); HWND button2 = CreateWindowEx( NULL, WC_BUTTON, "Button2", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 350, 50, 100, 25, mainWindow, NULL, hInstance, NULL); ShowWindow(mainWindow, nCmdShow); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { if (!IsDialogMessage(mainWindow, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return static_cast<int>(msg.wParam); } LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_DESTROY: PostQuitMessage(0); return 0; break; } return DefWindowProc(hWnd, uMsg, wParam, lParam); }
另一种方案:处理WM_GETDLGCODE消息
如果你不想用EM_SETOPTIONS,也可以通过修改Rich Edit控件的WM_GETDLGCODE返回值来实现。这个方法需要子类化Rich Edit控件,让它告诉父窗口自己不需要处理Tab键:
- 定义一个全局变量保存原来的窗口过程:
WNDPROC oldRichEditProc;
- 添加子类窗口过程:
LRESULT CALLBACK RichEditSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_GETDLGCODE) { // 获取原来的返回值,移除DLGC_WANTTAB标志 LRESULT result = CallWindowProc(oldRichEditProc, hWnd, uMsg, wParam, lParam); return result & ~DLGC_WANTTAB; } return CallWindowProc(oldRichEditProc, hWnd, uMsg, wParam, lParam); }
- 在创建Rich Edit控件后子类化它:
oldRichEditProc = (WNDPROC)SetWindowLongPtr(richEditControl, GWLP_WNDPROC, (LONG_PTR)RichEditSubclassProc);
两种方法都能解决你的问题,推荐第一种,因为更简洁直观。
内容的提问来源于stack exchange,提问作者krsi
相关产品推荐
相关产品推荐

