Contents Up Previous Next

How can I have class member functions as callbacks for buttons?

Here's some correspondence on the subject.

Date: Thu, 18 Nov 1993 11:15:44 +0200
From: Syst{ Kari <ks@fi.tut.cs>
Message-Id: <199311180915.AA06005@karikukko.cs.tut.fi>
To: wxwin-users@ed.aiai, tane@fi.uta.cs
Subject: Re: member function as wxFunction

> > is it true that i can't use a member function of a class as a callback
> > function?
> 
> Yes, unless you declare your member function as static. But then you
> don't have this-pointer available in your function.

There is actually a way. The I first define a new button class:

void my_text_callback (wxObject &obj, wxEvent &ev);

// The typedef for a callback member.
// I found something like this from the Solbource OI include files,
// I just copied it without fully understanding the corresponding
// C++ rule.
typedef void (wxObject::callback_member_func)(...);

// A class with a possibility to send callback method calls to other
// objects/
class MyButton: public wxButton {
public:
  wxObject *client_obj;
  callback_member_func* mem_func;
  MyButton(wxPanel *panel, wxObject *client,
	 callback_member_func *mem, char *label) :
  wxButton(panel, my_text_callback, label)
    {
    client_obj = client;
    mem_func = mem;
  };
};
    
void my_text_callback (wxObject &obj, wxEvent &ev) {

/* This is a terrible glude. The member 'mem_func' should
   should defined at the top of inheritance hierarchy */
  MyButton &o = (MyButton &) obj;
  wxObject *to = o.client_obj;
  (to->*(o.mem_func))(obj,ev);
};

Now, I can set a call back which is a object+method pair (see "new MyButton"):

#include <stream.h>
#include <wx.h>
#include "mybutton.h"

wxText *text;

class MyText: public wxText { // A new class with a call-back method
  int n;
  static char *toolkits[4];
public:
  MyText(wxPanel *p, wxFunction f, char *l) : wxText(p,f,l) {
    n = 0;
    this->SetValue(toolkits[0]);
  };
  void Inc() {
    ++n;
    if (n >= sizeof(toolkits) / sizeof(toolkits[0]))
     n = 0;    
    this->SetValue(toolkits[n]);
  }
};
char *MyText::toolkits[4] = {"wxwin", "uit", "oi", "interviews"};

class MyApp: public wxApp {
public:
  wxFrame *OnInit() {
    wxFrame *frame = new wxFrame(NULL,"Button test");
    wxPanel *panel = new wxPanel(frame);
    text = new MyText(panel, NULL,"Toolkit");
    panel->NewLine();

    new MyButton(panel, text, &MyText::Inc, "Push Me2");
    frame->Show(TRUE);
    return frame;
  };
} myApp;

From: Burell David Kingery <kingery@edu.tamu.cs>
Message-Id: <9312171452.AA05907@clavin>
Subject: wxWindows code sample
To: Craig Cockburn <craig@ed.festival>
Date: Fri, 17 Dec 1993 08:59:15 -0600 (CST)

Craig,

Here is the first code sample I promised.  It is a list dialog which can swap
between two different lists.  In my application I use it to swap between
active and inactive employees (those still working and those who aren't).

It is a good example of how to use static member functions as wxFunctions
for callbacks.

Here is the .h file for the list dialog.  I have added comments where I
thought they were appropriate so search for //= to find them.

Hope this helps,

David

================================ CODE STARTS HERE ==========================
/* listdiag.h */

#ifndef LISTDIAG_H
#define LISTDIAG_H

class ListDialog
{
  public:
    ListDialog(wxFrame *parent, int x, int y, char *message,
	       char *caption, int count1, char *list1[],
	       int count2, char *list2[], char **choice, int *index);
    ~ListDialog(void);

  private:
    wxDialogBox *dialog;
    wxListBox	*listbox;
    wxButton	*list_select;
    char	*the_selection;
    int		the_index;
    int 	visible_list;
    int 	c1;
    char	**l1;
    int 	c2;
    char	**l2;

//=
// Here are the three member functions which actually do the work I want
// when the callback is invoked.  Since these are member functions they
// will have access to the instance data of the object.

    void	OkCallback(void);
    void	CancelCallback(void);
    void	ListSelectCallback(void);


//=
// Here are three static member functions which are the initial callbacks
// of the class.  These are invoked in the normal manner for wxWindows.

    static void	OnOk(wxObject& the_object, wxEvent& the_event);
    static void	OnCancel(wxObject& the_object, wxEvent& the_event);
    static void OnListSelect(wxObject& the_object, wxEvent& the_event);
};

#endif


Now here is the .cc file for the list dialog.  I'm not real sure if you could
compile and link, but you could give it a try.  I'm now swamped with other
work here at Texas A&M so it's been awhile since I looked at this code.
Once again search for //= to find pertinent comments.

/* listdiag.cc */

/*
 * standard header goes here.
 */

#include <windows.h>

extern "C"
{
#include <stdio.h>
}

#include "wx.h"

#include "listdiag.h"

ListDialog::ListDialog(wxFrame *parent, int x, int y, char *message,
		       char *caption, int count1, char *list1[],
		       int count2, char *list2[], char **choice, int *index)
{
  if (x < 0) x = 300;
  if (y < 0) y = 300;

  wxDialogBox the_dialog(parent, caption, TRUE, x, y, 300, 250);

  dialog = &the_dialog;
  dialog->SetClientSize(300, 250);
  (void)new wxMessage(dialog, message);
  dialog->NewLine();

  listbox = new wxListBox(dialog, NULL, NULL, wxSINGLE,
			  -1, -1, 150, 200,
			  count1, list1);
  visible_list = 1;
  c1 = count1;
  l1 = list1;
  c2 = count2;
  l2 = list2;

  dialog->NewLine();

//=
// Here I am setting the callback function of the button to be the
// static member function ListDialog::OnOk.  The other buttons are 
// the same way.

  wxButton *ok = new wxButton(dialog,
			      (wxFunction)ListDialog::OnOk, "OK");

//=
// Here is where I set the client data of the button to the this pointer.
// This will be used in the static member function to invoke the correct
// method on the correct object.

  ok->SetClientData((char *)this);


  wxButton *button = new wxButton(dialog,
				  (wxFunction)ListDialog::OnCancel,
				  "Cancel");
  button->SetClientData((char *)this);
  list_select = new wxButton(dialog,
			     (wxFunction)ListDialog::OnListSelect,
			     "Show Inactive");
  list_select->SetClientData((char *)this);

  ok->SetDefault();

  dialog->Fit();
  dialog->Centre();
  dialog->Show(TRUE);
  *choice = the_selection;
  *index = the_index;
}

ListDialog::~ListDialog(void)
{
  return;
}

void ListDialog::OnOk(wxObject& the_object, wxEvent& the_event)
{
  ListDialog	*list_dialog;

//=
// Here is a static member function which is used as a wxFunction.
// The wxObject is the button which was pressed and I use it to get
// the client data of the button.  Remember the client data is the
// this pointer of the object of interest.

  list_dialog = (ListDialog *)((wxButton&)the_object).GetClientData();

//=
// Now that I have the correct object, I can invoke the member function
// of the object.

  list_dialog->OkCallback();

  return;
}

void ListDialog::OnCancel(wxObject& the_object, wxEvent& the_event)
{
  ListDialog	*list_dialog;

  list_dialog = (ListDialog *)((wxButton&)the_object).GetClientData();

  list_dialog->CancelCallback();

  return;
}

void ListDialog::OnListSelect(wxObject& the_object, wxEvent& the_event)
{
  ListDialog	*list_dialog;

  list_dialog = (ListDialog *)((wxButton&)the_object).GetClientData();

  list_dialog->ListSelectCallback();

  return;
}

void ListDialog::OkCallback(void)
{
//=
// This is the member function of the object which gets invoked from the
// static member function ListDialog::OnOk.  Now that I'm within a member
// function I can access the instance data of the object as well as invoke
// other member functions.  The other callbacks are handled the same way.

  the_index = listbox->GetSelection();

  if (the_index != -1)
    {
     the_selection = copystring(listbox->String(listbox->GetSelection()));
    }
  else
    {
     the_selection = NULL;
    }

  dialog->Show(FALSE);
  return;
}

void ListDialog::CancelCallback(void)
{
  the_selection = NULL;
  the_index = -1;
  dialog->Show(FALSE);
  return;
}

void ListDialog::ListSelectCallback(void)
{
  listbox->Clear();

  switch (visible_list)
    {
     case 1:
       listbox->Set(c2, l2);
       list_select->SetLabel(" Show Active ");
       visible_list = 2;
       break;

     case 2:
       listbox->Set(c1, l1);
       list_select->SetLabel("Show Inactive");
       visible_list = 1;
       break;
    }

  return;
}


Date: Wed, 12 Mar 1997 16:29:36 -0500
From: Bill McGrory <mcgrory@aerosft.com>
To: Julian Smart <julian.smart@ukonline.co.uk>

Hi Julian,

The examples you have of using member functions in callbacks
needs updating for what I believe is ANSI C++.
(the file members.txt, and the html page on c++ issues)

I tried the first example, and that failed, but with a little playing
around I got something to work.

here's my sample code. (it compiles with SGI's c++ compiler, and
Digital Unix's as well)

#include "stdio.h"

class CallbackClass;

typedef void (CallbackClass::*Func)();

class CallbackClass
{
private:
	int	junk;
};

class Main: public CallbackClass
{
public:
	int		data1, data2;

	void	Callback1(void) {printf("data1 = %d\n",data1);};
	void	Callback2(void) {printf("data2 = %d\n",data2);};
};

class Second
{
public:
	CallbackClass	*theObj;
	Func	theFunc;

	void	EvalCallback(void);
};

void
Second::EvalCallback(void)
{
	(theObj->*theFunc)();
}

main()
{
	Main	*main1, *main2;
	Second	*second;
	
	main1 = new Main;
	main2 = new Main;
	
	main1->data1 = 1;
	main1->data2 = 2;
	main2->data1 = 3;
	main2->data2 = 4;

	second = new Second;
	
	second->theObj = main1;
	second->theFunc = (Func)&Main::Callback1;
	second->EvalCallback();
	
	second->theObj = main1;
	second->theFunc = (Func)&Main::Callback2;
	second->EvalCallback();

	second->theObj = main2;
	second->theFunc = (Func)&Main::Callback1;
	second->EvalCallback();
	
	second->theObj = main2;
	second->theFunc = (Func)&Main::Callback2;
	second->EvalCallback();
};