Files
minigui-docs/programming-guide-zh/MiniGUIProgGuidePart6Chapter02-zh.md
lisimeng123 24cfe07828 update
2022-11-21 08:46:18 +08:00

409 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 按钮
按钮是除静态框之外使用最为频繁的一种控件。按钮通常用来为用户提供开关选择。MiniGUI 的按钮可划分为普通按钮、复选框和单选钮等几种类型。用户可以通过键盘或者鼠标来选择或者切换按钮的状态。用户的输入将使按钮产生通知消息,应用程序也可以向按钮发送消息以改变按钮的状态。
`CTRL_BUTTON` 为控件类名调用 `CreateWindow` 函数,即可创建按钮控件。
## 1.1 按钮的类型和风格
### 1.1.1 普通按钮
普通按钮是一个矩形,其中显示了通过 `CreateWindow` 传递的窗口标题。该矩形占用了在 `CreateWindow` 调用中给出的全部高度和宽度,而文字位于矩形的中心。
按钮控件主要用来触发一个立即回应的动作,并且不会长久保持开关信息。这种形态的按钮控件有两种窗口风格,分别叫做 `BS_PUSHBUTTON``BS_DEFPUSHBUTTON``BS_DEFPUSHBUTTON` 中的 “DEF” 代表 “默认”。当用来设计对话框时,`BS_PUSHBUTTON` 风格和 `BS_DEFPUSHBUTTON` 风格的作用不同,具有 `BS_DEFPUSHBUTTON` 的按钮将是默认接收 `ENTER` 键输入的按钮,而不管当前的输入焦点处于哪个控件上。但是当用作普通主窗口的控件时,两种型态的按钮作用相同,只是具有 `BS_DEFPUSHBUTTON` 风格的按钮的边框要粗一些。
当鼠标光标处在按钮中时,按下鼠标左键将使按钮用三维阴影重画自己,就好像真的被按下一样。放开鼠标按键时,就恢复按钮的原貌,并产生 `BN_CLICKED` 通知,当按钮拥有输入焦点时,在文字的周围就有虚线,按下及释放空格键与按下及释放鼠标按键具有相同的效果。
>【提示】本指南对控件行为和表象的描述以默认的经典风格为准。
通常情况下,按钮文本会以单行的形式在垂直和水平方向居中显示,不会自动换行。不过,应用程序也可以通过指定 `BS_MULTLINE` 风格来指定显示多行文本。下面的程序段创建了两个普通按钮:
```c
CreateWindow (CTRL_BUTTON,
"Push Button",
WS_CHILD | BS_PUSHBUTTON | BS_CHECKED | WS_VISIBLE,
IDC_BUTTON,
10, 10, 80, 30, hWnd, 0);
CreateWindow (CTRL_BUTTON,
"Multiple Lines Push Button",
WS_CHILD | BS_PUSHBUTTON | BS_MULTLINE | WS_VISIBLE,
IDC_BUTTON + 1,
100, 10, 80, 40, hWnd, 0);
```
上述代码段建立的普通按钮的显示效果如__图 1.1__ 所示。注意在使用 `BS_MULTILINE` 风格之后,文本将垂直向上对齐。
![普通按钮](figures/Part4Chapter02-1.1.jpeg)
__图 1.1__ 普通按钮
另外,也可以在普通按钮上显示位图或图标,这时要使用 `BS_BITMAP` 或者 `BS_ICON` 风格,并通过 `CreateWindow` 函数的 `dwAddData` 参数传递位图对象的指针或图标句柄。默认情况下位图或图标会缩放显示以充满整个按钮窗口范围,使用 `BS_REALSIZEIMAGE` 风格将使位图或图标显示在控件中部不作任何缩放。下面的代码段建立了一个带位图的按钮其效果见__图 1.2__。
```c
hwnd = CreateWindow (CTRL_BUTTON,
"Close",
WS_CHILD | BS_PUSHBUTTON | BS_BITMAP |BS_REALSIZEIMAGE | BS_NOTIFY | WS_VISIBLE,
IDC_BUTTON + 4,
10, 300, 60, 30, hWnd, (DWORD) GetSystemBitmap (IDI_APPLICATION));
```
![位图按钮](figures/Part4Chapter02-1.2.jpeg)
__图 1.2__ 位图按钮
### 1.1.2 复选框
复选框是一个文字方块,文字通常出现在复选框的右边(如果你在建立按钮时指定了 `BS_LEFTTEXT` 风格,那么文字会出现在左边)。复选框通常用于允许用户对选项进行选择的应用程序中。复选框的常用功能如同一个开关:单击一次将显示选中标记,再次单击则会清除选中标记。
复选框最常用的两种风格是 `BS_CHECKBOX``BS_AUTOCHECKBOX`。在使用 `BS_CHECKBOX` 时,应用程序需要自己向该控件发送消息来设定选中标记;而使用 `BS_AUTOCHECKBOX` 风格时,控件会自动在选中和非选中状态之间切换。
其余两种复选框风格是 `BS_3STATE``BS_AUTO3STATE`,正如它们名字所暗示的,这两种风格能显示第三种状态——复选框内是灰色的,这种状态表明该复选框不能被选择或者禁止使用。
`BS_3STATE``BS_AUTO3STATE` 风格之间的区别和上面一样:前者需要应用程序来操作其状态,而后者由控件负责状态的自动切换。
默认情况下,复选框沿矩形的左边框对齐,并位于控件窗口范围的顶边和底边之间(垂直居中),在该矩形内的任何地方按下鼠标都会产生通知消息。使用 `BS_LEFTTEXT` 风格将使复选框靠右对齐,并将文本置于复选框的左边。用于文本对齐的风格 `BS_LEFT``BS_CENTER``BS_RIGHT``BS_TOP``BS_VCENTER``BS_BOTTOM` 等可用于复选框。
另外,使用 `BS_PUSHLIKE` 风格将使复选框以普通按钮的形式显示:选中时显示为按下状态,未选中时显示为正常状态。
下面的程序段创建了两个复选框其效果在__图 1.3__ 中。
```c
CreateWindow (CTRL_BUTTON,
"Auto 3-state check box",
WS_CHILD | BS_AUTO3STATE | WS_VISIBLE,
IDC_CHECKBOX,
10, 60, 150, 30, hWnd, 0);
CreateWindow (CTRL_BUTTON,
"Auto check box on left",
WS_CHILD | BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_RIGHT | WS_VISIBLE,
IDC_CHECKBOX + 1,
170, 60, 150, 30, hWnd, 0);
```
![复选框按钮](figures/Part4Chapter02-1.3.jpeg)
__图 1.3__ 复选框按钮
### 1.1.3 单选钮
单选按钮就像收音机上选台按钮一样,每一个按钮都对应一个频道,而且一次只能有一个按钮被按下。在对话框中,单选按钮组常常用来表示相互排斥的选项。与复选框不同,单选按钮的工作方式不同于开关,也就是说,当第二次按单选按钮时,它的状态会保持不变。
单选按钮的形状是一个圆圈,而不是方框,除此之外,它的行为很像复选框。圆圈内的加重圆点表示该单选按钮已经被选中。单选按钮有风格 `BS_RADIOBUTTON``BS_AUTORADIOBUTTON` 两种,后者会自动显示用户的选择情况,而前者不会。
默认情况下,单选按钮沿控件窗口的左边框对齐,并位于控件窗口范围的顶边和底边之间(垂直居中),在该矩形内的任何地方按下鼠标都产生通知消息。使用 `BS_LEFTTEXT` 风格将使单选按钮靠右对齐,并将文本置于按钮的左边。用于文本对齐的风格 `BS_LEFT``BS_CENTER``BS_RIGHT``BS_TOP``BS_VCENTER``BS_BOTTOM` 等可用于单选按钮。
另外,使用 `BS_PUSHLIKE` 风格将使单选按钮以普通按钮的形式显示选中时显示为按下状态未选中时显示为正常状态。下面的程序段创建了两个单选按钮其效果见__图 1.4__。
```c
CreateWindow (CTRL_BUTTON,
"Auto Radio Button 2",
WS_CHILD | BS_AUTORADIOBUTTON | WS_VISIBLE,
IDC_RADIOBUTTON + 1,
20, 160, 130, 30, hWnd, 0);
CreateWindow (CTRL_BUTTON,
"Auto Radio Button 2",
WS_CHILD | BS_AUTORADIOBUTTON | BS_LEFTTEXT | BS_RIGHT | WS_VISIBLE,
IDC_RADIOBUTTON + 4,
180, 160, 140, 30, hWnd, 0);
```
![单选按钮](figures/Part4Chapter02-1.4.jpeg)
__图 1.4__ 单选按钮
单选按钮通常成组使用,同一组单选按钮每一刻只能有一个被选中。在创建一组单选按钮时,我们需要设定它们的状态是互斥的,因此,要在创建第一个单选按钮时使用 `WS_GROUP` 风格,以将其设置为该组单选按钮的“打头按钮”。
## 1.2 按钮消息
应用程序通过给按钮发送消息来实现如下目的:
- 查询/设置复选框或者单选钮的选中状态:`BM_GETCHECK``BM_SETCHECK`
- 查询/设置普通按钮或者复选框的按下或释放状态:`BM_GETSTATE``BM_SETSTATE`
- 获取/设置位图按钮上的位图或者图标:`BM_GETIMAGE``BM_SETIMAGE`
- 发送 `BM_CLICK` 模拟用户鼠标的单击操作
应用程序向复选框或者单选钮发送 `wParam` 等于 `BST_CHECKED``BM_SETCHECK` 消息来显示其处于选中状态:
```c
SendMessage (hwndButton, BM_SETCHECK, BST_CHECKED, 0);
```
其实 `wParam` 可取的值一共有三个见__表 1.1__。这些值也是通过 `BM_GETCHECK` 消息返回的选中状态值。
__表 1.1__ 复选框和单选钮的选中状态
| 状态标识符 | 含义 |
|:----------------------|:-------|
|`BST_UNCHECKED`(0) |未选中 |
|`BST_CHECKED`(1) |已选中 |
|`BST_INDETERMINATE`(2) |不可用状态|
我们可以通过给窗口发送 `BM_SETSTATE` 消息来模拟按钮闪动。以下的操作将导致按钮被按下:
```c
SendMessage (hwndButton, BM_SETSTATE, BST_PUSHED, 0) ;
```
下面的调用使按钮恢复正常:
```c
SendMessage (hwndButton, BM_SETSTATE, 0, 0) ;
```
对位图按钮,可使用 `BM_GETIMAGE``BM_SETIMAGE` 消息获取或设置位图对象或图标句柄:
```c
int image_type;
PBITMAP btn_bmp;
HICON btn_icon;
int ret_val = SendMessage (hwndButton, BM_GETIMAGE, (WPARAM)&image_type, 0) ;
if (image_type == BM_IMAGE_BITMAP) {
/* 该按钮使用的是位图对象 */
btn_bmp = (PBITMAP) ret_val;
}
else {
/* 该按钮使用的是图标对象 */
btn_icon = (HICON) ret_val;
}
/* 将按钮图象设置为位图对象 */
SendMessage (hwndButton, BM_SETIMAGE, BM_IMAGE_BITMAP, btn_bmp) ;
/* 将按钮图象设置为图标对象 */
SendMessage (hwndButton, BM_SETIMAGE, BM_IMAGE_ICON, btn_icon) ;
```
另外,我们在应用程序中也可以通过向按钮发送 `BM_CLICK` 消息来模拟用户在按钮上的单击操作。
## 1.3 按钮通知码
具有 `BS_NOTIFY` 风格的按钮可产生的通知码主要有:
- `BN_CLICKED`:表明用户单击此按钮。该通知码的值为 0因此如果要在按钮的父窗口中处理该按钮发送过来的 `BN_CLICKED` 通知消息,只需判断 `MSG_COMMAND` 消息的 `wParam` 参数是否等于按钮的标识符即可。该通知的产生是默认的,将忽略按钮控件的 `BS_NOTIFY` 风格。
- `BN_PUSHED`:表明用户将此按钮按下。
- `BN_UNPUSHED`:表明用户将此按钮释放。
- `BN_DBLCLK`:表明用户在此按钮上进行了鼠标左键的双击操作。
- `BN_SETFOCUS`:表明按钮获得了输入焦点。
- `BN_KILLFOCUS`:表明按钮失去了输入焦点。
## 1.4 编程实例
通常,应用程序只需处理 `BN_CLICKED` 通知码,对复选框和单选钮,一般设置为自动状态,并在需要时发送 `BM_GETCHECK` 消息来获得选中状态。在对话框中应用程序还可以使用__表 1.2__ 中的函数来快速获得按钮控件的状态信息。
__表 1.2__ 对话框为处理按钮控件而提供的便利函数
| 函数名 | 用途 | 备注 |
|:--------------------|:---------------------------------|:----------|
|`CheckDlgButton` |通过按钮标识符来改变按钮的选中状态 | |
|`CheckRadioButton` |通过按钮标识符来改变一组单选钮的选中状态 |确保互斥选中 |
|`IsDlgButtonChecked `|通过标识符判断按钮是否选中 | |
__清单 1.1__ 所示的程序代码,给出了一个按钮控件的综合性使用范例。该程序使用一个对话框来询问用户的口味,通过分组单选框来选择喜欢的小吃类型,并通过复选框来选择用户的一些特殊口味。该程序的完整源代码请见本指南示例程序包 `mg-samples` 中的 `button.c` 文件其运行效果见__图 1.5__。
__清单 1.1__ 按钮控件的使用范例
```c
#include <stdio.h>
#include <stdlib.h>
#include <minigui/common.h>
#include <minigui/minigui.h>
#include <minigui/gdi.h>
#include <minigui/window.h>
#include <minigui/control.h>
#define IDC_LAMIAN 101
#define IDC_CHOUDOUFU 102
#define IDC_JIANBING 103
#define IDC_MAHUA 104
#define IDC_SHUIJIAO 105
#define IDC_XIAN 110
#define IDC_LA 111
#define IDC_PROMPT 200
static DLGTEMPLATE DlgYourTaste =
{
WS_BORDER | WS_CAPTION,
WS_EX_NONE,
120, 100, 300, 280,
"你喜欢吃哪种风味的小吃",
0, 0,
12, NULL,
0
};
static CTRLDATA CtrlYourTaste[] =
{
{
"static",
WS_VISIBLE | SS_GROUPBOX,
16, 10, 130, 160,
IDC_STATIC,
"可选小吃",
0
},
{
"button",
/* 使用 BS_CHECKED初始时使其选中 */
WS_VISIBLE | BS_AUTORADIOBUTTON | BS_CHECKED | WS_TABSTOP | WS_GROUP,
36, 38, 88, 20,
IDC_LAMIAN,
"西北拉面",
0
},
{
"button",
WS_VISIBLE | BS_AUTORADIOBUTTON,
36, 64, 88, 20,
IDC_CHOUDOUFU,
"长沙臭豆腐",
0
},
{
"button",
WS_VISIBLE | BS_AUTORADIOBUTTON,
36, 90, 88, 20,
IDC_JIANBING,
"山东煎饼",
0
},
{
"button",
WS_VISIBLE | BS_AUTORADIOBUTTON,
36, 116, 88, 20,
IDC_MAHUA,
"天津麻花",
0
},
{
"button",
WS_VISIBLE | BS_AUTORADIOBUTTON,
36, 142, 100, 20,
IDC_SHUIJIAO,
"成都红油水饺",
0
},
{
"static",
WS_VISIBLE | SS_GROUPBOX | WS_GROUP,
160, 10, 124, 160,
IDC_STATIC,
"口味",
0
},
{
"button",
WS_VISIBLE | BS_AUTOCHECKBOX,
170, 38, 88, 20,
IDC_XIAN,
"偏咸",
0
},
{
"button",
/* 使用 BS_CHECKED初始时使其选中 */
WS_VISIBLE | BS_AUTOCHECKBOX | BS_CHECKED,
170, 64, 88, 20,
IDC_LA,
"偏辣",
0
},
{
"static",
WS_VISIBLE | SS_LEFT | WS_GROUP,
16, 180, 360, 20,
IDC_PROMPT,
"西北拉面是面食中的精品,但街上的兰州拉面除外!",
0
},
{
"button",
WS_VISIBLE | BS_DEFPUSHBUTTON | WS_TABSTOP | WS_GROUP,
80, 220, 95, 28,
IDOK,
"确定",
0
},
{
"button",
WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
185, 220, 95, 28,
IDCANCEL,
"取消",
0
},
};
static char* prompts [] = {
"西北拉面是面食中的精品,但街上的兰州拉面除外!",
"长沙臭豆腐口味很独特,一般人适应不了。",
"山东煎饼很难嚼 :(",
"天津麻花很脆,很香!",
"成都的红油水饺可真好吃啊!想起来就流口水。",
};
static void my_notif_proc (HWND hwnd, int id, int nc, DWORD add_data)
{
/* 用户选择不同的小吃时,在下面的静态框中显示针对这种小吃的提示信息 */
if (nc == BN_CLICKED) {
SetWindowText (GetDlgItem (GetParent (hwnd), IDC_PROMPT), prompts [id - IDC_LAMIAN]);
}
}
static int DialogBoxProc2 (HWND hDlg, int message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case MSG_INITDIALOG:
{
int i;
/* 为小吃单选钮设定通知回调函数 */
for (i = IDC_LAMIAN; i <= IDC_SHUIJIAO; i++)
SetNotificationCallback (GetDlgItem (hDlg, i), my_notif_proc);
}
return 1;
case MSG_COMMAND:
switch (wParam) {
case IDOK:
case IDCANCEL:
EndDialog (hDlg, wParam);
break;
}
break;
}
return DefaultDialogProc (hDlg, message, wParam, lParam);
}
int MiniGUIMain (int argc, const char* argv[])
{
#ifdef _MGRM_PROCESSES
JoinLayer(NAME_DEF_LAYER , "button" , 0 , 0);
#endif
DlgYourTaste.controls = CtrlYourTaste;
DialogBoxIndirectParam (&DlgYourTaste, HWND_DESKTOP, DialogBoxProc2, 0L);
return 0;
}
#ifndef _MGRM_PROCESSES
#include <minigui/dti.c>
#endif
```
![按钮控件的使用范例](figures/Part4Chapter02-1.5.jpeg)
__图 1.5__ 按钮控件的使用范例