gpt4 book ai didi

c++ - 您可以将函数指针连接到 WINAPI 中的 HMENU 项吗?

转载 作者:行者123 更新时间:2023-11-28 04:57:15 25 4
gpt4 key购买 nike

我创建了一个菜单:

HMENU subm = CreateMenu();
AppendMenuA(subm, MF_STRING, NULL, "SubItem1");
AppendMenuA(subm, MF_STRING, NULL, "SubItem2");
AppendMenuA(subm, MF_STRING, NULL, "SubItem2");

HMENU menu = CreateMenu();
AppendMenuA(menu, MF_STRING, (UINT_PTR)subm, "Item1");
AppendMenuA(menu, MF_STRING, NULL, "Item1");
AppendMenuA(menu, MF_STRING, NULL, "Item1");

SetMenu(hwnd, menu);

现在我希望能够将这些菜单项中的每一个连接到特定功能。

据我了解,经典方法是不发送 NULL 作为第三个参数,而是发送一个标识号,以便处理 WM_COMMAND 中的消息windows WndProc 函数。

但是,由于我的项目是基于 C++ 的 WinAPI 库,所以我更愿意隐藏原始数字的实现细节,而是将每个菜单项与函数指针连接起来。最终目标是用户应该能够看到以下内容:

MenuStrip subMenuStrip;
subMenuStrip.Add("SubItem1", std::bind(&Window1::SubItem1_Click, this));
subMenuStrip.Add("SubItem2", std::bind(&Window1::SubItem2_Click, this));
subMenuStrip.Add("SubItem3", std::bind(&Window1::SubItem3_Click, this));

MenuStrip menuStrip;
menuStrip.Add("Item1", subMenuStrip);
menuStrip.Add("Item2", std::bind(&Window1::Item2_Click, this));
menuStrip.Add("Item3", std::bind(&Window1::Item3_Click, this));

window.MainMenu(menuStrip);

所以,我的问题是:

如何将 HMENU 中的每个项目连接到函数指针?

性能越高效、方法越简单越好。

编辑:

我完全明白我可能想做的事情是完全没有意义的,但这也是我问这个问题的原因。那么有人可以指导我正确的方向吗?如果我们查看其他 GUI 库/框架,例如 C# 中的 Windows 窗体,它们完全支持将函数分配给不同的事件,例如菜单点击。在 C++ 中实现它的正确方法是什么?

最佳答案

I've created a menu:

Now I want to be able to connect each of these menu items to specific functions.

需要创建一个窗口来捕获 WM_COMMAND消息(在我的实现中,我使用仅消息窗口)( use this as starting point )。通过获取 LOWORD 可以找到与特定菜单项关联的 ID的 wParam .您可以使用 lParam(如果您使用重复的 ID)来识别菜单实例(通常是封装菜单的类的 this 指针)。然后,您的封装器需要将每个 ID 与回调(派生自 std::function )相关联(例如 typedef std::map<EventID, std::function<void()>> CallbackMap )。

我的第一步是允许将普通 Windows 消息(例如 WM_COMMAND)与成员函数相关联,其中成员函数可以订阅 WM 消息。此后封装处理特定消息的类,例如 WM_TIMERWM_COMMAND .

我的 Menu 类有一个 MessageHandlingWindow 接口(interface),通常这样调用:

//Allows for scoping lifetime without knowing type...
struct ScopedResource{virtual ~ScopedResource(){}};

Menu
{
std::unique_ptr<ScopedResource> event_;
//...
Menu()
: event_(msgHandler_.createEvent(
WM_COMMAND,this, &Menu::onWM_Received))
{}
};

