huidong

首页 | 会员登录 | 关于争取 2022 寒假做出汇东网 Ver3.0.0 !
搜索文章


    实验过程

    win32的每个控件都可以叫做一个“窗口”,下面这个代码不陌生了,创建一个edit控件:

CreateWindow(L"edit", L"Edit at here.",
                WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER,
                130, 50, 200, 20,
                hwnd, (HMENU)IDC_EDIT, hInstance, NULL);

    然而,我发现参数style可以是WS_CAPTION

    于是……


    WS_CAPTION  万 恶 之 源


    当你在一个控件的style中加入WS_CAPTION时……(此处用static控件举例)

#define IDC_BTN 101
#define IDC_BOX 102

// 创建static控件的“窗口”
HWND hBox = CreateWindow(L"static", L"xx",
                WS_CHILD | WS_VISIBLE | WS_CAPTION,
                10, 10, 300, 300,
                hwnd, (HMENU)IDC_BOX, hInstance, NULL);
    
// 在窗口内创建一个子控件
HWND hBtn = CreateWindow(L"button", L"Click Me!",
                WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER,
                50, 50, 100, 20, hBox, (HMENU)IDC_BTN, hInstance, NULL);


    它活了。

    它拥有了一个窗口标题栏,而且还有一个子控件。于是,一个static控件顺理成章地变成了交互窗口,而且,这个窗口只能在主窗口中使用。

图片.png

    值得一提的是,static控件创造的窗口不能够拖动,但是窗口内的子控件可以正常使用。


    把控件类型从static换成button后,窗口的整体是一个button,它的子控件仍可以正常使用,如果你点击了大的button,小的button会因为重绘而被覆盖。

    图片.png


    注意!不是所有的控件都可以这么玩的,比如:

    下拉框COMBOBOX,用它的话窗口标题栏是出不来的,而且窗口会被锁住,子控件也无法使用;

    滚动条SCROLLBAR,使用的时候会出现鬼畜情况,体验极差。

    还有其它控件,没有试。



    那么,可以把这样的“窗口”的子控件也变成“窗口”吗?当然可以。这样的窗口可以无限套娃!


    下面的代码进行了套娃:

            #define IDC_EDIT 100
            #define IDC_BTN 101
            #define IDC_BOX 102
            
            HWND hEdit;
            HWND hBtn;
            HWND hBox;
            
            hEdit = CreateWindow(L"edit", L"Edit at here.",
                WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER,
                130, 50, 200, 20,
                hwnd, (HMENU)IDC_EDIT, hInstance, NULL);



            hBox = CreateWindow(L"message", L"first",
                WS_CHILD | WS_VISIBLE  | WS_CAPTION | CS_VREDRAW | CS_HREDRAW,
                330, 50, 400, 400,
                hwnd, (HMENU)IDC_BOX, hInstance, NULL);

            
            hBtn = CreateWindow(L"button", L"Click Me!",
                WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER,
                50, 50, 100, 20,
                CreateWindow(L"message", L"third",
                    WS_CHILD | WS_VISIBLE  | WS_CAPTION,
                    10, 10, 200, 200,
                    CreateWindow(L"message", L"second",
                        WS_CHILD | WS_VISIBLE  | WS_CAPTION,
                        10, 10, 300, 300,
                        hBox, (HMENU)IDC_BOX+1, hInstance, NULL)
                    , (HMENU)IDC_BOX+2, hInstance, NULL)
                , (HMENU)IDC_BTN, hInstance, NULL);


效果:

图片.png

    根据上图,可以知道:

    1. 主窗口的子窗口不能拥有Aero效果。

    2. 子窗口的子窗口不能拥有Basic效果,只能拥有经典效果。

    3. 其实子窗口中的子窗口在拖动时会出现重绘残留的情况,图中擦除了重绘残留,故看不出来。


    还有更奇葩的一点是:上面的程序放到win10里运行,会卡出win7界面!

    图片.png


    可以看出,主窗口是win10,子窗口开始变成win7窗口了!!

    这一波,有图有真相,有码有真相,可以自己试试,不过不晓得微软啥时候就把它修复了。


    窗体也可以够拥有关闭按钮,放大按钮,最小化按钮,只需要将WS_CAPTION改为WS_OVERLAPPEDWINDOW即可。

    这样,做出来的效果和windows的MDI也差不多了:

    1610708661126823.png


实用建议


    要利用win32这一特性,现对上述实验结果做一个总结:

    1. 要使用子窗口,可以在控件的style参数中加入WS_CAPTION

    2. 选择怎样的控件类作为子窗口也是很重要的一点

        2.1 如果希望窗口不能够移动,推荐使用static,但是它和其它基础控件有个共同缺点是:窗口标题和控件标题是相同的,设置窗口标题会影响到控件文本。

        2.2 请不要选择如下拉框(COMBOBOX),滚动条(SCROLLBAR)等钉子控件,用不了的。

        2.3 如果希望窗口可以移动,类名请设置为message,它所创造出的窗口的客户区的全白的,设置它的窗口标题不会影响到窗口背景内容。

    3. 多次套娃,建议不要。上述实验可以总结出经验:套娃到第三次时,重绘有残留,而且窗口样式变成了经典。

    4. 非常重要:此操作不能取代win32原生MDI,因为经过测试,该操作无法获取子窗口内的控件消息,所以该操作仅适用于无需人机交互的情况。或者可以进行自绘控件,然后响应。


完整可运行代码:

VS2019

// WindowsProject1.cpp : 定义应用程序的入口点。
//

#include "framework.h"
#include "WindowsProject1.h"

#define MAX_LOADSTRING 100


#define IDC_EDIT 100
#define IDC_BTN 101
#define IDC_BOX 102

// 全局变量:
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

// 此代码模块中包含的函数的前向声明:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此处放置代码。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化:
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT1));

    MSG msg;

    // 主消息循环:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int)msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目标: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hInst = hInstance; // 将实例句柄存储在全局变量中

    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目标: 处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:

        HWND hEdit;
        HWND hBtn;
        HWND hBox;

        hEdit = CreateWindow(L"edit", L"Edit at here.",
            WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER,
            130, 50, 200, 20,
            hWnd, (HMENU)IDC_EDIT, hInst, NULL);



        hBox = CreateWindow(L"message", L"first",
            WS_CHILD | WS_VISIBLE | WS_CAPTION | CS_VREDRAW | CS_HREDRAW,
            330, 50, 400, 400,
            hWnd, (HMENU)IDC_BOX, hInst, NULL);


        hBtn = CreateWindow(L"button", L"Click Me!",
            WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER,
            50, 50, 100, 20,
            CreateWindow(L"message", L"third",
                WS_CHILD | WS_VISIBLE | WS_CAPTION,
                10, 10, 200, 200,
                CreateWindow(L"message", L"second",
                    WS_CHILD | WS_VISIBLE | WS_CAPTION,
                    10, 10, 300, 300,
                    hBox, (HMENU)IDC_BOX + 1, hInst, NULL)
                , (HMENU)IDC_BOX + 2, hInst, NULL)
            , (HMENU)IDC_BTN, hInst, NULL);

        break;

    case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);
        // 分析菜单选择:
        switch (wmId)
        {

        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: 在此处添加使用 hdc 的任何绘图代码...
        EndPaint(hWnd, &ps);
    }
    break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}




返回首页


Copyright (C) 2018-2024 huidong