轉載請說明原出處,謝謝~~:http://blog.csdn.net/zhuhongshu/article/details/42889709
大概半年前我寫過博客說明怎麼改造duilib的原代MenuDemo來支持消息發送(地址爲:http://blog.csdn.net/zhuhongshu/article/details/38253297),而後在仿酷狗項目裏也用到了菜單類,並且菜單類歲仿酷狗一起開源了。但是仿酷狗裏面的菜單是專門針對仿酷狗的需求而修改的,所以通用性還不夠。考慮到菜單也比較常用。所以今天就把菜單控件重整,修改爲通用的控件,並且把它集成了到我的duilib庫裏,可以直接使用。現在也增加了動態添加修改菜單項的功能。
在此我把通用菜單控件的使用方法說明一下,源碼和Demo可以在我的duilib庫中下載到。
一、首先看看這個菜單的功能:
1、可以展現多級菜單
2、可內嵌自定義控件,並且控件可以向主窗體發送消息,如圖的紅色歎號就是個按鈕控件,可以製作酷狗音樂的托盤菜單的播放暫停按鈕和進度控制進度條。
3、菜單擁有陰影效果(使用我庫裏的陰影類完成)
4、菜單可以自定義前方顯示小圖標,並且可以控制圖標的大小和是否顯示
5、菜單可以根據是否擁有子菜單決定是否顯示小箭頭
6、菜單可以添加分割線(控制分割線的樣式和位置)
7、每個菜單項都可以實現複選或者單選功能
8、優化菜單的xml描述文件,編寫方便容易
9、可以通過鍵盤的按鈕控制菜單的選項
10、每個菜單項的高度和寬度是任意調整的
11、可以動態修改或者添加任意的子菜單
二、接着是我更新後的屬性列表:
<Menu parent="CListUI" notifies="setfocus killfocus timer menu itemselect windowinit(root)">
<Attribute name="inset" default="0,0,0,0" type="RECT" comment="菜單的高度等於所有菜單項的高度之和,加上inset屬性的top和bottom"/>
<!-- Menu標籤的屬性要通過Default來定義,爲了讓下級菜單也能擁有同樣的外觀,其他屬性設置見List -->
</Menu>
<MenuElement parent="CListContainerElementUI" notifies="click valuechanged WM_MENUCLICK">
<Attribute name="icon" default="" type="STRING" comment="菜單項的圖標圖片"/>
<Attribute name="iconsize" default="0,0" type="SIZE" comment="圖片的大小,最大爲26x26"/>
<Attribute name="checkitem" default="false" type="BOOL" comment="是否有複選功能"/>
<Attribute name="ischeck" default="false" type="BOOL" comment="是否被選中(前提是開啓了複選功能,複選功能屬性應該寫在本屬性的前面)"/>
<Attribute name="linetype" default="false" type="BOOL" comment="是否是分割線(開啓後將不會顯示圖標)"/>
<Attribute name="linepadding" default="29,0,7,0" type="RECT" comment="分割線的外邊據"/>
<Attribute name="linecolor" default="0xFFBCBFC4" type="DWORD" comment="分割線的顏色"/>
<Attribute name="expland" default="false" type="BOOL" comment="是否顯示下級菜單的小三角圖片(需要通過Default標籤設置ExplandIcon屬性圖片的路徑)"/>
<Attribute name="height" default="30" type="INT" comment="菜單項高度(分割線默認高度是6)"/>
</MenuElement>
三、下面給出動態效果圖對應的佈局代碼:
<?xml version="1.0" encoding="utf-8"?>
<Window showshadow="true" shadowimage="shadow.png" shadowcorner="23,13,23,33">
<Font name="微軟雅黑" size="12" bold="false" default="true" />
<Font name="微軟雅黑" size="12" bold="true"/>
<!-- Menu標籤的屬性要通過Default來定義,爲了讓下級菜單也能擁有同樣的外觀 -->
<Default name="Menu" value="bordersize="1" borderround="2,2" bordercolor="0x303132" inset="2,2,2,2" itemtextpadding="30,0,0,0" bkimage="file='menu_bk.png' corner='26,2,2,2' source='6,6,44,24'" itemselectedbkcolor="0xFF338ACA"" />
<!-- ExplandIcon屬性定義了下級菜單的小圖標的樣子 -->
<Default name="ExplandIcon" value="menu_expand.png" />
<Menu>
<MenuElement text="菜單測試0" expland="true" >
<MenuElement text="菜單測試01" id="102"/>
<MenuElement text="菜單測試02" />
</MenuElement>
<MenuElement height="120" >
<VerticalLayout bkcolor="#FFFFFFFE">
<Button name="Menu_btn" height="118" normalimage="error.png" hotimage="check_hover.png" pushedimage="check_pressed.png"/>
</VerticalLayout>
</MenuElement>
<MenuElement name="Menu_Test1" text="菜單測試3" icon="right.png" iconsize="9,9" checkitem="true" ischeck="true" />
<MenuElement name="Menu_Test2" text="菜單測試31" />
<MenuElement name="Menu_Test3" text="菜單測試32" icon="right.png" iconsize="9,9" checkitem="true" ischeck="true" />
<MenuElement linetype="true" />
<MenuElement text="菜單測試4" icon="right.png" iconsize="9,9" expland="true" >
<MenuElement text="菜單測試5" expland="true" icon="WebSit.png" >
<MenuElement text="菜單測試6" icon="Virus.png" />
<MenuElement text="菜單測試7" />
</MenuElement>
<MenuElement text="菜單測試8" expland="true">
<MenuElement text="菜單測試a" />
<MenuElement text="菜單測試b" />
</MenuElement>
</MenuElement>
</Menu>
</Window>
這裏我說明一下使用菜單需要注意的一些地方:
1、需要通過Default來定義Menu的樣式,這是爲了可以讓所有菜單(包括各個下級菜單)使用統一的樣式
2、用Default標籤定義ExplandIcon屬性來制定下級菜單的小圖標的路徑
3、MenuElement如果要用單選或者複選功能,checkitem屬性要寫在ischeck屬性前面
4、指定Menu的inset內邊距屬性的top和bottom屬性,會自動讓菜單增高(很多情況需要這樣做來設置菜單的內邊距)
5、MenuElement的linepadding屬性可以設置分割線的外邊距,默認爲"29,0,7,0",這個值要根據實際的素材和需求來設置
四、使用菜單的c++代碼:
菜單的創建:
在類裏聲明CMenuWnd成員變量,在需要的地方調用如下代碼:
CPoint point = msg.ptMouse;
ClientToScreen(m_hWnd, &point);
CMenuWnd* pMenu = CMenuWnd::CreateMenu(NULL, _T("menutest.xml"), point, &m_PaintManager, &m_MenuCheckInfo);
//左側打開菜單
//CMenuWnd* pMenu = CMenuWnd::CreateMenu(NULL, _T("menutest.xml"), point, &m_PaintManager, &m_MenuCheckInfo, eMenuAlignment_Right );
//左上側打開菜單
//CMenuWnd* pMenu = CMenuWnd::CreateMenu(NULL, _T("menutest.xml"), point, &m_PaintManager, &m_MenuCheckInfo, eMenuAlignment_Right | eMenuAlignment_Bottom);
/*
* @pOwner 一級菜單不要指定這個參數,這是菜單內部使用的
* @xml 菜單的佈局文件
* @point 菜單的左上角座標
* @pMainPaintManager 菜單的父窗體管理器指針
* @xml 保存菜單的單選和複選信息結構指針
* @dwAlignment 菜單的出現位置,默認出現在鼠標的右下側。
*/
static CMenuWnd* CreateMenu(CMenuElementUI* pOwner, STRINGorID xml, POINT point,
CPaintManagerUI* pMainPaintManager, map<CDuiString,bool>* pMenuCheckInfo = NULL,
DWORD dwAlignment = eMenuAlignment_Left | eMenuAlignment_Top);
CreateMenu函數會使用new在堆上創建菜單控件(菜單會自己銷燬內存)。自己主動創建菜單時第一參數直接填寫NULL就可以;如果需要用到單選和複選功能就需要給控件指定一個map,來保存複選數據;如果需要控制菜單出現的位置,可以修改dwAlignment參數,參數可以設置eMenuAlignment_Left ,eMenuAlignment_Top ,eMenuAlignment_Right ,eMenuAlignment_Bottom 四種值,具體用法參見demo。
菜單的消息響應:
這個菜單類支持響應菜單內嵌的控件(比如按鈕或者滑動條控件,這個常用在做播放器的菜單上),內嵌控件的消息響應和普通的消息響應完全一樣,目前支持click消息和valuechanged消息(可以根據需求再增加),但是注意不可以在這些消息相應代碼彈出像是MessageBox這樣的模態對話框,原因見我之前寫的博客。
單擊每個菜單項的消息響應方法是,接收WM_MENUCLICK消息,這是我自定義的消息,處理這個消息時可以任意彈出模態對話框。通常消息響應的方法是重寫WindowImplBase類的HandleCustomMessage函數來處理自定義消息(如果沒有繼承WindowImplBase類就自己寫類似的函數功能),然後處理WM_MENUCLICK消息。處理代碼如下:
LRESULT CFrameWnd::HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if (uMsg == WM_MENUCLICK)
{
CDuiString *strMenuName = (CDuiString*)wParam;
BOOL bChecked = (BOOL)lParam;
if ( *strMenuName == _T("Menu_Test1"))
{
if (bChecked)
{
MessageBox(m_hWnd, L"你選中Menu_Test1", L"", 0);
}
else
{
MessageBox(m_hWnd, L"你取消Menu_Test1", L"", 0);
}
}
else if ( *strMenuName == _T("Menu_Test2"))
{
MessageBox(m_hWnd, L"你單擊了Menu_Test2", L"", 0);
}
else if ( *strMenuName == _T("Menu_Test3"))
{
if (bChecked)
{
MessageBox(m_hWnd, L"你選中Menu_Test3", L"", 0);
}
else
{
MessageBox(m_hWnd, L"你取消Menu_Test3", L"", 0);
}
}
}
bHandled = false;
return 0;
}
wParam參數是一個CDuiString指針,裏面是被單擊的菜單項的名字,通過它判斷單擊了哪個菜單項。lParam是菜單項的複選信息。
動態添加修改子菜單:
原本的菜單是沒有增加動態修改子菜單的功能的,羣裏一直有網友給我說動態添加的需求,我之前沒有考慮這個問題,不過今天恰好遇到項目需求也需要動態添加菜單項,所以專門增加了這個功能,其實這個功能增加起來非常簡單,我增加了三個函數來支持:
// 獲取根菜單控件,用於動態添加子菜單
CMenuUI* GetMenuUI();
// 重新調整菜單的大小
void ResizeMenu();
// 重新調整子菜單的大小
void ResizeSubMenu();
示例代碼如下:
// 先獲取到根項,然後就可以使用rootMenu插到到菜單內的任意子菜單項,然後做添加刪除操作
CMenuUI* rootMenu = pMenu->GetMenuUI();
if (rootMenu != NULL)
{
CMenuElementUI* pNew = new CMenuElementUI;
pNew->SetName(_T("Menu_Dynamic"));
pNew->SetText(_T("動態一級菜單"));
pNew->SetShowExplandIcon(true);
pNew->SetIcon(_T("WebSit.png"));
pNew->SetIconSize(16,16);
CMenuElementUI* pSubNew = new CMenuElementUI;
pSubNew->SetText(_T("動態二級菜單"));
pSubNew->SetName(_T("Menu_Dynamic"));
pSubNew->SetIcon(_T("Virus.png"));
pSubNew->SetIconSize(16,16);
pNew->Add(pSubNew);
rootMenu->Add(pNew);
CMenuElementUI* pNew2 = new CMenuElementUI;
pNew2->SetName(_T("Menu_Dynamic"));
pNew2->SetText(_T("動態一級菜單2"));
rootMenu->AddAt(pNew2,2);
}
// 動態添加後重新設置菜單的大小
pMenu->ResizeMenu();
菜單控件源碼:
菜單控件的源碼相對較多,我就不直接貼出來了,需要的朋友可以直接下載我的duilib庫來查看,裏面已經附帶了菜單使用Demo和對應源碼:點擊打開鏈接。有網友反映菜單崩潰的bug,這個問題的解決方法是在類成員變量裏聲明CMenuWnd指針,在源文件中動態new CMenuWnd,使用完畢後自己手動delete CMenuWnd,具體演示代碼看菜單使用Demo
2015.1.19 Redrain
QQ:491646717