Page 1 of 1

How to access "user data" from an event handler?

Posted: Tue Sep 20, 2011 5:00 pm
by Radek
I would like to process menu items in a single handler. I tried the following:

Code: Select all

  id = menu1->FindItem(wxT("aaa"));     // id is an int
  pp = menu1->FindItem(id);                 // pp is a wxMenuItem *
  Connect(
           id,
           wxEVT_COMMAND_MENU_SELECTED,
           wxCommandEventHandler(MyFrame::OnMenuItem),
           pp
         );
The "user data" parameter is wxObject * so that passing wxMenuItem * is okay. It compiles, running debugger shows that both id and pp have reasonable values. Now, the handler. If a can retrieve pp in it then I can reach both the menu item properties and the pointer to the menu the item belongs. Good. Therefore:

Code: Select all

void MyFrame::OnMenuItem( wxCommandEvent& event )
{
  wxMenuItem *pp   = reinterpret_cast<wxMenuItem *>(event.???);
  wxString    item = pp->GetItemLabel();

  ...
I have tried event.GetClientData(), event.GetClientObject() (not very promising but I was trying) but the debugger shows that pp = NULL. How can I reach pp from the event handler? Even if I can reach the menu item otherwise, how can I reach user data from the handler in general?

Re: How to access "user data" from an event handler?

Posted: Tue Sep 20, 2011 6:07 pm
by DavidHart
Hi,

See this thread, near the end (search for m_callbackUserData).

Regards,

David

Re: How to access "user data" from an event handler?

Posted: Tue Sep 20, 2011 6:26 pm
by RainRat
I think what you want is wxEvent::GetEventObject(). This is in the parent class of wxCommandEvent.

I would use dynamic_cast to cast the pointer and check that the event object is the correct type. It returns a NULL pointer if it is not of the specified type.

Code: Select all

void MyFrame::OnMenuItem( wxCommandEvent& event )
{
  wxMenuItem *pp = dynamic_cast<wxMenuItem *>(event.GetEventObject());
  if(pp) {
    wxString item = pp->GetItemLabel();
    // ...
  }
}

Re: How to access "user data" from an event handler?

Posted: Tue Sep 20, 2011 6:39 pm
by Radek
I've just tried GetEventObject(). It returns a non-null pointer but the pointer is different from the pointer to the wxMenuItem which should invoke the handler. A subsequent attempt to GetItemLabel() causes a GP fault so that the pointer does not point to a wxMenuItem most likely. The dynamic_cast will return NULL in my case, because the reinterpret_cast does.

Re: How to access "user data" from an event handler?

Posted: Tue Sep 20, 2011 7:23 pm
by RainRat
You should not use reinterpret_cast! static_cast can be used to downcast (from wxObject* to wxMenuItem*) if you are certain that the pointer will always actually point to the derived class. dynamic_cast with the NULL check is safest.

If dynamic_cast<wxMenuItem*> returns NULL, then the pointer is not of type wxMenuItem. Are you sure that your first calls to menu1->FindItem are returning valid id and wxMenuItem*?

Re: How to access "user data" from an event handler?

Posted: Wed Sep 21, 2011 3:15 pm
by Radek
The reinterpret_cast is okay here, RainRat. static_cast can create temporary objects so that I would lose control during a debug session. reinterpret_cast is a hint for the compiler: consider the memory at the pointer containing an object of the given class. No checking, no conversions, no other additional actions.

That's exactly what I want. I am passing wxMenuItem by means of Connect() and I want to retrieve this wxMenuItem in the handler. If I get a NULL pointer or a GP fault then something is wrong with my code. The pointer has not been passed for some reason (it I get NULL) or I have retrieved a pointer which I did not want (if I get GP fault). The question is why.

As to the FindItem() calls. They are okay. I have checked them by the debugger. At present, I am using event.GetId() for getting the menu item ID, then menu1->FindItem() for getting the corresponding wxMenuItem. It works but it isn't what I wanted.

Re: How to access "user data" from an event handler?

Posted: Wed Sep 21, 2011 3:44 pm
by doublemax
The "userData" passed to the Connect() call is written to the event table and later copied to the protected member variable wxEvent::m_callbackUserData. A comment in the header files says, it's for internal use only.

However, there is no method to retrieve that value, but i also don't see that it's used anywhere by wxWidgets internally.

Maybe you could make a feature request at http://trac.wxwidgets.org/ and ask for a public method to be added to wxEvent to read that value, so it can be used inside the event handler.

Re: How to access "user data" from an event handler?

Posted: Wed Sep 21, 2011 5:18 pm
by Radek
The data m_callbackUserData is even public but I hesitate to use it in my coding if it isn't mentioned in the official reference. The question seems to be why does Connect() have a parameter which cannot be utilized later? What are we supposed to pass using this parameter? And is the parameter even intended for passing some user data if it relates to "callback something"?

Oh, well ...

Re: How to access "user data" from an event handler?

Posted: Wed Sep 21, 2011 9:46 pm
by RainRat
Edit: From your other thread, I see that you already did this, but I posted before reading that thread.

Am I missing something here? Doesn't each of your wxMenuItems have a unique ID? Why can't you use the event id to get the wxMenuItem in your handler?

Code: Select all

void MyFrame::OnMenuItem( wxCommandEvent& event )
{
  int id = event.GetId();
  wxMenuItem *pp = menu1->FindItem(id);
  // or
  // wxMenuItem *pp = GetMenuBar()->FindItem(id);

  wxString item = pp->GetItemLabel();
  ...
}

Re: How to access "user data" from an event handler?

Posted: Thu Sep 22, 2011 7:30 am
by Radek
It's learning purposes and also a bit planning ahead. The FindItem() call needs a wxMenu object which the menu item ID belongs. As long as you have only one menu, this is no problem. If you have two or more popup menus then you need to find the wxMenu first.

It can be done by keeping a table with wxMenuItem IDs and the corresponding wxMenu pointers in the frame object. It can be done by assigning wxMenuItem IDs yourself and keeping info about the corresponding wxMenu in the IDs (IDs 100..199 = first menu, etc.) We know similar practices from "message loops" in winblows or OS/2. Isn't it possible to do it better in wxWidgets?

And the general question: Is it possible to pass user data from Connect() to the handler? :mrgreen: