失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > mfc 子窗口 跟随 主窗口

mfc 子窗口 跟随 主窗口

时间:2021-09-22 05:44:02

相关推荐

mfc 子窗口 跟随 主窗口

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明

/logs/29644154.html

前不久做了一个MFC项目。项目做完了,客户觉得MFC的标准界面太难看。尽管之前从未做过界面美化工作,但心想这东西网上资源丰富,应该不是难事,就满口答应了。这一答应不打紧,着实让我下足了工夫。

先大致介绍一下项目界面的构成。项目界面采用MFC的对话框模式,在父窗口中镶嵌子窗口来表现程序的层次结构,而且这种镶嵌不止一级。

具体的美化资源由客户提供,一张背景图和若干按钮图片,倒也简单。

我选择先添加背景图。因为设置背景应该相对容易点,毕竟那么多按钮做下来工作量还是挺大的。

我通过IDE的向导将图片添加到资源中,资源名为IDB_BITMAP_BACKGROUND。

随后覆写了主窗口类的OnEraseBkgnd函数,将图片用于主窗口的背景。

但是写完一运行就傻眼了。背景图片只在主窗口的空余区显示出来了。占窗口大部分区域的控件和子窗口内都保持不变。这和我的预期相错太远了。这样的话,岂不是要为每个控件和子窗口写背景代码?即便采用继承方式也相当麻烦啊。

但是等等,MFC会不会提供特殊的机制来解决这个问题呢?比如说可以让子窗口(控件本质上也是子窗口)的背景继承父窗口呢?想想,还真有这种可能。于是就开始上网搜索,查看MSDN,但并未找到关于背景继承的机制,看来MFC并未提供这样的功能。

不过在找的过程中,我接触到了“窗口透明化”的信息。“透明窗口”倒很熟悉,因为之前做截图程序时用到过。仔细想来,“透明窗口”对我的设想还真有点用处。我可以将子窗口们设为透明(或者半透明),这样父窗口的背景不就显示出来了吗?而且半透明说不定能表现出更好的效果呢!

但是(又是但是),我按照想法做完之后,再次郁闷。窗口透明针对的是整个窗口,这样一来子窗口中的所有内容都是透明的了,而不仅仅是背景!

难道必须放弃透明窗口的方案吗?不甘心的我再次仔细阅读MSDN,发现分层窗口可以仅透明化指定的颜色,相关信息如下:

函数原型:

BOOL CWnd::SetLayeredWindowAttributes(COLORREF crKey,BYTE bAlpha,DWORD dwFlags);

参数解释:

crKey

Pointer to a COLORREF value that specifies the transparency color key to be used when composing the layered window. All pixels painted by the window in this color will be transparent. To generate a COLORREF, use the RGB macro.

指定用于构造分层窗口时的透明色键(用COLORREF结构描述)。窗口中颜色为指定色键的像素将被透明处理。可以用RGB宏生成COLORREF值。

对于标准的MFC窗口来说,背景颜色全都是灰色。如果将“crKey”设为这种灰色,不就可以仅透明化背景了吗?将背景图用于主窗口上,子窗口透明背景色应该可以达到预期效果,看来实现有望。

但仍有问题。一方面子窗口众多,一个个实现仍很麻烦;另一方面,主窗口上的控件颜色亦要想办法处理。

有没有一个简洁的办法呢?我有了一个大胆的想法:

1. 添加一个背景窗口,在其上显示背景图。

2. 背景窗口不显示在任务栏中,更准确的说是不能让用户觉察到背景窗口的存在。

3. 背景窗口与主窗口保持同样尺寸,重合于主窗口并紧位于其后,与主窗口同步移动。

4. 主窗口的标准背景颜色设为透明(主窗口透明化,其麾下的子窗口和控件自然跟随其透明化,省去很多麻烦)。

如此一来就能实现预期的目标了,但要增加一个窗口。这会对窗口操作带来大的影响吗?只有尝试一下了。

于是我创建了背景窗口类CbackgroundWnd,直接继承于CWnd,并覆写了它的OnEraseBkgnd函数,在其上显示背景图:

//清除背景响应

BOOL CBackgroundWnd::OnEraseBkgnd(CDC* pDC)

{

//获得客户区尺寸

CRect rect;

GetClientRect(&rect);

//加载背景位图

CBitmap bitmap;

bitmap.LoadBitmap(IDB_BITMAP_BACKGROUND);

//创建内存DC

CDC dc;

dc.CreateCompatibleDC(pDC);

//选择位图

CBitmap* pOldBitmap=dc.SelectObject(&bitmap);

//绘制位图

pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);

//恢复原有位图