MessageHandler 是一个看起来像这样的接口(interface)(并且是根据仅消息窗口(目前)实现的。您如何实现它(根据仅消息或普通窗口)并不重要,但我更喜欢 little尽可能重复(讨厌必须重新实现窗口处理程序)。你会注意到我已经使用 std::bind 来完成繁重的工作:

class WindowMessageHandler
{
public:
typedef UINT MessageId;

typedef std::function<bool(WPARAM, LPARAM)> DefaultMessageHandler;
typedef std::function<bool(MessageId, WPARAM, LPARAM)> DefaultUnmappedMessageHandler;

typedef std::function<bool(WPARAM, LPARAM, LRESULT&)> MessageHandler;
typedef std::function<bool(MessageId, WPARAM, LPARAM, LRESULT&)> UnmappedMessageHandler;

template <class EventT> //NOTE: EvenT must be castable to MessageId.
void postMessage(EventT event, WPARAM wParam, LPARAM lParam)
{
PostMessage(getWindowHandle(), static_cast<MessageId>(event), wParam, lParam);
}

virtual HWND getWindowHandle() const = 0;

template <class MessageIdT, class ReceiverT>
std::unique_ptr<ScopedResource> createEvent(MessageIdT messageId, ReceiverT* receiver, bool (ReceiverT::*handler)(WPARAM, LPARAM, LRESULT&))
{
using namespace std::placeholders;
return addEventImpl(static_cast<MessageId>(messageId), MessageHandler{std::bind(handler, receiver, _1, _2, _3)});
}

template <class MessageIdT, class ReceiverT>
std::unique_ptr<ScopedResource> createEvent(MessageIdT messageId, ReceiverT* receiver, bool (ReceiverT::*handler)(WPARAM, LPARAM))
{
using namespace std::placeholders;
return addEventImpl(static_cast<MessageId>(messageId), DefaultMessageHandler{std::bind(handler, receiver, _1, _2)});
}

template <class MessageIdT, class MessageHandlerT>
std::unique_ptr<ScopedResource> createEvent(MessageIdT messageId, MessageHandlerT&& handler)
{
//Create temporary that will be moved...
return addEventImpl(static_cast<MessageId>(messageId), std::forward<MessageHandlerT>(handler));
}

template <class ReceiverT>
std::unique_ptr<ScopedResource> createUnmappedEvent(ReceiverT* receiver, bool (ReceiverT::*handler)(MessageId, WPARAM, LPARAM, LRESULT&))
{
using namespace std::placeholders;
return addUnmappedEventImpl(std::bind(handler, receiver, _1, _2, _3, _4));
}

template <class ReceiverT>
std::unique_ptr<ScopedResource> createUnmappedEvent(ReceiverT* receiver, bool (ReceiverT::*handler)(MessageId, WPARAM, LPARAM))
{
using namespace std::placeholders;
return addUnmappedEventImpl(std::bind(handler, receiver, _1, _2, _3));
}

template <class MessageHandlerT>
std::unique_ptr<ScopedResource> createUnmappedEvent(MessageHandlerT&& handler)
{
//Create temporary that will be moved...
return addUnmappedEventImpl(std::forward<MessageHandlerT>(handler));
}

protected:
virtual ~WindowMessageHandler() {}

private:
std::unique_ptr<ScopedResource> addEventImpl(MessageId messageId, const MessageHandler& messageHandler)
{
//Creating rvalue-ref
return addEventImpl(messageId, MessageHandler{messageHandler});
}

std::unique_ptr<ScopedResource> addEventImpl(MessageId messageId, const DefaultMessageHandler& defaultHandler)
{
//Creating rvalue-ref
return addEventImpl(
messageId,
MessageHandler{[defaultHandler](WPARAM wp, LPARAM lp, LRESULT& result) {
bool handled = defaultHandler(wp, lp);
if (handled) {
result = 0;
}
return handled;
}}
);
}

std::unique_ptr<ScopedResource> addUnmappedEventImpl(const UnmappedMessageHandler& messageHandler)
{
//Creating rvalue-ref
return addUnmappedEventImpl(UnmappedMessageHandler{messageHandler});
}

std::unique_ptr<ScopedResource> addUnmappedEventImpl(const DefaultUnmappedMessageHandler& defaultHandler)
{
//Creating rvalue-ref
return addUnmappedEventImpl(
UnmappedMessageHandler{[defaultHandler](MessageId messageId, WPARAM wp, LPARAM lp, LRESULT& result) {
bool handled = defaultHandler(messageId, wp, lp);
if (handled) {
result = 0;
}
return handled;
}}
);
}

virtual std::unique_ptr<ScopedResource> addEventImpl(MessageId messageId, MessageHandler&& messageHandler) = 0;
virtual std::unique_ptr<ScopedResource> addUnmappedEventImpl(UnmappedMessageHandler&& messageHandler) = 0;
};

我将把实现作为 OP 的练习留给 OP

编辑:

您不一定必须使用仅消息窗口(您可以通过调用 SetMenu 将任何窗口与您的菜单相关联,尽管在我的实现中我只有一个消息处理函数,它与一个仅消息窗口相关联(这是我与所有事件处理程序相关联的窗口)。这可能不是每个人都这样做的方式,但这意味着只需要编写一个事件处理程序。

另一种方法可能是封装所有窗口(因为我有消息窗口)并提供/注入(inject)单个处理程序。

有些框架可以让您更轻松。你可以看看 WTL

关于c++ - 您可以将函数指针连接到 WINAPI 中的 HMENU 项吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46887484/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com