EDIT控件实现只读模式
先说三种使EDIT控件实现只读模式的简单方式,然后由于这三种方式不能满足要求,最后又引出两种稍复杂的方式。这三种简单方式如下:
1、设置EDIT控件窗口风格,使窗口风格中含有ES_READONLY属性。
::SetWindowLong(hEdit,GWL_STYLE,oldstyle|ES_READONLY);
2、向EDIT控件窗口发送EM_SETREADONLY 消息。
::SendMessage(hEdit,EM_SETREADONLY,TRUE,0);
3、使用EnableWindow函数禁用EDIT控件窗口
::EnableWindow(hEdit,FALSE);
但是以上三种简单方式的效果差不多,虽然实现了只读,但都会使用EDIT控件窗口变灰。下面介绍两种可以让EDIT控件实现只读同时又不变灰的方法:
第一种,借助父窗口让EDIT控件实现只读同时又不变灰的方法。
这种方法要借助上面介绍的三种简单方式先把EDIT控件改为只读,然后再让EDIT控件保持不灰。只读模式的EDIT控件不再向父窗口发送WM_CTLCOLOREDIT通知,而是改为发送WM_CTLCOLORSTATIC通知。
父窗口可以在对WM_CTLCOLOREDIT和WM_CTLCOLORSTATIC通知的响应函数中设置EDIT控件的背景和文本颜色为任意值。
BOOL MyDialog::OnEditCtrlColor(WPARAM wParam,LPARAM lParam)
{
//设置红色的文本
::SetTextColor((HDC)wParam,RGB(255,0,0));
//设置黑色的背景
::SetBkColor((HDC)wParam,RGB(0,0,0));
//返回背景色对应的刷子句柄
::SetWindowLong(this->Handle(),DWL_MSGRESULT,::CreateSolidBrush(clr));
return TRUE;
}
第二种,通过子类化EDIT控件实现只读同时又不变灰的方法。
STEP1、派生自己的EDIT控件类edit。
派生的目的是实现对EDIT控件的子类化。如果你使用的基类不能自动子类化EDIT控件,那就需要你在Create之后自己调用SetWindowLong来子类化。这里使用的基类rubbish::window虽然会自动子类化EDIT控件,但派生类还需要重载rubbish::window::OnWndMsg函数才能实现对改变EDIT控件内容的行为拦截。
class edit:public rubbish::window
{
BOOL m_isReadOnly;
......
public:
void setReadOnley(BOOL isReadOnly){m_isReadOnly = isReadOnly;}
private:
//重载rubbish::window::OnWndMsg函数
virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult);
};
SETP2、重载OnWndMsg函数,如果控件处于只读模式,就阻止可以更改控件内容的动作。
BOOL edit::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
if(!m_isReadOnly)
{
//如果控件未处于只读模式,就返回FALSE,交由控件的原生过程函数处理
return FALSE;
}
//下面是控件处于只读模式时的代码逻辑
......
}
需要拦截的动作有三种,第一种来自系统菜单:

鼠标在EDIT控件窗口上单击右键,就会出现上图菜单。 菜单中的“复制”和“全选”不会改变控件内容,"剪切"、“粘贴”、“删除”等却会改变控件内容,它们对应着WM_CUT、WM_PASTE等系统命令。所以重载后的OnWndMsg函数绝不能让EDIT控件的原生过程处理这些命令。代码部分如下:
BOOL edit::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
if(!m_isReadOnly)
{
//如果控件未处于只读模式,就返回FALSE,交由控件的原生过程函数处理
return FALSE;
}
//下面是控件处于只读模式时的代码逻辑
switch(message)
{
case WM_CUT:
case WM_PASTE:
case WM_CLEAR:
case WM_UNDO:
return TRUE;//阻止命令菜单
break;
......
}
}
第二种需要拦截的行为是来自DELETE键。当DELETE键被按下时,系统会向控件发送WM_KEYDOWN消息,同时wParam中包含着VK_DELETE虚拟键。
BOOL edit::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
......
//下面是控件处于只读模式时的代码逻辑
switch(message)
{
......
case WM_KEYDOWN:
switch(wParam)
{
case VK_DELETE:
return TRUE;//阻止删除键
break;
default:
break;
}
break;
......
}
}
第三种是对WM_CHAR行为的拦截。但是并非所有的WM_CHAR行为都可以拦截,例如,当CTRL功能键被按下时如果不允许控件的原生过程处理的话,WM_PASTE、WM_COPY这些消息就不会产生。只读模式只是不允许改变控件内容,但复制控件的内容应该是允许的。所以还必须产生WM_COPY才行!
BOOL edit::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
......
//下面是控件处于只读模式时的代码逻辑
switch(message)
{
......
case WM_CHAR:
if (::GetKeyState(VK_CONTROL) >= 0 )
{
return TRUE;//阻止通过键盘敲入字符
}
break;
......
}
}
经过这样的重载之后,EDIT控件已经实现了不变灰的只读模式。使用这个控件类,只需要调用它的void setReadOnley(BOOL isReadOnly)函数就可以在只读模式和正常模式之间切换,比依赖父窗口的方案更适合封装成类库。这正是rubbish的实现方式。
下面是使用过程中遇到的一个问题,解决之后发现是一个误会,但还是值得注意:
EDIT控件只读模式的实践问题

(https://download.youkuaiyun.com/download/freex64/21081882)
上图界面是一个基于MDI框架的小工具,在文档区的左边有一些EDIT控件用来输入一些参数。但是当像上图那样选中“FreeX64”然后按下CTRL+C时却无法把选中的“FreeX64”放到粘贴板里去。一开始还以为是重载后的OnWndMsg函数代码有问题,后来才发现原来是应用程序的资源里有一个“加速键表”,而这个“加速键表”里已经有一个CTRL+C了。因此CTRL+C被rubbish::MDIFrame拦截而没有发往控件。
解决办法就是:在rubbish::MDIFrame::PreTranslateMessage里先判断当前键盘焦点在哪个窗口里,若是rubbish::MDIFrame不关心的窗口,就交由默认函数处理过程!完美解决,下面是具体代码:
BOOL MDIFrame::PreTranslateMessage(MSG* lpMsg)
{
if ( TranslateMDISysAccel (this->m_Client.Handle(), lpMsg) != FALSE)
{
return TRUE;
}
rubbish::Window * activeViewPtr = this->GetActiveView();
if(activeViewPtr)
{
HWND const hFoucsWnd = ::GetFocus();
if(hFoucsWnd == NULL || ::IsChild(activeViewPtr->Handle(),hFoucsWnd) != 0 )
{
if(activeViewPtr->PreTranslateMessage(lpMsg)!= FALSE)
{
return TRUE;
}
}
}
return RBSFrame::PreTranslateMessage(lpMsg);
}
本文介绍了EDIT控件实现只读模式的五种方法,包括设置ES_READONLY风格、发送EM_SETREADONLY消息、禁用编辑框、借助父窗口和子类化EDIT控件。重点讲述了后两种方法,如何让EDIT控件在只读状态下保持不灰,并详细说明了子类化EDIT控件的实现步骤和问题解决。
1160

被折叠的 条评论
为什么被折叠?



