发送WM_SETTEXT时如何避免EN_CHANGE通知?

问题描述 投票:0回答:7

我有一个 CEdit 派生控件,当基础数据为空时,该控件显示字符串“N/A”。我最近添加了代码,以在控件获得焦点时清空控件(SetWindowText(“”);),并在用户离开焦点时将其设置回“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
7个回答
10
投票

我以前做过的方式(上次,大概是 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 不提供一种方法来区分用户输入和代码更改,但事实就是如此。


6
投票

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

首先,我在派生控件的头文件中添加了一个标志,并在构造函数中将其初始化为 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;

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


1
投票

您可以在发送

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

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


1
投票

以下代码使用 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);
}

0
投票
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;
}

0
投票

我的解决方案。它基于 MFC,但可以轻松更改为原始 Window API。

标题:

#pragma once

class CMyEdit : public CEdit
{
public:

    using TBase = CEdit;

    CMyEdit();
    virtual ~CMyEdit();

    // Returns true when current text change operation is executed from WM_SETTEXT\SetWindowText
    bool IsSetTextChange() const { return m_isSetTextChange; }

    DECLARE_SERIAL(CMyEdit)

protected:

    LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam) override;

private:

    bool m_isSetTextChange;
};

来源:

#include "stdafx.h"
#include "Edit.h"

IMPLEMENT_SERIAL(CMyEdit, CMyEdit::TBase, 1)

CMyEdit::CMyEdit()
    : m_isSetTextChange(0)
{
}

CMyEdit::~CMyEdit()
{
}

LRESULT CMyEdit::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message == WM_SETTEXT)
    {
        m_isSetTextChange = true;
        const auto res = TBase::WindowProc(message, wParam, lParam);
        m_isSetTextChange = false;

        return res;
    }

    return TBase::WindowProc(message, wParam, lParam);
}

在您的windows\dialogs中使用CMyEdit而不是CEdit,您可以随时通过

myEdit.IsSetTextChange();
检查EN_CNANGE通知的原因。


-1
投票

万一其他人发现此讨论...

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

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

© www.soinside.com 2019 - 2024. All rights reserved.