Search code examples
c++c++11wxwidgets

Replace sizer of a wxFrame in wxWidgets


I am writing a small Minesweeper game just to get familiar with wxWidgets (Windows, wxWidgets 3.1.4). The app can handle one game well, and now I would like to add the "new game" functionality. For the layout, I am using a wxGridSizer.

My first approach was to create a new wxGridSizer with the new fields in it, and just replace the current sizer with the new one. Even though I figured out how to reuse the old sizer (and it is probably also a better solution in general), I am kind of curious how can I replace the sizer properly.

I was able to simplify my problem to this:


#include <wx/wxprec.h>
#ifndef WX_PRECOMP
  #include <wx/wx.h>
#endif

class MyApp : public wxApp {
public:
  virtual bool OnInit();
};
class MyFrame : public wxFrame {
public:
  MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size);

private:
  void OnExit(wxCommandEvent &event);
  void OnRefill(wxCommandEvent &event);
  wxDECLARE_EVENT_TABLE();
};

enum { ID_Refill = 1 };

wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(ID_Refill, MyFrame::OnRefill)
EVT_MENU(wxID_EXIT, MyFrame::OnExit)
wxEND_EVENT_TABLE() wxIMPLEMENT_APP(MyApp);

bool MyApp::OnInit() {
  MyFrame *frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(300, 200));
  frame->Show(true);
  return true;
}

MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size)
  : wxFrame(NULL, wxID_ANY, title, pos, size) {
  wxMenu *menu = new wxMenu;
  menu->Append(ID_Refill, "&Refill...\tCtrl-R", "Creates new layout");
  wxMenuBar *menuBar = new wxMenuBar;
  menuBar->Append(menu, "&Menu");
  SetMenuBar(menuBar);
  wxGridSizer *sizer = new wxGridSizer(2, 2, 1, 1);
  SetSizer(sizer);

  for (auto i = 0; i < 4; i++) {
    wxButton *button = new wxButton(this, wxID_ANY, std::to_string(i), wxDefaultPosition, wxDefaultSize, 0);
    sizer->Add(button, wxALL, 0);
  }
}

void MyFrame::OnExit(wxCommandEvent &) {
  Close(true);
}

void MyFrame::OnRefill(wxCommandEvent &) {
  Freeze();
  GetSizer()->Clear(true);
  wxGridSizer *sizer = new wxGridSizer(3, 3, 1, 1);
  SetSizer(sizer);
  for (auto i = 0; i < 9; i++) {
    wxButton *button = new wxButton(this, wxID_ANY, std::string("Refilled") + std::to_string(i), wxDefaultPosition,
                                    wxDefaultSize, 0);
    sizer->Add(button, wxALL, 0);
  }
  sizer->Layout();
  Thaw();
  Refresh();
}

The problem is after the OnRefill function the app only shows the first button (Refilled0), but not the rest of the buttons.

Before OnRefill:

All buttons are shown

After OnRefill:

Only the first button is shown

My question is how can I replace the sizer of MyFrame properly? Based on what I understood from the examples and the documentation, this should work, but I guess I am missing something.


Solution

  • To replace the sizer, you just need to make one small change to the MyFrame::OnRefill method.

    Instead of calling sizer->Layout(); simply call Layout();. I'm not entirely sure why calling Layout for the sizer doesn't work.

    The full method looks like this:

    void MyFrame::OnRefill(wxCommandEvent &) {
      Freeze();
      GetSizer()->Clear(true);
      wxGridSizer *sizer = new wxGridSizer(3, 3, 1, 1);
      SetSizer(sizer);
      for (auto i = 0; i < 9; i++) {
        wxButton *button = new wxButton(this, wxID_ANY, std::string("Refilled") + std::to_string(i), wxDefaultPosition,
                                        wxDefaultSize, 0);
        sizer->Add(button, wxALL, 0);
      }
      Layout();
      Thaw();
      Refresh();
    }