dc.SelectObject(pOldBitmap);

return TRUE;

}

为了不让背景窗口显示在任务栏上,我在CbackgroundWnd类的Create函数中将其扩展窗口类型设为WS_EX_TOOLWINDOW:

//创建函数

BOOL CBackgroundWnd::Create()

{

//注册窗口类

CString className;

try

{

className=AfxRegisterWndClass(NULL);

}

catch(CResourceException* pEx)

{

pEx->Delete();

return false;

}

//创建窗口

//设置为工具窗口可以使背景窗口不显示在任务栏中

if(!CreateEx(WS_EX_TOOLWINDOW,className,NULL,WS_POPUP,0,0,1,1,NULL,NULL))

return false;

return true;

}

还有一个问题要处理。背景窗口被设计为主窗口的背景,是不允许用户直接对其操作的。本来我想背景窗口被主窗口完全覆盖,是不可能接收到用户的鼠标点击信息的。但是始料未及的是,窗口透明后鼠标指令就可以穿透窗口达到后方。于是我在CbackgroundWnd类中覆写了函数OnMouseActivate,屏蔽了鼠标激活:

//鼠标激活响应

int CBackgroundWnd::OnMouseActivate(CWnd* pDesktopWnd,UINT nHitTest,UINT message)

{

//屏蔽鼠标激活

//背景窗口位于主窗口下方,不能被激活

return MA_NOACTIVATEANDEAT;

}

这样背景窗口类就创建完成了。接下来我在主窗口类CMainDlg中定义了背景窗口成员变量m_backgroundWnd,并在CMainDlg类的OnInitDialog函数中创建了背景窗口,同时也在其中将主窗口设为背景透明:

//初始化对话框函数

BOOL CMainDlg::OnInitDialog()

{

//其它代码...

//设置分层窗口

LONG style=::GetWindowLong(GetSafeHwnd(),GWL_EXSTYLE);

style|=WS_EX_LAYERED;

style=::SetWindowLong(GetSafeHwnd(),GWL_EXSTYLE,style);

style=::GetWindowLong(GetSafeHwnd(),GWL_EXSTYLE);

//获得窗口默认背景色

COLORREF bkColor=::GetSysColor(COLOR_3DFACE);

//设置分层窗口属性

SetLayeredWindowAttributes(bkColor,100,LWA_COLORKEY);

//创建背景窗口

m_backgroundWnd.Create();

//其它代码...

return TRUE; // 除非将焦点设置到控件,否则返回TRUE

}

为了使背景窗口与主窗口保持同样尺寸,重合于主窗口并紧位于其后,与主窗口同步移动,我在CMainDlg类中创建了AdjustBackGroundWnd函数,用于调整背景窗口的尺寸和位置。该函数在需要的时候被调用:

//调整背景窗口位置

void CMainDlg::AdjustBackGroundWnd()

{

if(m_backgroundWnd.GetSafeHwnd()==NULL)

return;

//获得窗口客户区位置

CRect clientRect;

GetClientRect(&clientRect);

ClientToScreen(&clientRect);

//调整尺寸

m_backgroundWnd.SetWindowPos(this,clientRect.left,clientRect.top,clientRect.Width(),clientRect.Height(),SWP_NOACTIVATE|SWP_SHOWWINDOW);

}

该在哪些场合下调用该函数呢?起初我只想到了移动主窗口时,所以对CMainDlg类的OnMove函数进行了覆写:

//窗口移动响应

void CFlyMessagerDlg::OnMove(int x,int y)

{

AdjustBackGroundWnd();

}

这样写好后,运行程序测试,不管是窗口移动还是最小化,效果都不错,用户绝不会发觉还有一个背景窗口。但还是发现了一个问题:当窗口被其它窗口遮挡,点击窗口有效部位激活窗口时,背景窗口没有紧跟上来,导致窗口遮挡部分显示错误。不过这倒好办,我对CMainDlg类的OnActivateApp函数进行覆写,解决了这个问题:

//激活程序响应

void CFlyMessagerDlg::OnActivateApp(BOOL bActive,DWORD dwThreadID)

{

if(bActive)

{

AdjustBackGroundWnd();

}

}

再次进行测试,一切运行正常!到此为止,终于完成了背景设置工作。稍有遗憾的是,由于存在一个背景窗口,导致从最小化恢复窗口时会有较明显的闪烁现象。另外,由于将MFC的标准背景颜色设为透明色键,导致窗口其它地方出现这种颜色时也变为透明色。好在这种情况不多,总体看来无甚大碍。

对话框 建立的过程 /tutorial/mfc/mfc12.php

如果觉得《mfc 子窗口 跟随 主窗口》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。