在【2013 duilib入門簡明教程 -- 複雜控件介紹 (13)】中雖然介紹了界面設計器上的所有控件,但是還有一些控件並沒有被放到界面設計器上,還有一些常用控件duilib並沒有提供(比如菜單控件)。雖然duilib沒有提供這些控件,但是自己繪製起來也是非常方便的,不過duilib的自繪可比MFC方便得不止一點點了,其實duilib的自繪大都不用自己繪製,就是一些控件和圖片的組合而已,相當簡單方便~~~
一、菜單控件
其實菜單控件的外觀和ListBox非常像,所以我們可以用教程13的ListBox控件模擬,也可以自行用其他的組合,由於duilib自帶的Demo裏有兩個Menu,而且樣式還很不錯,所以Alberl就拿來用了,不過那幾個Demo同樣是複雜得要命,還記得教程13中的ListCtrl使用起來多麼簡單嗎?如果不覺得它簡單,那麼再對比一下duilib自帶的ListDemo,就能感覺到它的簡單啦~O(∩_∩)O~
相信看過duilib自帶的MenuDemo以及ListDemo裏面的菜單,都不會覺得菜單很簡單吧,那麼來看看下面這個菜單的實現吧~O(∩_∩)O~
1、新建一個menu.xml,如下:
<?xml version="1.0" encoding="utf-8"?><Window size="120,82"><VerticalLayout bkimage="file='Menu/menu_bk.png' corner='40,8,8,8'" hole="false"> <List header="hidden" inset="8,8,8,8" itemhotimage="file='Menu/menu_hot_bk.png' corner='2,2,2,2'"> <ListContainerElement name="menu_Open" height="22" inset="40,0,0,0"> <Label text="打開" mouse="false"/> </ListContainerElement> <ListContainerElement name="menu_Mark" height="22" inset="40,0,0,0"> <Label text="標註" mouse="false"/> </ListContainerElement> <ListContainerElement name="menu_Delete" height="22" inset="40,0,0,0"> <Label text="刪除" mouse="false"/> </ListContainerElement> </List></VerticalLayout></Window>
可以看到Menu其實就是一個List和圖片組合的,當然,親們也可以用教程13中的ListBox來替換上述內容,只不過需要自己調整一下。這裏非常感謝提供MenuDemo的大神,菜單樣式非常漂亮~O(∩_∩)O~
2、新建一個DuiMenu.h,如下(爲了方便演示,將cpp的代碼都放到了.h裏,並且減少了空行,在最後的教程裏,會有一個完整的工程下載):
#pragma once// 此處需要包含duilib的頭文件#include <UIlib.h>那一段,詳細代碼請見前面教程 class CDuiMenu : public WindowImplBase{protected: virtual ~CDuiMenu(){}; // 私有化析構函數,這樣此對象只能通過new來生成,而不能直接定義變量。就保證了delete this不會出錯 CDuiString m_strXMLPath; public: explicit CDuiMenu(LPCTSTR pszXMLPath): m_strXMLPath(pszXMLPath){} virtual LPCTSTR GetWindowClassName()const{ return _T("CDuiMenu "); } virtual CDuiString GetSkinFolder() { return _T(""); } virtual CDuiString GetSkinFile() { return m_strXMLPath; } virtual void OnFinalMessage(HWND hWnd){ delete this; } virtual LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { Close(); bHandled = FALSE; return 0; } void Init(HWND hWndParent, POINT ptPos) { Create(hWndParent, _T("MenuWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE); ::ClientToScreen(hWndParent, &ptPos); ::SetWindowPos(*this, NULL, ptPos.x, ptPos.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); } virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT lRes = 0; BOOL bHandled = TRUE; switch( uMsg ) { case WM_KILLFOCUS: lRes = OnKillFocus(uMsg, wParam, lParam, bHandled); break; default: bHandled = FALSE; } if(bHandled || m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes)) { return lRes; } return __super::HandleMessage(uMsg, wParam, lParam); }};
3、此時一個菜單控件的所有代碼就完成啦,下面接下來要顯示菜單控件,我們模仿MFC的方式,即點擊一個菜單按鈕後,彈出菜單項~
顯然第一步需要添加一個菜單按鈕,XML如下:
<Button name="btnMenu" text="選項" float="true" pos="475,28,0,0" width="37" height="19" align="center" normalimage="" hotimage="Menu/btn_menu_hot.png" pushedimage="Menu/btn_menu_hot.png" focusedimage="Menu/btn_menu_hot.png" textcolor="#FF000000" hottextcolor="#FFFFFFFF" pushedtextcolor="#FFFFFFFF" focusedtextcolor="#FFFFFFFF" bkcolor="#FFECE9D8" />
第二步就是響應菜單按鈕的點擊,在主窗口的Notify函數裏添加以下代碼:
if( msg.sType == _T("click") ) { if( msg.pSender->GetName() == _T("btnMenu") ) { POINT pt = {msg.ptMouse.x, msg.ptMouse.y}; CDuiMenu *pMenu = new CDuiMenu(_T("Menu/menu.xml")); pMenu->Init(*this, pt); pMenu->ShowWindow(TRUE); } }
還有最重要的一步就是把圖片資源解壓到exe目錄啦,下載資源猛戳這裏~(注意:此處已給出所有的資源和代碼,後面的教程將不再重複提供資源下載)
好啦,菜單按鈕是不是和MFC的很像呢,還有陰影哦~O(∩_∩)O~
(更豐富的菜單樣式請參閱duilib自帶的MenuDemo)
【菜單類小知識】
如果不用指針的方式,而直接用變量的方式顯示菜單 CDuiMenu menu(_T("Menu/menu.xml")),則不能用ShowWindow,否則會崩潰,因爲出了作用域後窗口被銷燬了,所以此時可以將CDuiMenu 定義爲成員變量、全局變量、或者靜態變量,但是做爲一個局部使用的類,這些方法顯然不怎麼好;
這時可以用ShowModal代替ShowWindow,於是就能看到窗口啦,但是卻產生了一個問題,那就是菜單窗口不會失去焦點,或者說點擊主窗口的其他區域,菜單不會消失,當然,小夥伴們可以自己捕獲鼠標,來判斷是否點擊了主窗口的其他區域,但顯然這種方法也不太好;
這個時候delete this就派上用場啦(用智能指針也會崩潰,因爲出了作用域同樣會銷燬內存,所以只能用delete this啦~ 用delete this就是將作用域交給duilib了),據說COM裏面就是用delete this來銷燬內存的。Alberl在duilib的Demo裏面見到了大量的delete this,覺得這種自殺的方法很不靠譜,這不,前面教程就提到了ActiveX的一個bug,也是和delete this脫不了干係的~ 不過既然COM裏面都用了delete this,那就說明如果用好這把雙刃劍,還是可以帶來很多好處的。
因爲duilib提供了一個機制,就是窗口的最後一個函數一定是OnFinalMessage,之後不再調用窗口類的其他函數,這就爲自殺提供了兩個必要條件;delete this而還有一個必要條件就是這個類必須是通過new來申請內存的(而非 "new[]",亦非placement的"new" ,一定要是最原始的 "new",當然malloc也行(需要用free,而不是delete)),所以就將析構函數設置成私有函數,就保證了只有通過new申請內存的方式才能編譯通過。 而duilib的Demo中大量使用delete this卻沒有保證這些必要條件,只要直接用變量的方式來聲明類,則關閉窗口時就會崩潰,作爲Demo,如此不嚴謹,有待好好規範。當然,沒有XX黨,就沒有新中國,沒有那些大神的Demo,也就輪不到Alberl唧唧歪歪啦,這裏Alberl只是覺得Demo應該嚴謹和權威,畢竟是官方的,並沒有其他意思,請多多諒解~O(∩_∩)O~
二、組合框控件
由於duilib也沒有自帶GroupBox,所以我們一般採用的是Label + Control的組合,迅雷、百度、金山快盤、華爲網盤這些都是這種組合,如圖:
這個就不用代碼了吧,左邊的Label 設置文字顏色,右邊的Control設置背景色,高度設置爲1就好了。
三、複選框、單選框
duilib的TestApp1中有幾個CheckBox,不過也是需要圖片資源的,因爲圖中的方框就是圖片模擬的,代碼也很簡單,後面的教程會有詳細的代碼~
四、時間選擇控件
這個也很簡單,代碼如下:
<DateTime name="DateTimeDemo1" float="true" pos="30,118,0,0" width="120" height="30" bkcolor="#FFE2E5EA" padding="0,5,0,0" />
五、RichList、樹形控件
經過前面教程詳細的介紹,相信現在已經基本入門了,由於Alberl暫時也沒看這些控件,所以請自行參閱duilib自帶的Demo。
RichList請參閱duilib自帶的RichListDemo,效果如圖:
Tree控件請參閱 (QQDemo、GameDemo,TestApp1),效果如圖:
duilib的控件就基本介紹完啦,其他控件也請自行參閱~O(∩_∩)O~