我有一个 CEdit 派生控件,当基础数据为空时,该控件显示字符串“N/A”。我最近添加了代码,以在控件获得焦点时清空控件(SetWindowText(“”);),并在用户离开焦点时将其设置回“N/A”(SetWindowText(“N/A”))控制空。
唯一的问题是,将窗口文本设置为“”或“N/A”会触发EN_CHANGE,因此我的对话框认为数据已更改。
如何避免在调用 SetWindowText (WM_SETTEXT) 时触发 EN_CHANGE?
注释
-我知道我可以将编辑控件设置为 Multiline=TRUE,但这对我来说是不可接受的。
-我的应用程序是 MBCS,所以我无法使用 SetCueBanner
-我想要一个优雅的解决方案。暂时将父窗口设置为 NULL 并不是一个优雅的解决方案。
编辑:
-我希望解决方案位于我的自定义控件中,而不是位于每个对话框中
谢谢
我以前做过的方式(上次,大概是 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 不提供一种方法来区分用户输入和代码更改,但事实就是如此。
我终于找到了合适的解决方案来解决我的问题。
首先,我在派生控件的头文件中添加了一个标志,并在构造函数中将其初始化为 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;
这个解决方案对于我来说是最好的,因为我不必修改每个对话框。
您可以在发送
(EnableWindow(FALSE)
之前禁用 WM_ENABLE
或使用参数 FALSE 发送 WM_SETTEXT
)控件,然后再启用它。这应该可以防止EN_CHANGE
可能有一些更优雅的方法:p
以下代码使用 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);
}
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;
}
我的解决方案。它基于 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通知的原因。
万一其他人发现此讨论...
正如 Steven 所写,UpdateData 不会导致发送 EN_CHANGE。
在底层,MFC 调用 AfxSetWindowText,可以使用它指定一个 hwnd。