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