【MFC】实现子对话框透明并锚定于父对话框

需求

在父对话框下新创建一个子窗体,要求:
1.子对话框没有边框,完全透明,文字颜色为白色;
2.子对话框的初始位置在父对话框右下角;
3.子对话框锚定于父对话框,父对话框移动时子对话框随之一起移动,视觉上融为一体。

方案一 WS_CHILD类型对话框

实现方式

1.设置子对话框属性

在属性页中将子对话框的style属性设置为Child(默认是POPUP),此时查看.rc资源文件会发现该子对话框的属性变为WS_CHILD。

此时会发现原本与主窗体独立的子窗体与父窗体已经锚定,移动父窗体子窗体会跟着移动。但通过m_pChildDlg->GetParent() 函数发现此时子窗体的父窗体还不是原本的主窗体,调用 GetForegroundWindow() 发现此时m_pChildDlg的parent是这个获取的前台窗体,但这个前台窗体究竟是什么目前还不知道,猜测系统会将设置为child类型的子窗体赋给某个顶层窗体,这个窗体是什么还需要求证。
链接: 父窗体与子窗体的关系这篇文章
链接: 不同类型的窗体

2.为子对话框设置父窗体

虽然系统为子窗体指定了parent,但还是应该在程序主窗体类的初始化函数中将子窗体的父窗体指定为主窗体:

m_pChildDlg->SetParent(this)

3.指定子窗体初始位置

这一步应在父对话框的OnInitDialog()中实现,使用m_pChildDlg->MoveWindow(crect,true) 实现,crect是CRect类型,指定了子对话框的初始位置。这一步我本来使用的是m_pChildDlg->SetWindowPos(),但是在子对话框的style变为CHILD后就不起作用了,需要用MoveWindow()。

4.实现子对话框背景透明

在POPUP类型的对话框中本来使用的是SetLayeredWindowAttributes函数,但我发现一旦变成CHILD类型窗口,这一函数就不再起作用了,上网查了资料,有种说法是CHILD类型的窗口无法设置WS_EX_LAYERED属性,而要使用SetLayeredWindowAttributes函数就必须首先设置WS_EX_LAYERED属性,反正就是这条路走不通了。
后来看到一个方法,抱着试一试的态度,竟然成功了。
链接: 介绍透明子窗口的文章
我使用的是其中提到的最后一种,即重写子窗体用来通知父窗体将自己绘制成什么背景色的WM_CTLCOLOR消息,在其中返回空画刷HOLLOW_BRUSH,“空画刷就是不使用画刷,也就是不做画刷填充,不填充就是透明”。具体就是在OnCtlColor()函数中指定返回值:

return (HBRUSH)GetStockObject(HOLLOW_BRUSH)

如此一来就实现了子对话框的背景透明。
同时改变子对话框中的字体颜色也是在OnCtlColor()函数中实现。

HBRUSH ChildDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	if( IDC_STATIC_EDIT  ==  pWnd->GetDlgCtrlID())//判断发出消息的控件是否是要设置的静态文本框
	 {
	     pDC->SetTextColor(RGB(255,255,255));//设置文本颜色为白色
	}
}

添加OnCtlColor函数的方式是在子对话框中右键->类向导,选择消息选项卡,在其中找到WM_CTLCOLOR消息,然后点击右边的添加处理程序。

到此为止,就是用CHILD类型的子窗体实现了上面的3项需求,但要注意的是CHILD类型的窗体不是顶层窗口,会被父窗体上的其他控件所覆盖,因此使用时要依情况决定使用哪种窗体。

方案二 POPUP类型对话框

POPUP类型的子窗口无法直接与父窗口融为一体,移动父窗口的时候可以看到子窗口不能随之移动。因此这一点上我采取的方式是实现父窗体的WM_MOVE消息,每次移动父窗体时计算出位移,然后对子窗体调用MoveWindow函数,使之随父窗体一起移动。

1.设置子窗体属性

在属性页中将子对话框的style属性设置为Popup,此时查看.rc资源文件会发现该子对话框的属性变为WS_POPUP。

2.设置子窗体的初始位置

在父对话框的OnInitDialog()中实现
m_pChildDlg->SetWindowPos(),该函数的第一个参数选wndTop可以使之置于顶层,其他参数则指定初始位置。

3.设置子窗体透明

POPUP类型窗体可以使用SetLayeredWindowAttributes函数实现背景透明。具体方法是首先设置其属性:

SetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE,GetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE)|0x80000); 

此处0x80000 == WS_EX_LAYERED,要实现窗体透明,必须要设置其WS_EX_LAYERED扩展属性。
然后通过函数指针调用SetLayeredWindowAttributes:

MYFUNC       fun = NULL;       
//取得SetLayeredWindowAttributes函数指针           
fun=(MYFUNC)GetProcAddress(hInst,   "SetLayeredWindowAttributes");       
if (fun )
	fun(this->GetSafeHwnd(),colorkey,0,1);  //设置背景透明           
FreeLibrary(hInst); 

函数原型为

BOOL SetLayeredWindowAttributes(
    HWND hwnd,
    COLORREF crKey,
    BYTE bAlpha,
    DWORD dwFlags
);

这里注意参数4,可以取两个值LWA_COLORKEY (0x1)和 LWA_ALPHA(0x2),如下:

取值为LWA_ALPHA即等于2时,参数2无效,通过参数3决定透明度.
取值为LWA_COLORKEY即等于1时,参数3无效,参数2指定的颜色为透明色,其他颜色则正常显示,你可以在处理前先设定号窗口的背景颜色

————————————————
版权声明:本文为CSDN博主「rollingman」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接: MFC 对话框(窗口)透明
取值为LWA_ALPHA时很好理解,直接指定透明度即可,取值为LWA_COLORKEY的情况我的理解是预先设定一个背景色,然后这个函数会把这个颜色的区域都绘制成透明。
我的设置如下:

COLORREF   colorkey=14215660; 
SetBackgroundColor(colorkey);//先设置对话框背景色

我设置LWA_ALPHA时没有成功,而设置LWA_COLORKEY时只有先将背景色设置为colorkey=14215660,然后调用SetLayeredWindowAttributes时将参数2指定为同样的colorley这样才成功了,别的颜色似乎都不行,具体原因还有待深究。

4.实现父窗体的WM_MOVE消息

刚开始看解释我以为要实现WM_MOVING消息,经过实验发现WM_MOVING消息的参数,即x,y坐标在窗体移动时并不改变,反而WM_MOVE的参数会随着窗体的移动一直变化,反映了窗体客户区左上角的坐标。MSDN的文档:

WM_MOVE message
Sent after a window has been moved.
A window receives this message through its WindowProc function.
Parameters
wParam

This parameter is not used.

lParam

The x and y coordinates of the upper-left corner of the client area of the window. The low-order word contains the x-coordinate while the high-order word contains the y coordinate.

Return value
Type: LRESULT

If an application processes this message, it should return zero.
Remarks
The parameters are given in screen coordinates for overlapped and pop-up windows and in parent-client coordinates for child windows.

The following example demonstrates how to obtain the position from the lParam parameter.

Copy
xPos = (int)(short) LOWORD(lParam);   // horizontal position 
yPos = (int)(short) HIWORD(lParam);   // vertical position 

说得很清楚了,值得注意的一点是,如果窗体为OverLapped或POPUP窗体,则参数中的坐标是屏幕坐标系,如果是Child类型则是由父窗体决定的客户坐标系了。
实现WM_MOVE的方法跟上面WM_CTLCOLOR消息的实现方式一样,不再赘述了。
在父窗体的OnMove()函数中保留上一次的坐标,再获取新的坐标,计算出位移量,然后据此算出子窗体的新位置,调用MoveWindow()函数,就可以获得子窗体随父窗体移动的视觉效果了。

程序退出问题

如果程序需要使用ESC键退出,则在有子窗体的时候需要一些其他的处理。
ESC键的响应函数是OnCancel(),而有子窗体后按ESC键会使得子窗体先消失(由于子窗体在前方获得了焦点),解决方法是重写子窗体的OnCancel()函数,在其中向父窗体发送WM_CLOSE消息,使得父窗体可以和子窗体一起退出。

void m_pChildDlg::OnCancel()
{
::SendMessage(m_pParent->GetSafeHwnd(),WM_CLOSE,0,0);
CDialog::OnCancel();
}

其他用到的重要知识点

What’s the difference between HWND_TOP and HWND_TOPMOST?
GetWindowLongA function
GetWindowLong函数(最好的解释资料,绝对值得一看,此函数在子类化技巧时候经常用到)
SetBkMode函数用法详解
CFont的几种构建方法
MFC获取窗口句柄的方法

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值