当拼写检查下划线出现时,丰富的编辑控件发送 EN_CHANGE

如何解决当拼写检查下划线出现时,丰富的编辑控件发送 EN_CHANGE

假设您刚刚在启用拼写检查的 Rich Edit 控件中设置了一些文本,并且该文本有一些拼写错误。一秒钟过去了,拼写检查将开始,然后拼写错误的文本将带有下划线。但是你猜怎么着:Rich Edit 控件实际上会为下划线事件发送一个 /_ah/warmup 通知(假设您已经通过执行 EN_CHANGE 注册通知)。

是否有解决方法可以避免出现此类行为?我有一个带有一些启用拼写检查的富编辑控件的对话框。而且我还想知道何时发生了编辑事件,因此我知道何时启用“保存”按钮。因此,仅针对拼写检查下划线事件获取 SendMessage(hwnd,EM_SETEVENTMASK,(LParaM)ENM_CHANGE) 通知一个问题。

我考虑过的一个选项是完全禁用 EN_CHANGE 通知,然后在子类 Rich Edit 控件中自行触发它们。例如,当有 EN_CHANGE 时,它会显式发送 WM_CHAR 通知等。但这似乎是一个问题,因为有许多类型的事件应该触发更改,例如删除、复制/粘贴等,我可能无法正确捕获所有这些。

我考虑过的另一个选项是动态启用和禁用 EN_CHANGE 通知。例如,仅在有焦点时启用它们,并在焦点被杀死时禁用它们。但这似乎也有问题,因为在设置文本时,丰富的编辑可能已经具有焦点。然后会发生拼写检查下划线,并且会发送不需要的 EN_CHANGE 通知

我想也可以使用计时器,但我认为这很容易出错。

有人有其他想法吗?

这是一个可重现的示例。只需运行它,它就会说一些改变了:

EN_CHANGE

此外,使用 #include <Windows.h> #include <atlbase.h> #include <atlwin.h> #include <atltypes.h> #include <Richedit.h> class CMyWindow : public CWindowImpl<CMyWindow,CWindow,CWinTraits<WS_VISIBLE>> { public: CMyWindow() { } BEGIN_MSG_MAP(CMyWindow) MESSAGE_HANDLER(WM_CREATE,OnCreate) COMMAND_CODE_HANDLER(EN_CHANGE,OnChange) END_MSG_MAP() private: LRESULT OnCreate(UINT,WParaM,LParaM,BOOL& bHandled) { bHandled = FALSE; LoadLibrary(L"Msftedit.dll"); CRect rc; GetClientRect(&rc); m_wndRichEdit.Create(MSFTEDIT_CLASS,m_hWnd,&rc,NULL,WS_VISIBLE | WS_CHILD | WS_BORDER); INT iLangOpts = m_wndRichEdit.SendMessage(EM_GETLANGOPTIONS,NULL); iLangOpts |= IMF_SPELLCHECKING; m_wndRichEdit.SendMessage(EM_SETLANGOPTIONS,(LParaM)iLangOpts); m_wndRichEdit.SetwindowText(L"sdflajlf adlfjldsfklj dfsl"); m_wndRichEdit.SendMessage(EM_SETEVENTMASK,(LParaM)ENM_CHANGE); return 0; } LRESULT OnChange(WORD,WORD,HWND,BOOL&) { MessageBox(L"changed",NULL); return 0; } private: CWindow m_wndRichEdit; }; int APIENTRY wWinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPWSTR lpCmdLine,_In_ int nCmdshow) { CMyWindow wnd; CRect rc(0,200,200); wnd.Create(NULL,&rc); MSG msg; while (GetMessage(&msg,nullptr,0)) { TranslateMessage(&msg); dispatchMessage(&msg); } return (int)msg.wParam; } EM_SETMODIFY 似乎也无济于事。我猜拼写检查下划线的结果是 EM_GETMODIFY,因此在处理程序中检查该标志无济于事。

解决方法

使用 EM_CANUNDO(也可能是 EM_CANREDO)来验证内容是否已更改。我希望拼写检查器不要添加任何撤消信息。

,

