在WINAPI中动态创建按钮

时间:2013-03-06 14:34:42

标签: c++ winapi

我正在尝试为WINAPI创建一个窗口创建器类。

我很难搞清楚如何让Window动态添加控件并为它们注册消息。

#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);
}

在上面,它编译很好,并显示窗口就好了。它只是没有显示我试图动态添加到窗口的按钮。有没有人知道如何动态添加按钮到窗口并允许按钮注册消息?

1 个答案:

答案 0 :(得分:3)

有很多问题,但主要问题是您拥有的WM_PAINT处理程序。这将阻止主窗口绘制此子窗口。评论出来(并解决其他问题),你会没事的。

  1. 螺纹 - 不确定这是否是一个问题。这完全不标准。在主线程上创建主窗口(编辑:我相信线程的问题是你创建主窗口 - 并且在一个线程上有消息循环,而控制窗口在不同的线程中。这是不允许的)。
  2. Threaded为假时的逻辑错误。你不能在那里有消息循环。 WinForm的构造函数应该返回。否则,你永远不会到达Form.AddButton。
  3. 将消息循环作为主要
  4. 中的最后一件事
  5. 我认为您没有使用Visual Studio。有许多语法问题(微软尚未实现c ++ 11的东西),Windows应用程序的主要功能名为WinMain。那很好,但不推荐。 Microsoft有一个优秀的免费编译器,如果您使用付费编译器,您可以使用非常棒的ATL。
  6. 在不注释WM_PAINT的情况下,您的按钮已创建,但不可见。你可以用Spy ++
  7. 发现它
  8. 删除case WM_PAIN:。您正在阻止DefWindowProc,它将调用子窗口指向自己。
  9. 我第一次看到Widnow Proc的lambda表示法。这是一个很好的技巧,但我真的没有理由。
  10. 下面的代码正在运行,并包含针对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);
        }
    
    }