CWnd或CDialog-Create或CreateEx详解(包含窗口类型怎么填+动态创建窗口和控件例子)


前言:
静态创建窗口比较简单,都是资源文件,拖动控件,或支持xml配置的
那么如何动态创建窗口和控件呢?

本质:都是利用CreateWindowEx,CreateWindow

下面几点知道就差不多了:
[
1.CreateWindowEx动态创建窗口
CreateWindow动态创建控件

2.注册窗口类使用方法

3.窗口类型填什么(窗口和子窗口)

4.动态创建的窗口或控件响应

5.动态创建的窗口是非阻塞的,所以外边需要加消息循环
]

1动态创建窗口:  --CreateWindowEx
[
创建窗口:
                const WCHAR mywnd_class_name[] = L"Sample Window Class";

    WNDCLASS wc;
                memset(&wc, 0, sizeof(WNDCLASS));
    
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)::DefWindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = theApp.m_hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = L"";
    wc.lpszClassName = mywnd_class_name;
    RegisterClass(&wc);

    m_hMywnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, mywnd_class_name, L"mywnd", WS_POPUP | WS_OVERLAPPEDWINDOW, 0, 0, 800, 500, this->m_hWnd, nullptr, theApp.m_hInstance, nullptr);

显示:
                CWnd::FromHandle(m_hMywnd)->CenterWindow(this);
    ::ShowWindow(m_hMywnd, SW_SHOW);

说明:
1不注册类名,会导致创建失败;"WNDCLASS wc"后要初始化或者全都赋值,否则创建失败,初始化方法:WNDCLASS wc = {0};或者memset(&wc, 0, sizeof(WNDCLASS));
2使用默认响应使用::DefWindowProc,想自定义,就自己自定义一个,使用SetWindowLong,下面有
3.如果不需要标题等,上面参数直接填WS_POPUP就行了,其他可以自己绘制实现

4.CS_HREDRAW | CS_VREDRAW  --水平重绘,竖直重绘,不是水平,竖直

5.扩展类型
WS_EX_LEFT                     --CreateWindow是CreateWindowEx第一个参数为0的封装,0就是WS_EX_LEFT, 指定窗口具有左对齐属性。这是缺省值
WS_EX_APPWINDOW       --窗口有任务栏图标
WS_EX_TOOLWINDOW     --工具窗口不出现在任务条或用户按下ALT+TAB时出现的窗口中,一般弹出窗口设置这个
WS_EX_TOPMOST             --顶层
WS_EX_LAYERED               --分层或透明窗口,该样式可使用混合特效
WS_EX_TRANSPARENT      --透明的,这意味着,在这个窗口下面的任何窗口都不会被这个窗口挡住。用这个风格创建的窗口只有当它下面的窗口都更新过以后才接收WM_PAINT消息
其他不常用

所以,对一个默认的窗口,如果已经有父窗口时,如果不知道设置什么属性就设置WS_EX_LEFT 属性就行了,这是缺省属性,
也可以设置WS_EX_TOOLWINDOW
]


2动态创建子控件或子窗口:  --CreateWindow
[
响应函数:
static HWND g_hWndBtn;
static WNDPROC g_wndOrignProcBtn;
LRESULT APIENTRY BtnProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (uMsg == WM_LBUTTONDOWN)
    {
        ::MessageBox(NULL, 0, 0, MB_OK);
        return TRUE;
    }
    
    return CallWindowProc(g_wndOrignProcBtn, hwnd, uMsg, wParam, lParam);
}

创建:
g_hWndBtn = ::CreateWindowW(L"BUTTON", L"mybtn", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS , 0, 0, 0, 0, this->m_hWnd, nullptr, theApp.m_hInstance, nullptr);
if (g_hWndBtn)
{
    g_wndOrignProcBtn = (WNDPROC)SetWindowLong(g_hWndBtn,GWL_WNDPROC, (LONG)BtnProc);
}

位置和大小:在onsize中设置

