C语言编写图形化界面-为按钮指定样式-监听按钮点击事件

前置章节

开始之前,需要学习以下章节:
创建窗口
窗口过程函数
创建按钮

指定窗口样式

CreateWindow函数的第三个参数是窗口样式,它是一个用于指定窗口的外观和行为的标志的组合。

这里我们需要使用一些预定义的符号常量,通过位掩码的形式将这些符号常量进行一个组合。

在上面的示例中,我们使用了如下的定义:

WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON

WS_VISIBLE (0x10000000):表示创建后控件可见。如果不设置此标志,控件将在创建后不可见。

WS_CHILD (0x40000000):表示创建为父窗口的子窗口。子窗口是在父窗口的客户区内显示的独立窗口。如果没有设置此标志,那么按钮将会独立弹出一个窗口,也就是自己单独成个家,与父窗口分家了。

BS_PUSHBUTTON (0x00000000):表示创建一个普通按钮样式。此样式用于创建一个可以按下和释放的按钮。看到其符号常量的十六进制值,想必我不说你也能看出来,这就是默认值,也就是说,就算我们不使用此标志,也是没有关系的,默认创建的就是这个。

如上所述,我们通过按位或操作符 | 将这些常量组合在一起,并且可以同时设置多个样式。

给按钮加边框

我们可以使用WS_BORDER (0x00800000)来为窗口创建一个边框。边框样式可以根据操作系统的外观进行绘制。

代码如下所示:

HWND hButton = CreateWindow("BUTTON", "确认", WS_VISIBLE | WS_CHILD | WS_BORDER,
                            50, 50, 100, 30,
                            hWnd, NULL, NULL, NULL);

在这里插入图片描述

扁平化按钮

使用BS_FLAT (0x8000)可以创建一个扁平的按钮,也就是没有凸起的边框。这个样式的按钮更趋近于我们现代的程序样式。
代码如下所示:

    HWND hButton = CreateWindow("BUTTON", "确认", WS_VISIBLE | WS_CHILD | BS_FLAT,
                                50, 50, 100, 30,
                                hWnd, NULL, NULL, NULL);

在这里插入图片描述

复选框样式按钮

使用BS_CHECKBOX (0x00000002)创建一个复选框样式的按钮。
代码如下:

    HWND hButton = CreateWindow("BUTTON", "确认", WS_VISIBLE | WS_CHILD | BS_CHECKBOX,
                                50, 50, 100, 30,
                                hWnd, NULL, NULL, NULL);

在这里插入图片描述

自动复选框

前面那个复选框,你可以发现,他点击后并没有勾选,使用BS_AUTOCHECKBOX (0x00000003)可以创建一个自动复选框样式的按钮。当用户单击按钮时,按钮的选中状态将自动切换。
代码如下:

    HWND hButton = CreateWindow("BUTTON", "确认", WS_VISIBLE | WS_CHILD |BS_AUTOCHECKBOX,
                                50, 50, 100, 30,
                                hWnd, NULL, NULL, NULL);

在这里插入图片描述

单选按钮

有了复选按钮,自然少不了单选按钮。使用BS_RADIOBUTTON (0x00000004)创建一个单选按钮样式的按钮。
代码如下:

    HWND hButton = CreateWindow("BUTTON", "确认", WS_VISIBLE | WS_CHILD |BS_RADIOBUTTON,
                                50, 50, 100, 30,
                                hWnd, NULL, NULL, NULL);

在这里插入图片描述

三态按钮

使用BS_3STATE (0x00000005)创建一个三态按钮。按钮可以处于选中、未选中和半选中三种状态之一。
代码如下:

    HWND hButton = CreateWindow("BUTTON", "确认", WS_VISIBLE | WS_CHILD |BS_3STATE,
                                50, 50, 100, 30,
                                hWnd, NULL, NULL, NULL);

在这里插入图片描述

自动三态按钮

