2019独角兽企业重金招聘Python工程师标准>>>
玩duilib的时候在网上看到其他文章在duilib中嵌入MFC的例子,屡试不行,老是显示不出MFC控件或者win32控件,后来自己研究了一番,发现问题所在,并拿出来分享一下。
首先duilib是什么这里不多说,自行百度。这个例子效果是这样的,在dui界面中可以嵌入win32的按钮,如下图:
点击win32按钮可以响应消息:
至于网上其他教程我试验不灵光的原因是,duilib中使用UpdateLayeredWindow来支持窗口的透明效果,就是背景图片是个带透明通道的png图片的时候,就是有半透明渐变阴影的时候设置皮肤文件的属性bktrans:
只要这个是true,添加到主窗口的子窗口都隐藏不可见了。
暂时想到的办法就是创建一个以桌面为父窗口的window贴到dui界面上,然后再把需要添加的win32控件之类的加到这个窗口上来。
代码吭哧吭哧撸起来
首先我们这个需要是个dui控件可以支持皮肤文件编辑的,所以需要继承CControlUI,我不想像其他例子那样另外再创建window然后Attach,这样很麻烦,那么再继承自CWindowWnd就行了,那么我们的类看起来是这样子的
class CFrameWndUI : public CControlUI, public CWindowWnd
public:
CFrameWndUI();
virtual ~CFrameWndUI();
LPCTSTR GetWindowClassName() const;
protected:
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
};
GetWindowClassName就返回个窗口类名了
LPCTSTR CFrameWndUI::GetWindowClassName() const
{
return _T("CFrameWndUI");
}
我们在构造函数里为自己创建一个窗口
CFrameWndUI::CFrameWndUI()
{
// 创建一个无标题无边框的窗口
Create(NULL, _T("CFrameWndUI"), UI_WNDSTYLE_DIALOG & ~WS_CAPTION, WS_EX_TOOLWINDOW, 0, 0, 0, 0);
}
要创建无标题的窗口就不能设置WS_CAPTION,扩展样式里面WS_EX_TOOLWINDOW为了让任务栏不出现窗口图标,不设置的话,画面就是这样
重写CControlUI的 virtual void DoInit();,在里面创建3个按钮
void CFrameWndUI::DoInit()
{
// 创建win32类型的按钮
// 参数HMENU借来传递按钮ID
CreateWindow(_T("BUTTON"), _T("我是按钮1"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 0, 0, 80, 30, m_hWnd,
(HMENU)IDB_TEST1, CPaintManagerUI::GetInstance(), NULL);
CreateWindow(_T("BUTTON"), _T("我是按钮2"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 0, 40, 80, 30, m_hWnd,
(HMENU)IDB_TEST2, CPaintManagerUI::GetInstance(), NULL);
CreateWindow(_T("BUTTON"), _T("我是按钮3"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 0, 80, 80, 30, m_hWnd,
(HMENU)IDB_TEST3, CPaintManagerUI::GetInstance(), NULL);
}
接下来最重要的是根据主窗口的位置和控件大小设置窗口位置了,给类添加个私有函数AdjustRect,并且覆盖DoPaint,在DoPaint里面调用AdjustRect
class CFrameWndUI : public CControlUI, public CWindowWnd
public:
CFrameWndUI();
virtual ~CFrameWndUI();
LPCTSTR GetWindowClassName() const;
virtual void DoInit();
virtual void DoPaint(HDC hDC, const RECT& rcPaint);
private:
void AdjustRect(); // 调整自己的UI大小和坐标
};
void CFrameWndUI::AdjustRect()
{
// 获取APP客户端区域
CDuiRect rcApp;
GetWindowRect(m_pManager->GetPaintWindow(), &rcApp);
CDuiRect rcSelf(m_rcItem);
rcSelf.left += rcApp.left;
rcSelf.top += rcApp.top;
// 是否是固定坐标
if(m_bFloat)
{
rcSelf.right = m_cxyFixed.cx;
rcSelf.bottom = m_cxyFixed.cy;
}
::SetWindowPos(m_hWnd, NULL, rcSelf.left, rcSelf.top, rcSelf.right, rcSelf.bottom, SWP_NOACTIVATE);
}
void CFrameWndUI::DoPaint(HDC hDC, const RECT& rcPaint)
{
__super::DoPaint(hDC, rcPaint);
AdjustRect();
}
这里计算代码很简单,就是获取主窗口的坐标,然后加上控件坐标就是x,y了,当控件是float的时候,控件大小就是修正大小m_cxyFixed,否则就是自适应的大小。
控件如何自动创建就是新建的一个dui界面class CMainWnd : public WindowImplBase,继承自WindowImplBase就会有个virtual CControlUI* CreateControl(LPCTSTR pstrClass);里面判断控件类型,new一个就是了,皮肤文件里面
CControlUI* CMainWnd::CreateControl(LPCTSTR pstrClass)
{
if (_tcsicmp(pstrClass, _T("FrameWnd")) == 0)
{
CFrameWndUI *pUI = new CFrameWndUI();
return pUI;
}
return NULL;
}
现在运行看看,有效果了,
然后拖动主窗口看看,发现按钮并没有跟随窗口移动
这里我们需要再添加个处理,监听主窗口的WM_WINDOWPOSCHANGED消息,方法就是集成一个接口IMessageFilterUI,实现MessageHandler
class CFrameWndUI : public CControlUI, public CWindowWnd, public IMessageFilterUI
{
public:
CFrameWndUI();
virtual ~CFrameWndUI();
LPCTSTR GetWindowClassName() const;
virtual void DoInit();
virtual void DoPaint(HDC hDC, const RECT& rcPaint);
virtual LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled) override;
protected:
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
private:
void AdjustRect(); // 调整自己的UI大小和坐标
};
然后在DoInit注册消息监听
void CFrameWndUI::DoInit()
{
// 添加主窗口消息过滤器
m_pManager->AddMessageFilter(this);
...
}
LRESULT CFrameWndUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled)
{
// 判断主窗口位置改变就调整窗口
if(uMsg == WM_WINDOWPOSCHANGED)
{
AdjustRect();
}
return 0;
}
要处理win32按钮消息,就是在HandleMessage里处理WM_COMMAND消息即可
LRESULT CFrameWndUI::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lRes = 0;
BOOL bHandled = FALSE;
switch(uMsg)
{
case WM_COMMAND:
lRes = OnCommand(uMsg, wParam, lParam, bHandled);
break;
default:
break;
}
if(bHandled)
{
return lRes;
}
return __super::HandleMessage(uMsg, wParam, lParam);
}
完整代码:
#pragma once
/*!
* @file FrameWndUI.h
* @date 2017/07/14 14:33
*
* @author 阿力
*
* @brief 将win32或者MFC的控件嵌入到duilib上面
*
*/
class CFrameWndUI : public CControlUI, public CWindowWnd, public IMessageFilterUI
{
public:
CFrameWndUI();
virtual ~CFrameWndUI();
LPCTSTR GetWindowClassName() const;
virtual void DoInit();
virtual void DoPaint(HDC hDC, const RECT& rcPaint);
virtual LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled) override;
protected:
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
private:
void AdjustRect(); // 调整自己的UI大小和坐标
virtual LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
};
FrameWndUI.cpp
#include "stdafx.h"
#include "FrameWndUI.h"
static CONST INT IDB_TEST1 = 0x121;
static CONST INT IDB_TEST2 = 0x122;
static CONST INT IDB_TEST3 = 0x123;
CFrameWndUI::CFrameWndUI()
{
// 创建一个无标题无边框的窗口
Create(NULL, _T("CFrameWndUI"), UI_WNDSTYLE_DIALOG & ~WS_CAPTION, WS_EX_LAYERED | WS_EX_TOOLWINDOW, 0, 0, 0, 0);
}
CFrameWndUI::~CFrameWndUI()
{
if(m_pManager)
{
// 移除主窗口消息过滤器
m_pManager->RemoveMessageFilter(this);
}
}
LPCTSTR CFrameWndUI::GetWindowClassName() const
{
return _T("CWndUI");
}
void CFrameWndUI::DoInit()
{
// 添加主窗口消息过滤器
m_pManager->AddMessageFilter(this);
// 创建win32类型的按钮
// 参数HMENU借来传递按钮ID
CreateWindow(_T("BUTTON"), _T("我是按钮1"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 0, 0, 80, 30, m_hWnd,
(HMENU)IDB_TEST1, CPaintManagerUI::GetInstance(), NULL);
CreateWindow(_T("BUTTON"), _T("我是按钮2"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 0, 40, 80, 30, m_hWnd,
(HMENU)IDB_TEST2, CPaintManagerUI::GetInstance(), NULL);
CreateWindow(_T("BUTTON"), _T("我是按钮3"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 0, 80, 80, 30, m_hWnd,
(HMENU)IDB_TEST3, CPaintManagerUI::GetInstance(), NULL);
}
void CFrameWndUI::DoPaint(HDC hDC, const RECT& rcPaint)
{
__super::DoPaint(hDC, rcPaint);
AdjustRect();
}
LRESULT CFrameWndUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled)
{
// 判断主窗口位置改变就调整窗口
if(uMsg == WM_WINDOWPOSCHANGED)
{
AdjustRect();
}
return 0;
}
LRESULT CFrameWndUI::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lRes = 0;
BOOL bHandled = FALSE;
switch(uMsg)
{
case WM_COMMAND:
lRes = OnCommand(uMsg, wParam, lParam, bHandled);
break;
default:
break;
}
if(bHandled)
{
return lRes;
}
return __super::HandleMessage(uMsg, wParam, lParam);
}
void CFrameWndUI::AdjustRect()
{
// 获取APP客户端区域
CDuiRect rcApp;
GetWindowRect(m_pManager->GetPaintWindow(), &rcApp);
CDuiRect rcSelf(m_rcItem);
rcSelf.left += rcApp.left;
rcSelf.top += rcApp.top;
// 是否是固定坐标
if(m_bFloat)
{
rcSelf.right = m_cxyFixed.cx;
rcSelf.bottom = m_cxyFixed.cy;
}
::SetWindowPos(m_hWnd, NULL, rcSelf.left, rcSelf.top, rcSelf.right, rcSelf.bottom, SWP_NOACTIVATE);
}
LRESULT CFrameWndUI::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
switch (LOWORD(wParam))
{
case IDB_TEST1:
::MessageBox(NULL, _T("你点了按钮1"), _T(""), MB_OK);
break;
case IDB_TEST2:
::MessageBox(NULL, _T("你点了按钮2"), _T(""), MB_OK);
break;
case IDB_TEST3:
::MessageBox(NULL, _T("你点了按钮3"), _T(""), MB_OK);
break;
default:
break;
}
return 0;
}