说明:
1自定义的子窗口第一个参数类名填空,会导致创建窗口失败,所以要先注册窗口类名,然后再创建;如果想创建已经支持的控件,查看下面msdn网址
2变量用全局的, 原因是SetWindowLong(g_hWndBtn,GWL_WNDPROC, (LONG)BtnProc);的第三个参数只能设置为全局函数,设置为成员函数会导致编译不过,此时全局变量可以直接用
3响应方式不能使用窗口的Lbuttondown判断区域的方法,因为控件是实际窗口,鼠标被控件响应了
]

说明:
1所有控件、窗口,创建流程都是一样的,都是先注册类,然后创建,只不过有的接口封装了看不到而已,比如MFC的CWnd,CDialog
2窗口、控件都有类名,已经实现的控件的类名如下:
msdn参考:(含已有控件的类名)
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowa
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexa

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

1.MFC--所有的子控件、窗口,本质都是继承自CWnd,所以创建函数都是一样的,都是需要先注册窗口类,然后创建

创建窗口:
[
头文件:
CMyWnd *m_pMyWnd;

实现文件:
m_pMyWnd = new CMyWnd;
//m_pMyWnd->InitParam();
m_pMyWnd->CreateMyWindow(this->m_hWnd);

其中
bool CMyWnd::CreateMyWindow(HWND hWnd)
{
    m_hWndParent = hWnd;

    CString strWndClass = AfxRegisterWndClass(CS_VREDRAW | CS_HREDRAW, AfxGetApp()->LoadStandardCursor(IDC_ARROW), (HBRUSH)(COLOR_3DFACE + 1));
    CreateEx(WS_EX_TOOLWINDOW, strWndClass, NULL, WS_POPUP, CRect(0, 0, 0, 0), FromHandle(hWnd), 0);
    return true;
}

流程:
AfxRegisterWndClass注册窗口类
CreateEx创建

子控件的位置:在onsize中处理
]

---
创建子控件:
[
头文件:
CMyList *m_pWndList;

实现文件:
//创建列表
if (nullptr == m_pWndList)
{
    m_pWndList = new CMyList(this);
    //m_pWndList->InitParam();

    //m_pWndList->SetFont(m_fontText);
    //m_pWndList->SetBkColor(m_clrBackground);
    //m_pWndList->SetTextColor(m_clrText);
    //m_pWndList->SetTextHotColor(m_clrHotText);

    m_pWndList->Create(NULL, _T(""), WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_CHILDWINDOW | WS_VISIBLE | WS_EX_TOPMOST, CRect(0,0,0,0), this, 8000);
    m_pWndList->ShowWindow(SW_SHOW);
}

位置和大小:在onsize设置
]

----------
2.如何通过msdn找到创建窗口和子控件的代码?
bing搜索 "msdn createwindow"
->“Creating a Window - Win32 apps | Microsoft Docs”         --这是创建窗口
->“CreateWindowA macro (winuser.h) - Win32 apps”            -- CreateWindow使用
->“CreateWindowExA function (winuser.h) - Win32 apps”      --CreateWindowEx使用


1.Create,CreateEx的本质
[
关系:
[
#define CreateWindowA(lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)\
CreateWindowExA(0L, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)

#define CreateWindowW(lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)\
CreateWindowExW(0L, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)

#ifdef UNICODE
#define CreateWindow  CreateWindowW
#else
#define CreateWindow  CreateWindowA
#endif

msdn网址:https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowa

---
BOOL Create(
        PCWSTR lpWindowName,
        DWORD dwStyle,
        DWORD dwExStyle = 0,
        int x = CW_USEDEFAULT,
        int y = CW_USEDEFAULT,
        int nWidth = CW_USEDEFAULT,
        int nHeight = CW_USEDEFAULT,
        HWND hWndParent = 0,
        HMENU hMenu = 0
        )
    {
        WNDCLASS wc = {0};

        wc.lpfnWndProc   = DERIVED_TYPE::WindowProc;
        wc.hInstance     = GetModuleHandle(NULL);
        wc.lpszClassName = ClassName();

        RegisterClass(&wc);

        m_hwnd = CreateWindowEx(
            dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
            nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
            );

        return (m_hwnd ? TRUE : FALSE);
    }

参考msdn网址:https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexa
]

=>
[
MFC接口:
Create       --里面封装了CreateWindow
CreateEx    --里面封装了CreateWindowEx

win32接口:
CreateWindow  --里面封装了CreateWindowEx,第一个参数是0,即WS_EX_LEFT
CreateWindowEx  --最原始接口
]

总结:
[
最底层的接口是CreateWindowEx,
CreateWindow是对CreateWindowEx的封装,

MFC是对win32的封装,其中
Create是对CreateWindow的封装
CreateEx是对CreateWindowEx的封装
]
]

