替换 wxWidgets 中 wxFrame 的 sizer

问题描述

我正在编写一个小型扫雷游戏,只是为了熟悉 wxWidgets(Windows,wxWidgets 3.1.4)。该应用程序可以很好地处理一款游戏,现在我想添加“新游戏”功能。对于布局,我使用的是 wxGridSizer

我的第一种方法是创建一个包含新字段的新 wxGridSizer,然后将当前的 sizer 替换为新的。尽管我想出了如何重用旧的 sizer(通常它也可能是一个更好的解决方案),但我还是很好奇如何正确更换 sizer。

我能够将我的问题简化为:


#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 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,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);
  SetSizer(sizer);
  for (auto i = 0; i < 9; i++) {
    wxButton *button = new wxButton(this,std::string("Refilled") + std::to_string(i),0);
  }
  sizer->Layout();
  Thaw();
  Refresh();
}

问题是在 OnRefill 函数之后,应用只显示一个按钮 (Refilled0),而不显示其余按钮。

OnRefill之前:

All buttons are shown

OnRefill 之后:

Only the first button is shown

我的问题是如何正确替换 MyFrame 的 sizer?根据我从示例和文档中的理解,这应该可行,但我想我遗漏了一些东西。

解决方法

要替换 sizer,您只需对 MyFrame::OnRefill 方法进行一个小改动。

无需调用 sizer->Layout();,只需调用 Layout();。我不完全确定为什么为 sizer 调用 Layout 不起作用。

完整的方法如下所示:

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();
}