显然,上面的三态按钮点击并不会有反应,我们可以使用BS_AUTO3STATE (0x00000006)创建一个自动三态按钮。按钮的状态会自动切换。
代码如下:

    HWND hButton = CreateWindow("BUTTON", "确认", WS_VISIBLE | WS_CHILD |BS_AUTO3STATE,
                                50, 50, 100, 30,
                                hWnd, NULL, NULL, NULL);

在这里插入图片描述

默认按钮样式(对话框Enter键)

使用BS_DEFPUSHBUTTON (0x00000001)创建一个默认的按钮,通常是对话框中的 Enter 键默认按钮。
在这里插入图片描述

设置按钮位置和大小

CreateWindow 函数的第四个参数是按钮控件的位置和大小。
代码如下:

    HWND hButton = CreateWindow("BUTTON", text, WS_VISIBLE | WS_CHILD |BS_FLAT,
                                20, 50, 100, 40,
                                hWnd, NULL, NULL, NULL);

如上所示,20, 50 是按钮的左上角位置的 x 和 y 坐标,而 100, 40 是按钮的宽度和高度。

在这里插入图片描述

封装函数

可以发现CreateWindow 函数的自由度还是比较高的,但函数名并不贴切,而且创建的时候需要填写的内容太多了,我们之前只是简单的进行了一个封装,使其完全丧失了自由度,接下来我们进行新一轮的封装,让其为我们后续的开发提供更大的便利。
代码如下:

typedef struct _Vector2
{
    int x;
    int y;
}Vector2;
void CreateButton(HWND hWnd,char *text,Vector2 position,Vector2 size);
void CreateButton(HWND hWnd,char *text,Vector2 position,Vector2 size)
{
    HWND hButton = CreateWindow("BUTTON", text, WS_VISIBLE | WS_CHILD |BS_FLAT,
                                position.x, position.y, size.x, size.y,
                                hWnd, NULL, NULL, NULL);
}

我们声明了一个Vector2类型,用于表示我们的二维坐标,用其修改我们的位置和大小。
然后弄一个字符指针,指向一个常量字符串,用于指定我们按钮上的文字。

最后的调用示例如下:

CreateButton(hWnd,"按钮",(Vector2){10,30},(Vector2){100,20});

可以发现,调用的代码量大大缩减,当然我们没有为其样式做形参,如果你有需求的话,也可以修改函数。

监听按钮点击事件

按钮ID

接下来我们开始监听按钮的点击事件,如果要监听按钮的点击事件,就需要为按钮提供一个控件ID,这个ID用于标识按钮,也就是说,未来我们可以通过这个ID,来监测是否是这个按钮被单击了。

接下来我们封装的函数添加一个新的变量controlId,类型为HMENU,用于表示按钮的ID,CreateWindow的倒数第三个参数,就是我们要放置的ID,只需要替换这个NULL即可。

void CreateButton(HWND hWnd, char *text, Vector2 position, Vector2 size, HMENU controlId)
{
    HWND hButton = CreateWindow("BUTTON", text, WS_VISIBLE | WS_CHILD | BS_FLAT,
                                position.x, position.y, size.x, size.y,
                                hWnd, controlId, NULL, NULL);
}

controlId变量的类型为HMENU,当然你也可以设置为int,然后再强制类型转换,不过这样会出现警告:(cast to pointer from integer of different size),这是因为在CreateButton中,你将controlId设置为了int类型,int转换为一个指针,强制转换后,编译器觉得不妥,所以才提出警告,但是不会出现运行问题。

若你不喜警报,那便要像我一样声明,只不过未来使用CreateButton时,要多一步强制类型转换。

创建按钮

接着,我们可以创建一个按钮,并设置它的id,之前我说过了,若你声明的是int类型controlid,那便不需要此时的强制类型转换。

// 创建一个按钮,并设置ID为100
        CreateButton(hWnd, "点我弹消息!", (Vector2){100, 100},(Vector2) {80, 30}, (HMENU)100);

