Redrain 通用菜单控件使用方法和说明(增加动态添加功能、附源码和demo)

转载请说明原出处,谢谢~~: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、可以动态修改或者添加任意的子菜单


     二、接着是我更新后的属性列表:


[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <Menu parent="CListUI" notifies="setfocus killfocus timer menu itemselect windowinit(root)">  
  2.     <Attribute name="inset" default="0,0,0,0" type="RECT" comment="菜单的高度等于所有菜单项的高度之和,加上inset属性的top和bottom"/>  
  3.     <!-- Menu标签的属性要通过Default来定义,为了让下级菜单也能拥有同样的外观,其他属性设置见List -->  
  4. </Menu>  
  5. <MenuElement parent="CListContainerElementUI" notifies="click valuechanged WM_MENUCLICK">  
  6.     <Attribute name="icon" default="" type="STRING" comment="菜单项的图标图片"/>  
  7.     <Attribute name="iconsize" default="0,0" type="SIZE" comment="图片的大小,最大为26x26"/>  
  8.     <Attribute name="checkitem" default="false" type="BOOL" comment="是否有复选功能"/>  
  9.     <Attribute name="ischeck" default="false" type="BOOL" comment="是否被选中(前提是开启了复选功能,复选功能属性应该写在本属性的前面)"/>  
  10.     <Attribute name="linetype" default="false" type="BOOL" comment="是否是分割线(开启后将不会显示图标)"/>  
  11.     <Attribute name="linepadding" default="29,0,7,0" type="RECT" comment="分割线的外边据"/>  
  12.     <Attribute name="linecolor" default="0xFFBCBFC4" type="DWORD" comment="分割线的颜色"/>  
  13.     <Attribute name="expland" default="false" type="BOOL" comment="是否显示下级菜单的小三角图片(需要通过Default标签设置ExplandIcon属性图片的路径)"/>  
  14.     <Attribute name="height" default="30" type="INT" comment="菜单项高度(分割线默认高度是6)"/>  
  15. </MenuElement>  

       三、下面给出动态效果图对应的布局代码:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <Window showshadow="true" shadowimage="shadow.png" shadowcorner="23,13,23,33">  
  3. <Font name="微软雅黑" size="12" bold="false" default="true" />  
  4. <Font name="微软雅黑" size="12" bold="true"/>  
  5.   
  6. <!-- Menu标签的属性要通过Default来定义,为了让下级菜单也能拥有同样的外观 -->  
  7. <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"/>  
  8. <!-- ExplandIcon属性定义了下级菜单的小图标的样子 -->  
  9. <Default name="ExplandIcon" value="menu_expand.png" />  
  10.   
  11. <Menu>  
  12.     <MenuElement text="菜单测试0" expland="true" >  
  13.          <MenuElement text="菜单测试01" id="102"/>  
  14.          <MenuElement text="菜单测试02" />  
  15.     </MenuElement>  
  16.   
  17.     <MenuElement height="120" >  
  18.         <VerticalLayout bkcolor="#FFFFFFFE">  
  19.             <Button name="Menu_btn" height="118" normalimage="error.png" hotimage="check_hover.png" pushedimage="check_pressed.png"/>  
  20.         </VerticalLayout>  
  21.     </MenuElement>  
  22.   
  23.     <MenuElement name="Menu_Test1" text="菜单测试3" icon="right.png" iconsize="9,9" checkitem="true" ischeck="true" />  
  24.     <MenuElement name="Menu_Test2" text="菜单测试31" />  
  25.     <MenuElement name="Menu_Test3" text="菜单测试32" icon="right.png" iconsize="9,9" checkitem="true" ischeck="true" />  
  26.   
  27.     <MenuElement linetype="true" />  
  28.   
  29.     <MenuElement text="菜单测试4" icon="right.png" iconsize="9,9" expland="true" >  
  30.           <MenuElement text="菜单测试5" expland="true" icon="WebSit.png" >    
  31.               <MenuElement text="菜单测试6"  icon="Virus.png" />  
  32.               <MenuElement text="菜单测试7" />  
  33.           </MenuElement>  
  34.           <MenuElement text="菜单测试8" expland="true">   
  35.               <MenuElement text="菜单测试a" />  
  36.               <MenuElement text="菜单测试b" />  
  37.           </MenuElement>  
  38.     </MenuElement>   
  39. </Menu>  
  40. </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成员变量,在需要的地方调用如下代码:


[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. CPoint point = msg.ptMouse;  
  2. ClientToScreen(m_hWnd, &point);  
  3. CMenuWnd* pMenu = CMenuWnd::CreateMenu(NULL, _T("menutest.xml"), point, &m_PaintManager, &m_MenuCheckInfo);  
  4. //左侧打开菜单  
  5. /<span style="font-size:10px;">/<span style="font-family: Arial, Helvetica, sans-serif;">CMenuWnd* pMenu = CMenuWnd::CreateMenu</span><span style="font-family: Arial, Helvetica, sans-serif;">(NULL, _T("menutest.xml"), point, &m_PaintManager, &m_MenuCheckInfo, eMenuAlignment_Right );</span></span>  
  6. //左上侧打开菜单  
  7. //CMenuWnd* pMenu = CMenuWnd::CreateMenu(NULL, _T("menutest.xml"), point, &m_PaintManager, &m_MenuCheckInfo, eMenuAlignment_Right | eMenuAlignment_Bottom);  



        其中菜单控件的的CreateWnd函数说明:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  *  @pOwner 一级菜单不要指定这个参数,这是菜单内部使用的 
  3.  *  @xml    菜单的布局文件 
  4.  *  @point  菜单的左上角坐标 
  5.  *  @pMainPaintManager  菜单的父窗体管理器指针 
  6.  *  @xml    保存菜单的单选和复选信息结构指针 
  7.  *  @dwAlignment        菜单的出现位置,默认出现在鼠标的右下侧。 
  8.  */  
  9.    static CMenuWnd* CreateMenu(CMenuElementUI* pOwner, STRINGorID xml, POINT point,  
  10.     CPaintManagerUI* pMainPaintManager, map<CDuiString,bool>* pMenuCheckInfo = NULL,  
  11.     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消息。处理代码如下:


[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. LRESULT CFrameWnd::HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)  
  2.  {  
  3.      if (uMsg == WM_MENUCLICK)  
  4.      {  
  5.          CDuiString *strMenuName = (CDuiString*)wParam;  
  6.          BOOL bChecked = (BOOL)lParam;         
  7.   
  8.          if ( *strMenuName == _T("Menu_Test1"))   
  9.          {  
  10.              if (bChecked)  
  11.              {  
  12.                  MessageBox(m_hWnd, L"你选中Menu_Test1", L"", 0);  
  13.              }   
  14.              else  
  15.              {  
  16.                  MessageBox(m_hWnd, L"你取消Menu_Test1", L"", 0);  
  17.              }             
  18.          }  
  19.          else if ( *strMenuName == _T("Menu_Test2"))   
  20.          {  
  21.                 MessageBox(m_hWnd, L"你单击了Menu_Test2", L"", 0);         
  22.          }  
  23.          else if ( *strMenuName == _T("Menu_Test3"))   
  24.          {  
  25.              if (bChecked)  
  26.              {  
  27.                  MessageBox(m_hWnd, L"你选中Menu_Test3", L"", 0);  
  28.              }   
  29.              else  
  30.              {  
  31.                  MessageBox(m_hWnd, L"你取消Menu_Test3", L"", 0);  
  32.              }             
  33.          }  
  34.   
  35.      }  
  36.      bHandled = false;  
  37.      return 0;  
  38.  }  

        wParam参数是一个CDuiString指针,里面是被单击的菜单项的名字,通过它判断单击了哪个菜单项。lParam是菜单项的复选信息。


      动态添加修改子菜单:

      原本的菜单是没有增加动态修改子菜单的功能的,群里一直有网友给我说动态添加的需求,我之前没有考虑这个问题,不过今天恰好遇到项目需求也需要动态添加菜单项,所以专门增加了这个功能,其实这个功能增加起来非常简单,我增加了三个函数来支持:


[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // 获取根菜单控件,用于动态添加子菜单  
  2. CMenuUI* GetMenuUI();  
  3.   
  4. // 重新调整菜单的大小  
  5. void ResizeMenu();  
  6.   
  7. // 重新调整子菜单的大小  
  8. void ResizeSubMenu();  


       示例代码如下:


[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // 先获取到根项,然后就可以使用rootMenu插到到菜单内的任意子菜单项,然后做添加删除操作  
  2. CMenuUI* rootMenu = pMenu->GetMenuUI();  
  3. if (rootMenu != NULL)  
  4. {  
  5.     CMenuElementUI* pNew = new CMenuElementUI;  
  6.     pNew->SetName(_T("Menu_Dynamic"));  
  7.     pNew->SetText(_T("动态一级菜单"));  
  8.     pNew->SetShowExplandIcon(true);  
  9.     pNew->SetIcon(_T("WebSit.png"));  
  10.     pNew->SetIconSize(16,16);  
  11.   
  12.   
  13.     CMenuElementUI* pSubNew = new CMenuElementUI;  
  14.     pSubNew->SetText(_T("动态二级菜单"));  
  15.     pSubNew->SetName(_T("Menu_Dynamic"));  
  16.     pSubNew->SetIcon(_T("Virus.png"));  
  17.     pSubNew->SetIconSize(16,16);  
  18.     pNew->Add(pSubNew);  
  19.       
  20.   
  21.     rootMenu->Add(pNew);  
  22.   
  23.   
  24.     CMenuElementUI* pNew2 = new CMenuElementUI;  
  25.     pNew2->SetName(_T("Menu_Dynamic"));  
  26.     pNew2->SetText(_T("动态一级菜单2"));  
  27.     rootMenu->AddAt(pNew2,2);  
  28. }  
  29.   
  30. // 动态添加后重新设置菜单的大小  
  31. pMenu->ResizeMenu();  



菜单控件源码:


     菜单控件的源码相对较多,我就不直接贴出来了,需要的朋友可以直接下载我的duilib库来查看,里面已经附带了菜单使用Demo和对应源码:点击打开链接。有网友反映菜单崩溃的bug,这个问题的解决方法是在类成员变量里声明CMenuWnd指针,在源文件中动态new CMenuWnd,使用完毕后自己手动delete CMenuWnd,具体演示代码看菜单使用Demo



   2015.1.19   Redrain


   QQ:491646717

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值