我正在尝试为 WINAPI 创建一个窗口创建器类..
我一直在思考如何让窗口动态添加控件并为它们注册消息。
#include <windows.h>
#include <iostream>
#include <vector>
#include <tuple>
#include <thread>
using namespace std;
class WinForm
{
private:
HWND WindowHandle = nullptr;
std::thread Thread;
std::vector<std::tuple<std::string, std::size_t, HWND>> ControlHandles;
public:
~WinForm();
WinForm(std::string ClassName, std::string WindowName, bool Threaded = false, int Width = CW_USEDEFAULT, int Height = CW_USEDEFAULT, WNDPROC WindowProcedure = nullptr, WNDCLASSEX WndClass = {0});
bool AddButton(std::string ButtonName, POINT Location, int Width, int Height);
};
WinForm::~WinForm()
{
if (Thread.joinable())
{
Thread.join();
}
}
WinForm::WinForm(std::string ClassName, std::string WindowName, bool Threaded, int Width, int Height, WNDPROC WindowProcedure, WNDCLASSEX WndClass)
{
if (WindowProcedure == nullptr)
{
WindowProcedure = [](HWND window, UINT msg, WPARAM wp, LPARAM lp) -> LRESULT __stdcall
{
switch(msg)
{
case WM_PAINT:
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(window, msg, wp, lp);
}
return 0;
};
}
if (WndClass.cbSize == 0)
{
WndClass =
{
sizeof(WNDCLASSEX), CS_DBLCLKS, WindowProcedure,
0, 0, GetModuleHandle(nullptr), LoadIcon(nullptr, IDI_APPLICATION),
LoadCursor(nullptr, IDC_ARROW), HBRUSH(COLOR_WINDOW+1),
nullptr, ClassName.c_str(), LoadIcon (nullptr, IDI_APPLICATION)
};
}
if (RegisterClassEx(&WndClass))
{
if (Threaded)
{
Thread = std::thread([ClassName, WindowName, Width, Height, this]{
WindowHandle = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
if(WindowHandle)
{
MSG msg = {nullptr};
ShowWindow(WindowHandle, SW_SHOWDEFAULT);
while(GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
});
}
else
{
WindowHandle = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
if(WindowHandle)
{
MSG msg = {nullptr};
ShowWindow(WindowHandle, SW_SHOWDEFAULT);
while(GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
}
bool WinForm::AddButton(std::string ButtonName, POINT Location, int Width, int Height)
{
for (std::vector<std::tuple<std::string, std::size_t, HWND>>::iterator it = ControlHandles.begin(); it != ControlHandles.end(); ++it)
{
if (ButtonName == std::get<0>(*it))
{
return false;
}
}
std::size_t ID = 1;
for (std::vector<std::tuple<std::string, std::size_t, HWND>>::iterator it = ControlHandles.begin(); it != ControlHandles.end(); ++it, ++ID)
{
if (std::get<1>(*it) != ID)
{
break;
}
}
HWND ButtonHandle = CreateWindowEx(0, "Button", ButtonName.c_str(), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, Location.x, Location.y, Width, Height, WindowHandle, (HMENU)ID, GetModuleHandle(nullptr), nullptr);
ControlHandles.push_back(std::make_tuple(ButtonName, ID, ButtonHandle));
SendMessage(WindowHandle, WM_CREATE, 0, 0);
return true;
}
int main()
{
WinForm Form("Class", "Title", true);
Form.AddButton("NewButton", {50, 50}, 25, 25);
}
在上面,它编译得很好并显示了窗口。它只是没有显示我尝试动态添加到窗口的按钮。有谁知道如何动态地将按钮添加到窗口并允许按钮注册消息?
最佳答案
存在很多问题,但主要问题是您拥有的 WM_PAINT 处理程序。这将阻止主窗口绘制该子窗口。将其注释掉(并解决其他问题),就可以了。
- 线程化 - 不确定这是否是一个问题。这完全不标准。在主线程上创建主窗口(编辑:我相信线程的问题是您创建主窗口 - 并在一个线程上有消息循环,而控制窗口位于不同的线程中。这是不允许的)。
- 当
Threaded
为 false 时,您的逻辑是错误的。你不能在那里有消息循环。 WinForm 的构造函数应该返回。否则,您将永远无法到达 Form.AddButton。 - 将消息循环作为 main 中的最后一件事
- 我认为您没有使用 Visual Studio。有很多语法问题(微软还没有实现c++11的东西),Windows应用程序的主函数名为WinMain。这很好,但不推荐。 Microsoft 有一个优秀的免费编译器,如果您使用付费编译器,您可以使用 ATL,这非常棒。
- 如果不注释掉 WM_PAINT,您的按钮就会创建,但不可见。您可以使用 Spy++ 发现它
- 删除
案例WM_PAIN:
。您正在阻止 DefWindowProc,它将调用子窗口来指向自己。 - 我第一次看到 Widnow Proc 的 lambda 表示法。这是一个很好的技巧,但我真的看不出有什么理由。
下面的代码可以正常工作,并且包含针对 Visual Studio 2012 的修复。请注意,Microsoft 还没有初始化列表(这很糟糕)。不客气。
#include "stdafx.h"
using namespace std;
WNDCLASSEX defWndClass = { 0 };
class WinForm
{
private:
HWND WindowHandle;
std::thread Thread;
std::vector<std::tuple<std::string, std::size_t, HWND>> ControlHandles;
public:
~WinForm();
WinForm(std::string ClassName, std::string WindowName, bool Threaded = false, int Width = CW_USEDEFAULT,
int Height = CW_USEDEFAULT, WNDPROC WindowProcedure = nullptr, WNDCLASSEX WndClass = defWndClass);
bool AddButton(std::string ButtonName, POINT Location, int Width, int Height);
};
WinForm::~WinForm()
{
if (Thread.joinable())
{
Thread.join();
}
}
WinForm::WinForm(std::string ClassName, std::string WindowName, bool Threaded, int Width, int Height, WNDPROC WindowProcedure, WNDCLASSEX WndClass)
:WindowHandle(nullptr)
{
if (WindowProcedure == nullptr)
{
WindowProcedure = [](HWND window, UINT msg, WPARAM wp, LPARAM lp) -> LRESULT __stdcall
{
switch(msg)
{
/*
case WM_PAINT:
break;
*/
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CREATE:
break;
default:
return DefWindowProc(window, msg, wp, lp);
}
return 0;
};
}
if (WndClass.cbSize == 0)
{
WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = CS_DBLCLKS;
WndClass.lpfnWndProc = WindowProcedure;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = GetModuleHandle(nullptr);
WndClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
WndClass.hbrBackground = HBRUSH(COLOR_WINDOW+1);
WndClass.lpszMenuName = nullptr;
WndClass.lpszClassName = ClassName.c_str();
WndClass.hIconSm = LoadIcon( nullptr, IDI_APPLICATION);
}
if (RegisterClassEx(&WndClass))
{
if (Threaded)
{
// can't do that!
}
else
{
WindowHandle = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
if(WindowHandle)
{
ShowWindow(WindowHandle, SW_SHOWDEFAULT);
// don't put message loop here!
}
}
}
}
bool WinForm::AddButton(std::string ButtonName, POINT Location, int Width, int Height)
{
for (std::vector<std::tuple<std::string, std::size_t, HWND>>::iterator it = ControlHandles.begin(); it != ControlHandles.end(); ++it)
{
auto& tu = *it;
auto& str = std::get<0>(tu);
if( ButtonName.compare( str ) == 0 ) {
return false;
}
}
std::size_t ID = 1;
for (std::vector<std::tuple<std::string, std::size_t, HWND>>::iterator it = ControlHandles.begin(); it != ControlHandles.end(); ++it, ++ID)
{
if (std::get<1>(*it) != ID)
{
break;
}
}
HWND ButtonHandle = CreateWindowEx(
0, "button", ButtonName.c_str(), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, Location.x, Location.y, Width, Height,
WindowHandle, (HMENU)ID, (HINSTANCE)GetWindowLong(WindowHandle, GWL_HINSTANCE), nullptr);
ShowWindow( ButtonHandle, SW_SHOW );
ControlHandles.push_back(std::make_tuple(ButtonName, ID, ButtonHandle));
//SendMessage(WindowHandle, WM_CREATE, 0, 0);
return true;
}
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
WinForm Form("Class", "Title", false);
POINT pt = { 50, 50 };
Form.AddButton("NewButton", pt, 80, 50);
MSG msg = {nullptr};
while(GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
关于c++ - 在WINAPI中动态创建按钮,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15250094/