这段代码还要放在WM_CREATE窗口消息中。

监听点击事件

接下来我们需要使用一个新的窗口消息,WM_COMMAND

// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
        break;
    case WM_COMMAND:
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}

现在我们需要处理按钮的BN_CLICKED通知消息。当用户点击按钮时,这个消息会被发送给父窗口。因此,在WndProc中添加对BN_CLICKED的处理:

case WM_COMMAND:
        if (LOWORD(wParam) == 100 && HIWORD(wParam) == BN_CLICKED) // 检查是否是ID为100的按钮被点击
        {
            MessageBox(hWnd, "按钮被点击了!", "信息", MB_OK);
        }
        break;

在上面示例中,监听到按钮被点击后,我们弹出一个消息窗口,提示用户点击了按钮。

这里注意,LOWORD与HIWORD并非是函数,而是宏,是一个类函数宏,但有宏参数的宏。

其定义如下:

#define LOWORD(l) ((WORD) (((DWORD_PTR) (l)) & 0xffff))
#define HIWORD(l) ((WORD) ((((DWORD_PTR) (l)) >> 16) & 0xffff))

这两个宏被定义在minwindef.h头文件中,但我们引用windows.h之后,这个头文件也会被包含。所以不需要单独引入这个头文件。

而BN_CLICKED则是一个宏定义,其定义如下:

#define BN_CLICKED 0

BN_CLICKED被定义在winuser.h头文件中,同样在引入windows.h后,也被一起引入了,不需要单独引入。

MB_OK也是宏定义,也在winuser.h中,定义如下:

#define MB_OK __MSABI_LONG(0x00000000)

其后面的宏定义如下,被定义在_mingw_mac.h头文件,也在windows.h中被一同引入:

#ifndef __MSABI_LONG
#  ifndef __LP64__
#    define __MSABI_LONG(x) x ## l
#  else
#    define __MSABI_LONG(x) x
#  endif
#endif

以上这些宏定义了解即可,不需要记忆,知道他们是宏定义便足矣,这里告诉大家定义,也只是为了更好的理解这些内容。

完整代码如下:

#include <windows.h>
typedef struct _Vector2
{
    int x;
    int y;
} Vector2;
// 声明窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void CreateButton(HWND hWnd, char *text, Vector2 position, Vector2 size, HMENU controlId);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    // 定义窗口类
    WNDCLASS wc = {0};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = "MyWinClass";

    // 注册窗口类
    if (!RegisterClass(&wc))
    {
        MessageBox(NULL, "窗口注册失败!", "错误", MB_ICONERROR);
        return 1;
    }

    // 创建窗口
    HWND hWnd = CreateWindow("MyWinClass", "我的窗口", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
                             NULL, NULL, hInstance, NULL);
    if (!hWnd)
    {
        MessageBox(NULL, "窗口创建失败!", "错误", MB_ICONERROR);
        return 1;
    }

    // 显示窗口
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
        // 创建一个按钮,并设置ID为100
        CreateButton(hWnd, "点我弹消息!", (Vector2){100, 100}, (Vector2){80, 30}, (HMENU)100);
        break;
    case WM_COMMAND:
        if (LOWORD(wParam) == 100 && HIWORD(wParam) == BN_CLICKED) // 检查是否是ID为100的按钮被点击
        {
            MessageBox(hWnd, "按钮被点击了!", "信息", MB_OK);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}

void CreateButton(HWND hWnd, char *text, Vector2 position, Vector2 size, HMENU controlId)
{
    HWND hButton = CreateWindow("BUTTON", text, WS_VISIBLE | WS_CHILD | BS_FLAT,
                                position.x, position.y, size.x, size.y,
                                hWnd, controlId, NULL, NULL);
}

接着我们运行程序,然后点击按钮,会弹出如下窗口:
在这里插入图片描述

  • 4
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水智

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值