EDIT控件只读模式及实践问题

本文介绍了EDIT控件实现只读模式的五种方法,包括设置ES_READONLY风格、发送EM_SETREADONLY消息、禁用编辑框、借助父窗口和子类化EDIT控件。重点讲述了后两种方法,如何让EDIT控件在只读状态下保持不灰,并详细说明了子类化EDIT控件的实现步骤和问题解决。

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); 
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值