Skip to content

扩展菜单控件 (SMenuEx)

Warning

The current page still doesn't have a translation for this language.

You can read it through google translate.

基本信息

  • 类名SMenuEx
  • 基类SHostWnd(宿主窗口,实现 IMenuEx
  • 菜单项标签menuItem(对应 SMenuExItem,继承自 SWindow
  • 功能描述:基于 SOUI 布局系统的扩展菜单,菜单项本身就是 SWindow 派生控件,因此可以完全自定义外观(皮肤、布局、子控件),并支持图标、勾选、单选、悬停高亮、子菜单等特性。

重要纠正:旧文档把 SMenuEx 当成普通布局控件并列出 iconSkin / itemHeight / iconMargin / textMargin / separatorHeight / maxWidth 等“控件属性”,这些在 SMenuEx 源码(SOUI/include/helper/SMenuEx.h)中并不存在——SMenuEx 本身没有 SOUI_ATTRS。它的外观由布局 XML(根窗口样式与皮肤)决定;可配置的 XML 属性只存在于菜单项 menuItemSMenuExItem)上。下为对照源码后的正确属性表。

每个菜单项在布局 XML 中是一个 <menuItem> 标签,对应 SMenuExItem,其属性在 SMenuExItem::SOUI_ATTRS_BEGIN 中声明:

属性名 类型 默认值 是否重绘 说明
icon int -1 图标索引(指向菜单项皮肤中的图标帧)
check int 0 是否勾选(1 勾选,0 不勾选)
radio int 0 是否为单选按钮样式(1 单选,0 否)
hotKey char 0(无) 热键字符(显示在菜单项右侧)

说明:check / radio 虽以 int 声明(ATTR_INT),但语义上是布尔开关。菜单项其余外观(背景、文本颜色、字体、间距等)由布局 XML 中 menuItem 的皮肤 / 样式决定,非独立 XML 属性。

使用示例

布局 XML 示例

SMenuEx 通过 LoadMenu / LoadMenu2 从一个布局 XML 加载,根节点下直接放置 <menuItem>

<menuExRoot>
    <root>
        <menuItem text="新建" icon="0">
            <menuItem id="100" text="文件"/>
            <menuItem id="101" text="文件夹"/>
        </menuItem>
        <menuItem text="编辑" icon="1">
            <menuItem id="200" text="复制" hotKey="C"/>
            <menuItem id="201" text="粘贴" hotKey="V"/>
        </menuItem>
        <menuItem id="300" text="自动保存" check="1"/>
        <menuItem id="301" text="只读模式" radio="1"/>
        <menuItem id="302" text="禁用项" disable="1"/>
    </root>
</menuExRoot>

代码加载与弹出

#include <helper/SMenuEx.h>

SMenuEx m_mainMenu;

void OnInit()
{
    if (!m_mainMenu.LoadMenu(L"layout:menu_main_ex"))
    {
        SASSERT_FMT(FALSE, "加载扩展菜单失败");
        return;
    }
}

void ShowContextMenu(CPoint pt)
{
    int nCmd = m_mainMenu.TrackPopupMenu(
        TPM_RETURNCMD | TPM_RIGHTALIGN, pt.x, pt.y, m_hWnd);
    if (nCmd)
        OnCommand(nCmd);
}

动态增删菜单项

void MenuExApiDemo(SMenuEx& menu)
{
    // 真实 API:InsertMenu(UINT uPosition, UINT uFlags, int nIDNewItem, LPCTSTR strText, int iIcon)
    menu.InsertMenu(0, MF_STRING, 1000, L"新菜单项", /*iIcon*/ -1);

    // 启用 / 禁用、勾选
    menu.EnableMenuItem(1000, MF_BYCOMMAND | MF_GRAYED);
    menu.CheckMenuItem(300, MF_BYCOMMAND | MF_CHECKED);

    // 删除与销毁
    menu.DeleteMenu(1000, MF_BYCOMMAND);
}

事件处理

扩展菜单的菜单项均为 SWindow 派生控件,选中时向宿主发送 EventCmd;悬停时触发 EventMenuHover

bool OnMenuCommand(EventCmd* pEvt)
{
    int nID = pEvt->idFrom;
    switch (nID)
    {
    case 100: CreateNewFile(); break;
    case 200: OpenFile();      break;
    case 201: SaveFile();      break;
    }
    return true;
}

bool OnMenuHover(EventMenuHover* pEvt)
{
    SMenuExItem* pItem = sobj_cast<SMenuExItem>(pEvt->sender);
    if (pItem)
        UpdateStatusText(pItem->GetText());
    return true;
}

常见问题

  1. SMenuEx vs SMenuSMenuEx 的菜单项是真正的 SWindowmenuItem),外观可完全自定义;SMenu 基于原生 HMENU,由 SMenuAttr 控制样式。需要高度自定义时优先选 SMenuEx
  2. 项属性不生效:确认属性写在 <menuItem> 上,而不是根节点或 <sep> 上。
  3. 取消“控件属性”误解SMenuEx 没有 itemHeight / iconMargin / maxWidth 这类属性,项高度/间距应通过项的皮肤尺寸或布局参数控制。

相关主题