所以我正在编写自己的小GUI框架,将Windows API包装在有用的类中。我目前正试图让用户以面向对象的方式处理WndProc的消息,但我遇到了一些障碍。
我有一个Button
类,它继承自一个抽象的Control
类,它几乎是Button的HWND句柄的包装器。
还有一个Window
类,它包含(你知道什么)窗口的句柄。该类还有一个Control
的容器,负责创建自己的(静态)WndProc方法。
我要做的是在包含窗口的WndProc中有一个很大的switch语句,它基于函数的wParam和lParam参数调用适当的发送控件的处理函数。我看到大多数人做的事情是这样的:
#define MY_BUTTON 101 //Define button's ID
Button::Button()
{
hwndButton= CreateWindow(
"BUTTON", text,
WS_VISIBLE | WS_CHILD,
position.x, position.y,
size.x, size.y,
parent_handle,
(HMENU)MY_BUTTON,
GetModuleHandle(NULL),
NULL);
}
Button* button = new Button();
//Later on, in the Window class...
LRESULT CALLBACK Window::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch((LOWORD)wParam)
{
case MY_BUTTON:
button->HandleMessage(&msg);
break;
}
}
但是,我不希望用户为他们创建的每个对象实例分配唯一的整数。 相反,因为我有一个控件容器,我宁愿做这样的事情:
//In the Window Class...
Button* button = new Button();
AddControl(button);
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(&msg)
{
ObtainControlFromParams(wParam, lParam)->HandleMessage(&msg);
//ObtainControlFromParams should return a pointer to a Control,
//and it should look into the window's Control collection.
}
}
虽然听起来很棒,但我找不到实现ObtainControlFromParams功能的方法。
我想要区分每个控件实例的方法是有一个字符串,我称之为控件的“名称”,它应该对每个对象实例都是唯一的。这会让我有两个选择(我能想到)
如果我想要获得的内容不是很清楚,我很抱歉,这对我来说是一个复杂的概念。
非常感谢任何帮助!
答案 0 :(得分:5)
将按钮的Control*
对象指针存储在按钮HWND
中,消息过程可以在其中检索它。您可以使用(Set|Get)WindowLongPtr(GWL_USERDATA)
或(Get|Set)SetProp()
来实现此目的。
只有某些消息(例如WM_COMMAND
和WM_NOTIFY
)会识别发送它们的控件。这些消息被发送到控件的父窗口。这些消息包含子控件HWND
。父母可以检索孩子的Control*
并将信息转发给她。
其他消息直接发送到控件自己的窗口,而不是其父窗口。这些消息不识别它们被发送到的控件。因此,每个Control
都需要为自己的HWND
创建自己的WndProc过程。在SetWindowLongPtr(GWL_WNDPROC)
被调用后,使用SetWindowSubclass()
或HWND
将WndProc分配给CreateWindow()
。
尝试类似这样的事情(这非常粗糙,在构建UI框架时涉及更多内容,但这应该会给你一些想法):
typedef std::basic_string<TCHAR> tstring;
class Control
{
private:
HWND fWnd;
Control *fParent;
POINT fPosition;
SIZE fSize;
tstring fText;
std::list<Control*> fControls;
...
void addControl(Control *c);
void removeControl(Control *c);
virtual void createWnd() = 0;
void internalCreateWnd(LPCTSTR ClassName, DWORD style, DWORD exstyle);
...
static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
protected:
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
...
public:
Control();
virtual ~Control();
HWND getWnd();
void destroyWnd();
void wndNeeded();
Control* getParent();
void setParent(Control *value);
POINT getPosition();
void setPosition(POINT value);
SIZE getSize();
void setSize(SIZE value);
tstring getText();
void setText(const tstring &value);
...
};
static const LPTSTR szControlPropStr = TEXT("ControlPtr")
Control* ControlFromWnd(HWND hwnd)
{
return (Control*) GetProp(hwnd, szControlPropStr);
}
Control::Control()
: fWnd(0), fParent(0)
{
fPosition.x = fPosition.y = 0;
fSize.cx = fSize.cy = 0;
...
}
Control::~Control()
{
setParent(0);
destroyWnd();
}
void Control::addControl(Control *c)
{
fControls.push_back(c);
c->fParent = this;
if (fWnd)
c->wndNeeded();
}
void Control::removeControl(Control *c)
{
fControls.remove(c);
c->destroyWnd();
c->fParent = 0;
}
HWND Control::getWnd()
{
wndNeeded();
return fWnd;
}
void Control::internalCreateWnd(LPCTSTR ClassName, DWORD style, DWORD exstyle)
{
style |= WS_VISIBLE;
if (fParent)
style |= WS_CHILD;
fWnd = CreateWindowEx(exstyle,
ClassName, fText.c_cstr(), style,
fPosition.x, fPosition.y,
fSize.cx, fSize.cy,
(fParent) ? fParent->getWnd() : 0,
0,
GetModuleHandle(NULL),
NULL);
SetProp(fWnd, szControlPropStr, (HANDLE)this);
SetWindowLongPtr(fWnd, GWL_WNDPROC, (LONG_PTR)&Control::WndProc);
}
void Control::destroyWnd()
{
DestroyWindow(fWnd);
fWnd = 0;
}
void Control::wndNeeded()
{
if (!fWnd)
{
createWnd();
for (std::list<Control*>::iterator iter = fControls.begin(); iter != fControls.end(); ++iter)
iter->wndNeeded();
}
}
Control* Control::getParent()
{
return fParent;
}
void Control::setParent(Control *value)
{
if (fParent != value)
{
if (fParent)
fParent->removeControl(this);
fParent = value;
if (fParent)
fParent->addControl(this);
}
}
POINT Control::getPosition()
{
return fPosition;
}
void Control::setPosition(POINT value)
{
fPosition = value;
if (fWnd)
SetWindowPos(fWnd, 0, fPosition.x, fPosition.y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
SIZE Control::getSize()
{
return fSize;
}
void Control::setSize(SIZE value)
{
fSize = value;
if (fWnd)
SetWindowPos(fWnd, 0, 0, 0, fSize.cx, fSize.cy, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
}
tstring Control::getText()
{
return fText;
}
void Control::setText(const tstring &value)
{
fText = value;
if (fWnd)
SetWindowText(fWnd, fText.c_str());
}
LRESULT CALLBACK Control::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Control *pThis = ControlFromWnd(hwnd);
if (pThis)
return pThis->HandleMessage(uMsg, wParam, lParam);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT Control::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
WM_NCDESTROY:
{
RemoveProp(fWnd, szControlPropStr);
fWnd = 0;
break;
}
case WM_COMMAND:
{
HWND hwnd = (HWND) lParam;
if ((hwnd) && (hwnd != fWnd))
{
Control *c = ControlFromWnd(hwnd);
if (c)
return c->HandleMessage(uMsg, wParam, lParam);
}
...
break;
}
case WM_NOTIFY:
{
NMHDR *hdr = (NMHDR*) lParam;
if ((hdr->hwndFrom) && (hdr->hwndFrom != fWnd))
{
Control *c = ControlFromWnd(hdr->hwndFrom);
if (c)
return c->HandleMessage(uMsg, wParam, lParam);
}
...
break;
}
case WM_WINDOWPOSCHANGED:
{
WINDOWPOS *p = (WINDOWPOS*) lParam;
if (!(p->flags & SWP_NOMOVE))
{
fPosition.x = p->x;
fPosition.y = p->y;
}
if (!(p->flags & SWP_NOSIZE))
{
fSize.cx = p->cx;
fSize.cy = p->cy;
}
...
return 0;
}
case WM_SETTEXT:
{
LRESULT ret = DefWindowProc(fWnd, uMsg, wParam, lParam);
if (ret == TRUE)
fText = (TCHAR*) lParam;
return ret;
}
...
}
return DefWindowProc(fWnd, uMsg, wParam, lParam);
}
class Button : public Control
{
protected:
virtual void createWnd();
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
public:
Button();
};
Button::Button()
: Control()
{
}
void Button::createWnd()
{
Control::intetnalCreateWnd(TEXT("BUTTON"), BS_PUSHBUTTON | BS_TEXT, 0);
...
}
LRESULT Button::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_COMMAND:
{
HWND hwnd = (HWND) lParam;
if (hwnd != fWnd)
break;
switch (HIWORD(wParam))
{
case BN_CLICKED:
{
...
return 0;
}
...
}
break;
}
}
return Control::HandleMessage(uMsg, wParam, lParam);
}
//In the Window Class...
Button* button = new Button();
button->setPosition(...);
button->setSize(...);
button->setText(...);
button->setParent(this);
答案 1 :(得分:5)
通常的方法是反映消息,例如WM_COMMAND
和WM_NOTIFY
,这些消息会发送到控件的父窗口,返回给发送它们的控件。这是可能的,因为这些消息标识发件人(每个都以独特的方式,因此请检查文档)。
有许多方法可以将C ++对象指针与窗口关联起来:
动态生成的蹦床功能为window-proc
最有效但也最棘手。可能需要使用VirtualAlloc
。
窗口属性。
SetProp
和GetProp
函数。
窗口对象词
SetWindowLongPtr
。需要确保已经分配了空间。
静态地图。
例如。单身std::unordered_map
,映射句柄→对象。
Windows标准子类化使用的任何内容
即使用SetWindowSubclass
。