---
2.使用方式
动态创建窗口用CreateWindowEx 
动态创建控件用CreateWindow     
(有人可能不服,就要让两个都支持,寻找两个使用的方法,没必要,因为msdn有例子,按例子来就好了)

---
3.使用CreateWindowEx动态创建一个窗口:
msdn创建窗口的例子是用CreateWindowEx:

[
代码:
// Register the window class.
const wchar_t CLASS_NAME[]  = L"Sample Window Class";

WNDCLASS wc = { };

wc.lpfnWndProc   = WindowProc;
wc.hInstance     = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

// Create the window.

HWND hwnd = CreateWindowEx(
    0,                              // Optional window styles.
    CLASS_NAME,                     // Window class
    L"Learn to Program Windows",    // Window text
    WS_OVERLAPPEDWINDOW,            // Window style

    // Size and position
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

    NULL,       // Parent window    
    NULL,       // Menu
    hInstance,  // Instance handle
    NULL        // Additional application data
    );

if (hwnd == NULL)
{
    return 0;
}

ShowWindow(hwnd, nCmdShow);

网址:https://docs.microsoft.com/en-us/windows/win32/learnwin32/creating-a-window
]
---

上面的WindowProc这里给出:
代码:
[
LRESULT CALLBACK MainWndProc(
    HWND hwnd,        // handle to window
    UINT uMsg,        // message identifier
    WPARAM wParam,    // first message parameter
    LPARAM lParam)    // second message parameter

 
    switch (uMsg) 
    { 
        case WM_CREATE: 
            // Initialize the window. 
            return 0; 
 
        case WM_PAINT: 
            // Paint the window's client area. 
            return 0; 
 
        case WM_SIZE: 
            // Set the size and position of the window. 
            return 0; 
 
        case WM_DESTROY: 
            // Clean up window-specific data objects. 
            return 0; 
 
        // 
        // Process other messages. 
        // 
 
        default: 
            return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    } 
    return 0; 

int APIENTRY WinMain( 
    HINSTANCE hinstance,  // handle to current instance 
    HINSTANCE hinstPrev,  // handle to previous instance 
    LPSTR lpCmdLine,      // address of command-line string 
    int nCmdShow)         // show-window type 

    WNDCLASS wc; 
 
    // Register the main window class. 
    wc.style = CS_HREDRAW | CS_VREDRAW; 
    wc.lpfnWndProc = (WNDPROC) MainWndProc; 
    wc.cbClsExtra = 0; 
    wc.cbWndExtra = 0; 
    wc.hInstance = hinstance; 
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wc.hbrBackground = GetStockObject(WHITE_BRUSH); 
    wc.lpszMenuName =  "MainMenu"; 
    wc.lpszClassName = "MainWindowClass"; 
 
    if (!RegisterClass(&wc)) 
       return FALSE; 
 
    // 
    // Process other messages. 
    // 
 

网址:https://docs.microsoft.com/en-us/windows/win32/winmsg/using-window-procedures

]

这两个结合,就能写出一个具体的代码

注意:
偷工取巧的方法是新建一个win32工程,就能看到自动生成的代码了,工程及消息循环代码都可以直接参考

说明:CreateWindow,CreateWindowEx动态创建的窗口,不会阻塞,所以外边需要添加消息循环

