First off, I want to say, for anyone who is going to say "why am I reinventing the wheel", I am doing this for fun, and for a project of mine I'm currently working on.
As you can see from the code below, I am trying to dynamically create a window and a button, but something I'm having trouble with is adding a function to that button when it's clicked.
I know it would be really simple to go into the window procedure in WM_COMMAND
and do so there, but that's missing the whole point of what I'm trying to accomplish here, have it so I can simple call btn.add(params)
to a certain window, and add a certain function to that button by calling say btn.click(function);
without having to go into the window procedure itself when adding the controls.
How would I accomplish this?
#include <Windows.h>
#include <vector>
#include <thread>
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);
}
}