Wilcox:Subclassing Top Level Windows

From WxWiki
Jump to: navigation, search

Subclassing wxTopLevelWindows (such as wxFrame) is relatively common task, but there's no definitive "Best Way To Do It". This attempts to be that guide.

Let's start by making a frame. For example purposes this frame will have a single button on it.

The Code

class OurFrame : public wxFrame
{
	// Hook up with wxWidgets RTTI
	DECLARE_DYNAMIC_CLASS(OurFrame);

public:

	// Optionally use : to clear member variables
	// or set up other class internal members,
	// but don't use this to build the controls for your window.
        // (for this example, we don't need to do anything here)
	OurFrame() {;}

	// Optionally use : to clear member variables
	OurFrame(wxWindow* parent,
	         wxWindowID id,
	         const wxString & title,
	         const wxPoint & pos = wxDefaultPosition,
	         const wxSize & size = wxDefaultSize,
	         long style = wxDEFAULT_FRAME_STYLE);

	// Destroy class internal variables.
	// (but remember wxWidgets will free your GUI objects,
	// don't call this explicitly)
        //(for this example, we don't need to do anything here)
	~OurFrame() {;}

        //Optionally you could create #defines or consts for the position, size, and styles
        //to avoid having to change these values two places in the code
	bool Create(wxWindow* parent,
	            wxWindowID id,
	            const wxString & title,
	            const wxPoint & pos = wxDefaultPosition,
	            const wxSize & size = wxDefaultSize,
	            long style = wxDEFAULT_FRAME_STYLE,
	            const wxString & name = _T("OurFrame"));

private:

	void HandleDoItBtn(wxCommandEvent & evt);

	// We want to handle events, so we need an event table.
	DECLARE_EVENT_TABLE();
};
// Magic macro: Defines wx RTTI information.
IMPLEMENT_DYNAMIC_CLASS(OurFrame, wxFrame)

enum OurFrameControlIDs
{
	// wxID_HIGHEST is where the predefined wxWidgets IDs stop.
	// Your user control IDs should start here.
	ID_doItBtn = wxID_HIGHEST
};

BEGIN_EVENT_TABLE(OurFrame, wxFrame)
	EVT_BUTTON(ID_doItBtn, OurFrame::HandleDoItBtn)
END_EVENT_TABLE()

OurFrame::OurFrame(wxWindow* parent,
                   wxWindowID id,
                   const wxString & title,
                   const wxPoint & pos,
                   const wxSize & size,
                   long style)
{
	Create(parent, id, title, pos, size, style);
}

bool OurFrame::Create(wxWindow* parent,
                      wxWindowID id,
                      const wxString & title,
                      const wxPoint & pos,
                      const wxSize & size,
                      long style,
                      const wxString& name)
{
	if ( !wxFrame::Create(parent, id, title, pos, size, style, name) )
		return false;

	wxButton* doItBtn = new wxButton( this, ID_doItBtn, _("Do It!") );

	return true;
}

void OurFrame::HandleDoItBtn(wxCommandEvent & evt)
{
	wxBell();
}

The Explanation

There are a few interesting patterns here:

OurFrame() vs OurFrame(wxWindow* parent, ...)

wxWidgets conventions say there should be two ways to construct a window:

OurFrame* frame = new OurFrame( parent, wxID_ANY, _("Our Frame!") );

And...

OurFrame* frame = new OurFrame();
frame->Create( parent, wxID_ANY, _("Our Frame!") );

Our class reflects both these methods. The multi-parameter constructor just calls Create() automatically, where the other constructor requires the user to call Create() manually.

Managing Control IDs

The wxWidgets event system separates one control from another by event IDs. Thus it's important that every control that the user interact with (at least) has a unique ID within that frame assigned to it. The "within that frame" part is important: because events can't travel from top level window to top level window, two controls on separate windows can have the same event ID... because they never interfere with each other.

To facilitate the assigning of control IDs we have an enum where we stash our IDs. As more controls are added to OurFrame more items are added to the enum... and the enum worries about what the value of these items really are.

We set the first item in the enum to be wxID_HIGHEST, the number where we can start defining our own event IDs.