---
4.使用CreateWindow动态创建一个按控件
msdn创建控件介绍的例子:
[
原理:利用CreateWindow里的lpClassName
如创建按钮:
[
cpp文件添加:
static HWND g_hWndBtn;
static WNDPROC g_wndOrignProcBtn;

LRESULT APIENTRY BtnProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (uMsg == WM_LBUTTONDOWN)
    {
        ::MessageBox(NULL, 0, 0, MB_OK);
        return TRUE;
    }
    
    return CallWindowProc(g_wndOrignProcBtn, hwnd, uMsg, wParam, lParam);
}

创建:  --L"BUTTON"是控件类名,默认支持的在msdn能找到,下面有网址
g_hWndBtn = ::CreateWindowW(L"BUTTON", L"mybtn", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS , 0, 0, 0, 0, this->m_hWnd, nullptr, theApp.m_hInstance, nullptr);
if (g_hWndBtn)
{
    g_wndOrignProcBtn = (WNDPROC)SetWindowLong(g_hWndBtn,GWL_WNDPROC, (LONG)BtnProc);//设置响应

    CRect rcClient;
    GetClientRect(rcClient);
    ::MoveWindow(g_hWndBtn, rcClient.left, rcClient.top, 80, 50, TRUE);
}

说明:
(1)变量用全局的, 原因是SetWindowLong(g_hWndBtn,GWL_WNDPROC, (LONG)BtnProc);的第三个参数只能设置为全局函数,设置为成员函数会导致编译不过,此时全局变量可以直接用
(2)响应方式不能使用窗口的Lbuttondown判断区域的方法,因为控件是实际窗口,鼠标被控件响应了

]

msdn参考:(含已有控件的类名)
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowa
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexa
]

]

---
5.窗口类型dwStyle,dwExStyle怎么填?
[
::CreateWindowExW(WS_EX_TOOLWINDOW, mywnd_class_name, L"mywnd", WS_POPUP, 0, 0, 800, 500, this->m_hWnd, nullptr, theApp.m_hInstance, nullptr);
::CreateWindowW(L"BUTTON", L"mybtn", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS , 0, 0, 0, 0, this->m_hWnd, nullptr, theApp.m_hInstance, nullptr);

一般情况下窗口:--CreateWindowEx
[
dwExStyle   -- 默认填WS_EX_LEFT,就是0,就是窗口具有左对齐属性;一般工程自动生成的都是WS_EX_APPWINDOW带有任务栏图标的;
                        自定义窗口填WS_EX_LEFT或WS_EX_TOOLWINDOW都行,有时会用到WS_EX_TOPMOST,但是WS_EX_TOPMOST类型的
                        注意父窗口隐藏或者按alt+tab后被其他窗口遮挡主窗口后,此窗口会始终显示在最顶层,所以要注意下这个问题,解决办法
                        是换成WS_EX_LEFT或WS_EX_TOOLWINDOW,不要用WS_EX_TOPMOST

dwStyle       --WS_POPUP就行,其他根据情况添加
]

子窗口或控件:--CreateWindowW
[
dwStyle       --填“WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS ”
]

msdn参考:
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowa
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexa

]

------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------

总结:

1.CreateWindowEx动态创建窗口
CreateWindow动态创建控件

2.注册窗口类使用方法

3.窗口类型填什么(窗口和子窗口)

4.动态创建的窗口或控件响应

5.动态创建的窗口是非阻塞的,所以外边需要加消息循环

说明:
1.因为SetWindowLong(g_hWndBtn,GWL_WNDPROC, (LONG)BtnProc);的第三个参数只能设置为全局函数或静态全局的,所以变量
设置为全局比较方便调用

2设置成员变量可以利用SetWindowLong(m_hWndBtn, GWL_USERDATA, (LONG)this);,然后获取时取指针用即可
但是调用此函数是在窗口创建后才能调用,所以响应的消息里面WM_CREATE是先响应的,此时还无法获取到指针

所以非要使用成员变量,就需要根据需要合理调整调用函数位置,导致显得繁琐,而直接设置全局变量方便,也不用考虑调用位置

个人感觉:用全局的方式最简单,肯定不会出问题,因为官方api给的接口就这样,msdn也这样写的(个人观点)

Demo地址:Demo-动态创建窗口
 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值