因为关于 CHANGENOTIFY 的文档(必须包含与 EN_CHANGE 通知代码相关的信息,但不是..)是错误的 - 只有研究存在。

在我的测试中,我认为与拼写检查相关的 EN_CHANGE 仅在富编辑处理 WM_TIMER 消息时收到。所以解决方案是下一个-子类richedit并记住(保存在类成员变量中)-当它在WM_TIMER中时。比,当我们处理 EN_CHANGE 时 - 检查 WM_TIMER 内的 Richedit。

部分 POC 代码。我特别展示了更复杂的情况 - 如果框架或对话框窗口中存在多个(不止一个)子 Richedit

#include <richedit.h>

class RichFrame : public ZFrameMultiWnd
{
    enum { richIdBase = 0x1234 };
    bool _bInTimer[2] = {};

public:
protected:
private:
    static LRESULT WINAPI SubclassProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam,UINT_PTR uIdSubclass,DWORD_PTR dwRefData)
    {
        if ((uIdSubclass -= richIdBase) >= _countof(_bInTimer))
        {
            __debugbreak();
        }

        bool bTimerMessage = uMsg == WM_TIMER;
        
        if (bTimerMessage)
        {
            reinterpret_cast<RichFrame*>(dwRefData)->_bInTimer[uIdSubclass] = TRUE;
        }
        
        lParam = DefSubclassProc(hWnd,uMsg,wParam,lParam);

        if (bTimerMessage)
        {
            reinterpret_cast<RichFrame*>(dwRefData)->_bInTimer[uIdSubclass] = false;
        }

        return lParam;
    }

    virtual BOOL CreateClient(HWND hWndParent,int nWidth,int nHeight,PVOID /*lpCreateParams*/)
    {
        UINT cy = nHeight / _countof(_bInTimer),y = 0;

        UINT id = richIdBase;
        ULONG n = _countof(_bInTimer);

        do 
        {
            if (HWND hwnd = CreateWindowExW(0,MSFTEDIT_CLASS,WS_CHILD|ES_MULTILINE|WS_VISIBLE|WS_BORDER,y,nWidth,cy,hWndParent,(HMENU)id,0))
            {
                SendMessage(hwnd,EM_SETLANGOPTIONS,SendMessage(hwnd,EM_GETLANGOPTIONS,0) | IMF_SPELLCHECKING);

                SetWindowText(hwnd,L"sdflajlf adlfjldsfklj d");
                SendMessage(hwnd,EM_SETEVENTMASK,ENM_CHANGE);

                if (SetWindowSubclass(hwnd,SubclassProc,id,reinterpret_cast<ULONG_PTR>(this)))
                {
                    continue;
                }
            }

            return FALSE;

        } while (y += cy,id++,--n);
        
        return TRUE;
    }

    virtual LRESULT WindowProc(HWND hwnd,LPARAM lParam)
    {
        switch (uMsg)
        {
        case WM_COMMAND:
            if (EN_CHANGE == HIWORD(wParam))
            {
                if ((wParam = LOWORD(wParam) - richIdBase) >= _countof(_bInTimer))
                {
                    __debugbreak();
                }
                
                DbgPrint("EN_CHANGE<%x> = %x\n",_bInTimer[wParam]);
            }
            break;

        case WM_DESTROY:
            {
                UINT id = richIdBase;
                ULONG n = _countof(_bInTimer);
                do 
                {
                    RemoveWindowSubclass(GetDlgItem(hwnd,id),id);
                } while (id++,--n);
            }
            break;

        case WM_NCDESTROY:
            PostQuitMessage(0);
            break;
        }
        return __super::WindowProc(hwnd,lParam);
    }
};
,

我最近尝试在没有子类化的情况下解决这个问题,但它只是有点成功。

我的替代解决方法包括将整个文档标记为 CFE_PROTECTED。在 EN_PROTECTED 处理程序中,当来自拼写检查器时,ENPROTECTED::msgWM_NULL,您可以设置一个标志,告诉自己忽略下一个 EN_CHANGE。要允许从上下文菜单中进行实际拼写更正,您还需要跟踪菜单。

这感觉相当脆弱,但跟踪 WM_TIMER 也是如此。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?