玩duilib的时候在网上看到其余文章在duilib中嵌入MFC的例子,屡试不行,总是显示不出MFC控件或者win32控件,后来本身研究了一番,发现问题所在,并拿出来分享一下。 ide
首先duilib是什么这里很少说,自行百度。这个例子效果是这样的,在dui界面中能够嵌入win32的按钮,以下图:函数
点击win32按钮能够响应消息:ui
至于网上其余教程我试验不灵光的缘由是,duilib中使用UpdateLayeredWindow来支持窗口的透明效果,就是背景图片是个带透明通道的png图片的时候,就是有半透明渐变阴影的时候设置皮肤文件的属性bktrans:<Window size="544,394" caption="22,22,22,50" roundcorner="5,5" bktrans="true">this
只要这个是true,添加到主窗口的子窗口都隐藏不可见了。code
暂时想到的办法就是建立一个以桌面为父窗口的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"); }
咱们在构造函数里为本身建立一个窗口it
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一个就是了,皮肤文件里面<FrameWnd name="test" mouse="false" float="true" pos="100,100,0,0" width="200" height="200" />就是这样便可
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; }