【问题标题】:How to avoid EN_CHANGE notifications when sending WM_SETTEXT?发送 WM_SETTEXT 时如何避免 EN_CHANGE 通知?
【发布时间】:2012-07-04 01:43:46
【问题描述】:

我有一个 CEdit 派生控件,当 undelying 数据为空时,它会显示字符串“N/A”。我最近添加了代码来清空控件(SetWindowText("");) 当它获得焦点并设置 if back to "N/A"(SetWindowText("N/A")) 当焦点丢失时如果用户离开控件为空。

唯一的问题是,将窗口文本设置为“”或“N/A”会触发 EN_CHANGE,所以我的对话框认为数据已更改。

如何避免在调用 SetWindowText (WM_SETTEXT) 时触发 EN_CHANGE?

注意事项

-我知道我可以将编辑控件设置为 Multiline=TRUE,但这对我来说是不可接受的。

-我的应用程序是 MBCS,所以我不能使用 SetCueBanner

-我想要一个优雅的解决方案。将父窗口临时设置为 NULL 并不是一个优雅的解决方案。

编辑:

-我希望解决方案在我的自定义控件中,而不是在每个对话框中

谢谢

【问题讨论】:

    标签: c++ visual-studio user-controls mfc


    【解决方案1】:

    我以前做过的方式(上次,比如 20 分钟前;实际上我正在考虑问同样的问题),是设置一个标志。当我要以编程方式设置文本时,我设置了标志,并在 EN_CHANGE 处理程序中检查它:

    void CMyDialog::MyFunction()
    {    
        setEditTextProgramatically = true;  
        c_Edit.SetWindowText(_T("Whatever"));  
        setEditTextProgramatically = false;
    }
    
    void CMyDialog::OnEnChangeEdit()
    {
        if (!setEditTextProgramatically)
        {
            // Do whatever you need to do
        }
    }
    

    我知道这不是最优雅的解决方案,但它确实有效,至少对我而言。

    我一直想知道为什么 MFC 没有提供一种方法来区分用户输入和代码更改,但事实就是这样。

    【讨论】:

    • 我有类似的解决方案来解决 MFC 通知问题。当您有通知以递归方式调用通知时,它会变得非常有趣。
    • 发送消息的过程并不是真正的 MFC。它是发送消息的底层 Win32“编辑”控件。 CEdit 只是它的一个包装器。
    • @VanDarg 是的,当通知调用通知时,事情会变得有趣。在这种情况下,可能很难控制是用户输入还是导致通知的代码。但是对于简单的情况,这个解决方案是可行的。
    • @JoeWillcoxson 没错,发送消息的是 Win32。但也许,如果 MFC 是一个“更厚”的包装器,他们可能会添加更多信息。无论如何,我经常希望这种信息是可用的,无论是谁提供的。
    • 另请参阅 Joseph Newcomer 撰写的 Avoiding EN_CHANGE notifications,2006 年。
    【解决方案2】:

    我终于找到了适合我的问题的解决方案。

    首先,我在派生控件的头文件中添加了一个标志,并在构造函数中将其初始化为 false

    bool m_bNoEnChange;
    

    我在派生控件的头文件中覆盖了 OnChildNotify,在实现中,我使用 EN_CHANGE 参数检查了 WM_COMMAND 消息。然后我返回 TRUE 以防止将消息发送到父级(对话框/页面)

    virtual BOOL OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult);
    

    BOOL CADEdit::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult) 
    {
        if(message == WM_COMMAND && HIWORD(wParam) == EN_CHANGE)
        {
            //If the flag is set, don't send the message to the parent window
            if(m_bNoEnChange)
                return TRUE;
        }
    
        return CEdit::OnChildNotify(message, wParam, lParam, pLResult);
    }
    

    最后,当控件获得和失去焦点时,我用我的标志包装了有问题的 SetWindowText

    m_bNoEnChange = true;
    SetWindowText(_T(""));
    m_bNoEnChange = false;
    

    这个解决方案对我来说是最好的,因为我不必修改每个对话框。

    【讨论】:

    • 使用UpdateData,你不会再收到消息了。
    • UpdateData 是对话框级函数,而不是 CEdit 函数。
    • 我认为你误解了这个问题。我的问题来自 CEdit 控件内部调用的代码。我不能从那里调用 UpdateData。查看 UpdateData 文档。 msdn.microsoft.com/en-us/library/t9fb9hww(v=vs.80).aspx
    • 此外,UpdateData 从对话框中检索数据并将其传输到变量。就我而言,我不希望传输数据。
    【解决方案3】:

    您可以在发送WM_SETTEXT 之前禁用(EnableWindow(FALSE) 或使用参数FALSE 发送WM_ENABLE 控件,然后再启用它。那应该防止EN_CHANGE

    可能还有一些更优雅的方法:p

    【讨论】:

    • 禁用编辑控件并没有阻止我的 EN_CHANGE... 使用 Windows 7 64 位
    【解决方案4】:

    以下代码使用 C++ 11 功能,但可以轻松更改。

    标题

    // CEditOptionalNotify.h
    //
    // CEdit derived class allowing the control's text value to be
    // set without (optionally) causing EN_CHANGE processing.
    //
    #pragma once
    
    class CEditOptionalNotify : public CEdit
    {
        //DECLARE_DYNAMIC(CEditOptionalNotify)
        // Enable use of RUNTIME_CLASS macro and CObject::IsKindOf()
    
    public:
    
        CEditOptionalNotify();
        virtual ~CEditOptionalNotify();
    
        enum class PerformOnChangeProcessing { No, Yes };
        void vSetText(const TCHAR* pText, PerformOnChangeProcessing e);
    
    protected:
    
        afx_msg BOOL bConsiderEnChangeAsHandled();
    
        bool m_bChangeNotificationsEnabled;
    
        DECLARE_MESSAGE_MAP()
    };
    

    实施

    // EditOptionalNotify.cpp : implementation file
    //
    
    #include "stdafx.h"
    #include <EditOptionalNotify.h>
    
    //IMPLEMENT_DYNAMIC(CEditOptionalNotify, CEdit)
    
    CEditOptionalNotify::CEditOptionalNotify() :
        m_bChangeNotificationsEnabled(true)
    { 
    }
    
    CEditOptionalNotify::~CEditOptionalNotify()
    {
    }
    
    
    BEGIN_MESSAGE_MAP(CEditOptionalNotify, CEdit)
        ON_CONTROL_REFLECT_EX(EN_CHANGE, bConsiderEnChangeAsHandled)
    END_MESSAGE_MAP()
    
    
    BOOL CEditOptionalNotify::bConsiderEnChangeAsHandled()
    {
        return (m_bChangeNotificationsEnabled ? FALSE : TRUE);
    }
    
    
    void CEditOptionalNotify::vSetText(const TCHAR* pText, PerformOnChangeProcessing e)
    {
        bool bChangeNotificationsDesired = (PerformOnChangeProcessing::No == e ? false : true);
    
        if (bChangeNotificationsDesired != m_bChangeNotificationsEnabled)
        {
            m_bChangeNotificationsEnabled = bChangeNotificationsDesired;
            CEdit::SetWindowText(pText);
            m_bChangeNotificationsEnabled = (bChangeNotificationsDesired ? false : true);
        }
        else
            CEdit::SetWindowText(pText);
    }
    

    【讨论】:

    • 我喜欢你的代码中的 ON_CONTROL_REFLECT 部分,这似乎比我的 OnChildNotify 部分更简单。有什么困扰我,如果不使用 vSetText 方法,你的控件会起作用吗? (如在 DoDataExchange 中,在 SetWindowText 中)
    【解决方案5】:
    LRESULT CMainDlg::OnEnUpdateEditID(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    {   
        //using static variable
        static bool isCodeChangeText = false;
        if(isCodeChangeText) 
            return 0;
    
        ……//Deal Window Text
    
        if(old == new)
            return 0; 
    
        int nSel = m_editPID.GetSel();//record cursor pos
    
        isCodeChangeText = true;
        m_editID.SetWindowText(new);
        m_editID.SetSel(nSel);
        isCodeChangeText = false;
    
        return 0;
    }
    

    【讨论】:

      【解决方案6】:

      万一其他人发现这个讨论......

      正如 Steven 所写,UpdateData 不会导致发送 EN_CHANGE。

      在底层 MFC 调用 AfxSetWindowText 可以指定一个 hwnd。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-25
        • 1970-01-01
        • 2018-05-16
        • 1970-01-01
        • 2021-10-28
        • 1970-01-01
        • 2017-01-01
        • 2021-06-28
        相关资源
        最近更新 更多