Files
minigui-docs/programming-guide-zh/MiniGUIProgGuidePart6Chapter06-zh.md
lisimeng123 12ea4e47e4 update
2022-11-18 00:00:48 +08:00

13 KiB
Raw Blame History

24 菜单按钮

菜单按钮的功能和普通下拉式组合框的功能基本一样,实质上,在 MiniGUI 早期版本中,菜单按钮就是以组合框的替代品出现的。当然,菜单按钮有很大的限制,比如不能够编辑、不提供列表条目的滚动等等。

在外观上菜单按钮类似一个普通按钮不同的是在按钮矩形区域的右侧有一个向下的箭头。当用户点击该控件时就会弹出一个菜单而用户使用鼠标点击菜单中某一条目时按钮的内容就变为该条目的内容。如__图 1.1__ 所示。

![菜单按钮(左边为平常状态,右边为弹出菜单后的效果)](figures/Part4Chapter06-1.1.jpeg 图 1.1 菜单按钮(左边为平常状态,右边为弹出菜单后的效果)

CTRL_MENUBUTTON 为控件类名调用 CreateWindow 函数,即可创建菜单按钮。

1.1 菜单按钮风格

菜单按钮有一组自己特有的风格,一般使用如下的方式对风格进行组合。

WS_CHILD | WS_VISIBLE | MBS_SORT
  • MBS_SORT:对菜单按钮中的条目进行排序显示。
  • MBS_LEFTARROW:箭头显示在菜单按钮的左侧。
  • MBS_NOBUTTON:不显示按钮。
  • MBS_ALIGNLEFT:菜单按钮上的文字向左对齐。
  • MBS_ALIGNRIGHT:菜单按钮上的文字向右对齐。
  • MBS_ALIGNCENTER:菜单按钮上的文字居中对齐。

当菜单条目使用位图的时候,位图的位置不受对齐方式的影响。

1.2 菜单按钮消息

1.2.1 向菜单按钮控件添加条目

向菜单按钮添加条目时使用 MBM_ADDITEM 消息,并传递一个经过初始化的 MENUBUTTONITEM 结构,如下所示:

MENUBUTTONITEM mbi;       // 声明一个菜单条目结构体变量
mbi.text = "item one";    // 设置条目文字
mbi.bmp = NULL;           // 在这里可以指定位图对象
mbi.data = 0;
pos = SendMessage (hMbtnWnd, MBM_ADDITEM, -1, (LPARAM) &mbi);

其中,hMbtnWnd 是菜单按钮控件的句柄。Pos 得到新添加的菜单条目的索引值,当内存空间不足时,则返回 MB_ERR_SPACE

1.2.2 从菜单按钮控件删除条目

从菜单按钮中删除条目,可使用 MBM_DELITEM 消息,并指定要删除的菜单项索引号。如下所示:

SendMessage (hMbtnWnd, MBM_DELITEM, index, 0);

其中,index 为条目的索引值。

1.2.3 删除菜单中的所有条目

和列表框一样,菜单按钮也提供了删除所有条目的消息,即 MBM_RESETCTRL 消息。如下所示:

SendMessage (hMbtnWnd, MBM_RESETCTRL, 0, 0);

1.2.4 设置当前选定条目

类似地,用 MBM_SETCURITEM 消息设置选中条目,被选中的条目文本将显示在菜单按钮上。如下所示:

SendMessage (hMbtnWnd, MBM_SETCURITEM, index, 0);

其中,index 为要设定的条目索引值。

1.2.5 得到当前选定条目

MBM_GETCURITEM 消息可获得当前选中条目的索引号。如下所示:

index = SendMessage (hMbtnWnd, MBM_GETCURITEM, 0, 0);

该消息返回当前选定条目的索引值。

1.2.6 获取或设置菜单项条目数据

使用 MBM_GETITEMDATAMBM_SETITEMDATA 消息可获取或设置菜单项条目的数据。使用这个两个消息时,wParam 参数传递要获取或设置的菜单项索引值,lParam 参数传递一个指向 MENUBUTTONITEM 的结构指针,其中包括菜单项的文本、位图对象以及附加数据。

需要注意的是,MENUBUTTONITEM 结构中含有一个 which 成员,该成员指定了要获取或设置菜单项的哪个数据(文本、位图对象或者附加数据中的一个或多个),通常是下列值的组合:

  • MB_WHICH_TEXT:表明要获取或设置菜单项的文本,这时,该结构的 text 成员必须指向有效缓冲区。
  • MB_WHICH_BMP:表明要获取或设置菜单项的位图对象。
  • MB_WHICH_ATTDATA:表明要获取或设置菜单项的附加数据。

示例如下:

MENUBUTTONITEM mbi;

mbi.which = MB_WHICH_TEXT | MB_WHICH_ATTDATA;
mbi.text = newtext;
mbi.data = 1;
SendMessage (menubtn, MBM_SETITEMDATA, 0, (LPARAM) &mbi);

其中,menubtn 是某个菜单按钮的句柄。

1.2.7 其他消息

在使用 MBS_SORT 风格时,因为涉及到条目的排序,所以 MiniGUI 也为应用程序提供了 MBM_SETSTRCMPFUNC 消息,用来设定一个定制的排序函数。一般而言,应用程序要在添加条目之前使用该消息设定新的字符串比较函数。

该消息的用法可参照列表框消息 LB_SETSTRCMPFUNC 消息。

1.3 菜单按钮的通知消息

菜单按钮没有 MBS_NOTIFY 风格,因此,任意一个菜单按钮控件均可能产生如下的通知消息:

  • MBN_ERRSPACE:内存分配失败,存储空间不足。
  • MBN_SELECTED:对菜单按钮控件进行了选择。不管前后选择的菜单项是否改变,均会产生该通知消息。
  • MBN_CHANGED:菜单按钮控件的选择项发生了变化。
  • MBN_STARTMENU:用户激活了菜单按钮的弹出式菜单。
  • MBN_ENDMENU:弹出式菜单关闭。

1.4 编程实例

清单 1.1 给出了菜单按钮的使用实例。该程序段是对__清单 1.1__ 程序的一个小改动即将__清单 1.1__ 程序中的下拉式组合框改成了菜单按钮来实现。该程序的完整源代码可见本指南示例程序包 mg-samples 中的 menubutton.c 文件其运行效果见__图 1.2__。

清单 1.1 菜单按钮的使用实例

/* 定义对话框模板 */
static DLGTEMPLATE DlgMyDate =
{
        WS_BORDER | WS_CAPTION,
        WS_EX_NONE,
        100, 100, 304, 135,
        "约会大侠",
        0, 0,
        9, NULL,
        0
};

static CTRLDATA CtrlMyDate[] =
{ 
        
        ...
        
        /* 将用于显示大侠名单的组合框换成按钮菜单来实现 */
        {
                CTRL_MENUBUTTON,
                WS_CHILD | WS_VISIBLE,
                190, 20, 100, 20,
                IDL_DAXIA,
                "",
                0
        },
        
        ...
        
};

...

static void daxia_notif_proc (HWND hwnd, int id, int nc, DWORD add_data)
{
        if (nc == CBN_SELCHANGE) {
                /* 获得选定的大侠,并显示其性格特点 */
                int cur_sel = SendMessage (hwnd, MBM_GETCURITEM, 0, 0);
                if (cur_sel >= 0) {
                        SetWindowText (GetDlgItem (GetParent(hwnd), IDC_PROMPT), daxia_char [cur_sel]);
                }
        }
}

static void prompt (HWND hDlg)
{
        char date [1024];
        
        int hour = SendDlgItemMessage(hDlg, IDC_HOUR, CB_GETSPINVALUE, 0, 0);
        int min = SendDlgItemMessage(hDlg, IDC_MINUTE, CB_GETSPINVALUE, 0, 0);
        int sel = SendDlgItemMessage(hDlg, IDL_DAXIA, MBM_GETCURITEM, 0, 0);
        
        sprintf (date, "你打算于今日 %02d:%02d 去见那个%s的%s", hour, min, 
        daxia_char [sel], daxia [sel]);
        
        MessageBox (hDlg, date, "约会内容", MB_OK | MB_ICONINFORMATION);
}

static int MyDateBoxProc (HWND hDlg, int message, WPARAM wParam, LPARAM lParam)
{
        int i;
        switch (message) {
                case MSG_INITDIALOG:
                SendDlgItemMessage(hDlg, IDC_HOUR, CB_SETSPINFORMAT, 0, (LPARAM)"%02d");
                SendDlgItemMessage(hDlg, IDC_HOUR, CB_SETSPINRANGE, 0, 23);
                SendDlgItemMessage(hDlg, IDC_HOUR, CB_SETSPINVALUE, 20, 0);
                SendDlgItemMessage(hDlg, IDC_HOUR, CB_SETSPINPACE, 1, 1);
                
                SendDlgItemMessage(hDlg, IDC_MINUTE, CB_SETSPINFORMAT, 0, (LPARAM)"%02d");
                SendDlgItemMessage(hDlg, IDC_MINUTE, CB_SETSPINRANGE, 0, 59);
                SendDlgItemMessage(hDlg, IDC_MINUTE, CB_SETSPINVALUE, 0, 0);
                SendDlgItemMessage(hDlg, IDC_MINUTE, CB_SETSPINPACE, 1, 2);
                
                /* 向菜单按钮中加入各位大侠的名字 */
                for (i = 0; i < 7; i++) {
                        MENUBUTTONITEM mbi;
                        mbi.text = daxia[i];
                        mbi.bmp = NULL;
                        mbi.data = 0;
                        SendDlgItemMessage(hDlg, IDL_DAXIA, MBM_ADDITEM, -1, (LPARAM)&mbi);
                }
                
                /* 设定菜单按钮的通知回调函数 */
                SetNotificationCallback (GetDlgItem (hDlg, IDL_DAXIA), daxia_notif_proc);
                SendDlgItemMessage(hDlg, IDL_DAXIA, MBM_SETCURITEM, 0, 0);
                SetWindowText (GetDlgItem (hDlg, IDC_PROMPT), daxia_char [0]);
                return 1;
                
                case MSG_COMMAND:
                switch (wParam) {
                        case IDOK:
                        prompt (hDlg);
                        case IDCANCEL:
                        EndDialog (hDlg, wParam);
                        break;
                }
                break;
                
        }
        
        return DefaultDialogProc (hDlg, message, wParam, lParam);
}/* 定义对话框模板 */
static DLGTEMPLATE DlgMyDate =
{
        WS_BORDER | WS_CAPTION,
        WS_EX_NONE,
        100, 100, 304, 135,
        "约会大侠",
        0, 0,
        9, NULL,
        0
};

static CTRLDATA CtrlMyDate[] =
{ 
        
        ...
        
        /* 将用于显示大侠名单的组合框换成按钮菜单来实现 */
        {
                CTRL_MENUBUTTON,
                WS_CHILD | WS_VISIBLE,
                190, 20, 100, 20,
                IDL_DAXIA,
                "",
                0
        },
        
        ...
        
};

...

static void daxia_notif_proc (HWND hwnd, int id, int nc, DWORD add_data)
{
        if (nc == CBN_SELCHANGE) {
                /* 获得选定的大侠,并显示其性格特点 */
                int cur_sel = SendMessage (hwnd, MBM_GETCURITEM, 0, 0);
                if (cur_sel >= 0) {
                        SetWindowText (GetDlgItem (GetParent(hwnd), IDC_PROMPT), daxia_char [cur_sel]);
                }
        }
}

static void prompt (HWND hDlg)
{
        char date [1024];
        
        int hour = SendDlgItemMessage(hDlg, IDC_HOUR, CB_GETSPINVALUE, 0, 0);
        int min = SendDlgItemMessage(hDlg, IDC_MINUTE, CB_GETSPINVALUE, 0, 0);
        int sel = SendDlgItemMessage(hDlg, IDL_DAXIA, MBM_GETCURITEM, 0, 0);
        
        sprintf (date, "你打算于今日 %02d:%02d 去见那个%s的%s", hour, min, 
        daxia_char [sel], daxia [sel]);
        
        MessageBox (hDlg, date, "约会内容", MB_OK | MB_ICONINFORMATION);
}

static int MyDateBoxProc (HWND hDlg, int message, WPARAM wParam, LPARAM lParam)
{
        int i;
        switch (message) {
                case MSG_INITDIALOG:
                SendDlgItemMessage(hDlg, IDC_HOUR, CB_SETSPINFORMAT, 0, (LPARAM)"%02d");
                SendDlgItemMessage(hDlg, IDC_HOUR, CB_SETSPINRANGE, 0, 23);
                SendDlgItemMessage(hDlg, IDC_HOUR, CB_SETSPINVALUE, 20, 0);
                SendDlgItemMessage(hDlg, IDC_HOUR, CB_SETSPINPACE, 1, 1);
                
                SendDlgItemMessage(hDlg, IDC_MINUTE, CB_SETSPINFORMAT, 0, (LPARAM)"%02d");
                SendDlgItemMessage(hDlg, IDC_MINUTE, CB_SETSPINRANGE, 0, 59);
                SendDlgItemMessage(hDlg, IDC_MINUTE, CB_SETSPINVALUE, 0, 0);
                SendDlgItemMessage(hDlg, IDC_MINUTE, CB_SETSPINPACE, 1, 2);
                
                /* 向菜单按钮中加入各位大侠的名字 */
                for (i = 0; i < 7; i++) {
                        MENUBUTTONITEM mbi;
                        mbi.text = daxia[i];
                        mbi.bmp = NULL;
                        mbi.data = 0;
                        SendDlgItemMessage(hDlg, IDL_DAXIA, MBM_ADDITEM, -1, (LPARAM)&mbi);
                }
                
                /* 设定菜单按钮的通知回调函数 */
                SetNotificationCallback (GetDlgItem (hDlg, IDL_DAXIA), daxia_notif_proc);
                SendDlgItemMessage(hDlg, IDL_DAXIA, MBM_SETCURITEM, 0, 0);
                SetWindowText (GetDlgItem (hDlg, IDC_PROMPT), daxia_char [0]);
                return 1;
                
                case MSG_COMMAND:
                
                int MiniGUIMain (int argc, const char* argv[])
                {
                        #ifdef _MGRM_PROCESSES
                        JoinLayer(NAME_DEF_LAYER , "menubutton" , 0 , 0);
                        #endif
                        
                        DlgMyDate.controls = CtrlMyDate;
                        
                        DialogBoxIndirectParam (&DlgMyDate, HWND_DESKTOP, MyDateBoxProc, 0L);
                        
                        return 0;
                }
                
                ...

使用菜单按钮 图 1.2 使用菜单按钮