2013-03-06 30 views
2

我試圖創建一個WINAPI窗口創一流動態創建按鈕..在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); 
} 

在上面,它編譯得很好,並顯示窗口就好了..它只是不顯示我試圖動態添加到窗口的按鈕。有沒有人有任何想法,我可以動態地添加按鈕到窗口,並允許按鈕註冊消息?

+1

這不太可能是你的問題的原因,但我沒有理由在創建按鈕後發送'WM_CREATE'消息。 – chris 2013-03-06 14:49:46

+4

@chris不僅沒有理由,它明確地被禁止。系統發送'WM_CREATE'作爲創建的一部分。如果你手動發送,那麼你[惡作劇 - 調用窗口](http://blogs.msdn.com/b/oldnewthing/archive/2011/09/26/10216420.aspx)。 – 2013-03-06 15:20:36

+0

@RaymondChen,我對'WM_DESTROY'有些意識,但沒有'WM_CREATE'。我也認爲我以前不知道該怎麼讀:) – chris 2013-03-06 15:46:29

回答

3

有一些問題,但主要的是你有WM_PAINT處理程序。這將阻止主窗口繪製這個子窗口。評論它(並解決其他問題),你會沒事的。

  1. 螺紋 - 不確定這是否是一個問題。這完全不是標準。在主線程上創建主窗口(編輯:我相信線程的問題是你創建主窗口 - 並且在一個線程上有消息循環,而控制窗口在不同的線程中,這是不允許的)。
  2. Threaded爲假時,您的邏輯錯誤。你不能有消息循環。 WinForm的構造函數應該返回。否則,你永遠不會到Form.AddButton。
  3. 做消息循環作爲最後一件事在主
  4. 我認爲你沒有使用Visual Studio。有很多語法問題(微軟尚未實現的C++ 11的東西),Windows應用程序的主要功能被命名爲WinMain。這很好,但不推薦。微軟有一個很好的免費編譯器,如果你使用付費編譯器,你可以使用ATL,這非常棒。
  5. 沒有註釋掉WM_PAINT,你的按鈕被創建,但它不可見。你可以用Spy ++發現它
  6. 刪除case WM_PAIN:。你正在阻塞DefWindowProc,它將調用子窗口來指向自己。
  7. 第一次看到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); 
    } 

} 
+0

哦,我使用了lambda,因爲我不能在類中使用回調,除非它是靜態的,我不想根據需要靜態回調對於每個類的每個實例,每個回調都不相同。我已經添加了線程,因爲你是正確的阻塞。刪除循環固定它。 我使用int main,因爲它是我正在使用的控制檯應用程序,並且該類將產生多個窗口:)謝謝!花時間幫助我。 – Brandon 2013-03-06 21:40:38

+1

lambda - 那麼,讓我知道如果我失去了一些東西。使用代碼的當前狀態 - 每個實例的回調都是相同的。此外,由於閉包是空的,所以在回調中沒有狀態。我相信你不能在關閉中放入任何東西 - 因爲如果你有一個不平凡的關閉,那麼對Windows Proc的分配將不會成功。 WNDPROC確實沒有上下文(HWND除外),因此必須是靜態的。解決這個問題的標準方法是使用SetWindowLong,或者與ATL的thunk類似。讓我知道你是否有更好的東西。 – Uri 2013-03-07 09:48:14