Painting your custom control
From WxWiki
The following code demonstrates how to start to create a custom widget (that you paint yourself) by deriving from wxPanel. It creates a simple basic button that does nothing useful but can be used as starting point.
Not that although this example draws a rectangular component, nothing prevents you from changing the paint method to draw any shape you fancy.
Some common mistakes
- Do not derive from wxWindow, you should use wxPanel instead. wxWindow should be seen as a mostly abstract base class that doesn't do much on its own. wxPanel will require much less knowledge of wx internals from your side.
- Using wxPaintDC outside paint events (use wxClientDC)
- Draw something on a wxClientDC and expect it will stay there until you draw something else (wrong because your window manager may throw away your drawing at any time - e.g. if the window is minimized or hidden behind something else - and will expect you can draw it back later when receiving a paint event) i.e. always enable the paint event to draw everything. The best way to deal with this is to separate state/data from view. (The render routine reads variables describing current state and draws according to these variables. When something needs to change, don't render it straight ahead; instead, update the variables and call for a repaint - if well-coded, your paint routine should catch the change)
Example
#include <wx/wx.h> #include <wx/sizer.h> class wxCustomButton : public wxPanel { bool pressedDown; wxString text; static const int buttonWidth = 200; static const int buttonHeight = 50; public: wxCustomButton(wxFrame* parent, wxString text); void paintEvent(wxPaintEvent & evt); void paintNow(); void render(wxDC& dc); // some useful events void mouseMoved(wxMouseEvent& event); void mouseDown(wxMouseEvent& event); void mouseWheelMoved(wxMouseEvent& event); void mouseReleased(wxMouseEvent& event); void rightClick(wxMouseEvent& event); void mouseLeftWindow(wxMouseEvent& event); void keyPressed(wxKeyEvent& event); void keyReleased(wxKeyEvent& event); DECLARE_EVENT_TABLE() }; BEGIN_EVENT_TABLE(wxCustomButton, wxPanel) EVT_MOTION(wxCustomButton::mouseMoved) EVT_LEFT_DOWN(wxCustomButton::mouseDown) EVT_LEFT_UP(wxCustomButton::mouseReleased) EVT_RIGHT_DOWN(wxCustomButton::rightClick) EVT_LEAVE_WINDOW(wxCustomButton::mouseLeftWindow) EVT_KEY_DOWN(wxCustomButton::keyPressed) EVT_KEY_UP(wxCustomButton::keyReleased) EVT_MOUSEWHEEL(wxCustomButton::mouseWheelMoved) // catch paint events EVT_PAINT(wxCustomButton::paintEvent) END_EVENT_TABLE() wxCustomButton::wxCustomButton(wxFrame* parent, wxString text) : wxPanel(parent) { SetMinSize( wxSize(buttonWidth, buttonHeight) ); this->text = text; pressedDown = false; } /* * Called by the system of by wxWidgets when the panel needs * to be redrawn. You can also trigger this call by * calling Refresh()/Update(). */ void wxCustomButton::paintEvent(wxPaintEvent & evt) { // depending on your system you may need to look at double-buffered dcs wxPaintDC dc(this); render(dc); } /* * Alternatively, you can use a clientDC to paint on the panel * at any time. Using this generally does not free you from * catching paint events, since it is possible that e.g. the window * manager throws away your drawing when the window comes to the * background, and expects you will redraw it when the window comes * back (by sending a paint event). */ void wxCustomButton::paintNow() { // depending on your system you may need to look at double-buffered dcs wxClientDC dc(this); render(dc); } /* * Here we do the actual rendering. I put it in a separate * method so that it can work no matter what type of DC * (e.g. wxPaintDC or wxClientDC) is used. */ void wxCustomButton::render(wxDC& dc) { if (pressedDown) dc.SetBrush( *wxRED_BRUSH ); else dc.SetBrush( *wxGREY_BRUSH ); dc.DrawRectangle( 0, 0, buttonWidth, buttonHeight ); dc.DrawText( text, 20, 15 ); } void wxCustomButton::mouseDown(wxMouseEvent& event) { pressedDown = true; paintNow(); } void wxCustomButton::mouseReleased(wxMouseEvent& event) { pressedDown = false; paintNow(); wxMessageBox( wxT("You pressed a custom button") ); } void wxCustomButton::mouseLeftWindow(wxMouseEvent& event) { if (pressedDown) { pressedDown = false; paintNow(); } } // currently unused events void wxCustomButton::mouseMoved(wxMouseEvent& event) {} void wxCustomButton::mouseWheelMoved(wxMouseEvent& event) {} void wxCustomButton::rightClick(wxMouseEvent& event) {} void wxCustomButton::keyPressed(wxKeyEvent& event) {} void wxCustomButton::keyReleased(wxKeyEvent& event) {} // ---------------------------------------- // how-to-use example class MyApp: public wxApp { wxFrame *frame; wxCustomButton* m_btn_1; wxCustomButton* m_btn_2; wxCustomButton* m_btn_3; public: bool OnInit() { // make sure to call this first wxInitAllImageHandlers(); wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); frame = new wxFrame(NULL, wxID_ANY, wxT("Hello wxDC"), wxPoint(50,50), wxSize(800,600)); // then simply create like this m_btn_1 = new wxCustomButton( frame, wxT("My Custom Button 11") ); sizer->Add(m_btn_1, 0, wxALL, 5); m_btn_2 = new wxCustomButton( frame, wxT("Hello World!") ); sizer->Add(m_btn_2, 0, wxALL, 5); m_btn_3 = new wxCustomButton( frame, wxT("Foo Bar") ); sizer->Add(m_btn_3, 0, wxALL, 5); frame->SetSizer(sizer); frame->Show(); return true; } }; IMPLEMENT_APP(MyApp)
Flickering
If you have flickering problems, see Drawing_on_a_panel_with_a_DC#To_avoid_flickering