Can you connect a function pointer to a HMENU item in WINAPI?
我已经创建了一个菜单:
1 2 3 4 5 6 7 8 9 10 11 | 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); |
现在,我希望能够将每个菜单项连接到特定功能。
据我了解,经典方法是不发送
但是,由于我的项目是一个基于C的WinAPI库,所以如果我可以隐藏此原始数字的实现细节,而是将每个菜单项与一个函数指针连接,则希望使用。最终目标是使用户能够看到以下内容:
1 2 3 4 5 6 7 8 9 10 11 | 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); |
所以,我的问题是:
如何将
效率越高,方法越简单越好。
编辑:
我完全理解我可能想做的事完全是没有道理的,但这也是为什么我问这个问题。有人能指导我朝正确的方向走吗?如果我们查看其他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.
需要创建一个捕获
我的第一步是允许将普通的Windows消息(例如
"我的菜单"类具有MessageHandlingWindow的接口,通常这样称呼:
1 2 3 4 5 6 7 8 9 10 11 12 | //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是一个看起来像这样的接口(目前仅在仅消息窗口中实现)。如何实现(无论是仅消息窗口还是普通窗口)并不重要,但我更喜欢尽可能的重复(讨厌不得不重新实现窗口处理程序)。您会注意到我已经使用std :: bind来完成繁重的工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | 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的练习
编辑:
您不一定必须使用"仅消息窗口"(您可以通过调用SetMenu来将任何窗口与菜单相关联,尽管在我的实现中,我只有一个消息处理功能,并且它与"仅消息窗口"相关联(此操作是我与所有事件处理程序关联的窗口)。也许这不是每个人都这样做的方式,但这意味着一个人只需要编写一个事件处理程序即可。
另一种方法可能是封装所有窗口(因为我有消息窗口)并提供/注入单个处理程序。
有些框架使您更轻松。您可以看一下WTL