Search code examples
c++wxwidgets

wxWidgets GridSizer puts all buttons in the same position


I'm learning wxWidgets and am trying to make minesweeper using wxButtons. I use the following code to create and position the buttons:

int length = 10;

wxGridSizer *grid = new wxGridSizer(length, length, 0, 0);
wxButton *buttons[length*length];

for (int i=0; i<length*length; i++){
    buttons[i] = new wxButton(this, wxID_ANY);
    grid->Add(buttons[i], 1, wxEXPAND | wxALL);
    if (mineField[i]){
        Bind(wxEVT_BUTTON, &MainWindow::isMine, this);
    } else {
        Bind(wxEVT_BUTTON, &MainWindow::notMine, this);
    }
    
}

I expect this to generate a 10x10 grid of buttons, but instead it positions all of the buttons at (0,0). I have been Googling the problem for a while now but I can't find the issue. How do I position the buttons in a 10x10 grid? Thanks.


Solution

  • To make the grid sizer layout the buttons, you need to either

    1. set it to be the sizer for a window or
    2. add it to another sizer.

    Assuming the code above is from the constructor of your main frame window, you would set grid to be the sizer for the frame like this

    SetSizer(grid);
    

    On the other hand, if you have other controls in the frame and need to add grid to another sizer, you could do that something like this:

    otherSizer->Add(grid,wxSizerFlags(0));
    

    Obviously, 'otherSizer' should be replaced with the name of the other sizer. There are many options that can be used with wxSizerFlags to get the exact layout you want.

    In addition, as mentioned above, it's sometimes helpful to end all the code the creates and organizes the controls your using with a call to the Layout() method.


    Another tactic you can use is to have a single panel be the only child of the frame and then set a sizer for the panel. This works because in wxWidgets whenever a top level window such as a frame has a single child, that child is automatically sized to fill all of the available area.

    Using this technique, a layout with a text control along the top and the grid underneath it could be created like this:

    // Create the controls
    wxPanel* bgPanel = new wxPanel(this, wxID_ANY);
    wxTextCtrl* text = new wxTextCtrl(bgPanel, wxID_ANY);
    
    wxGridSizer *grid = new wxGridSizer(length, length, 0, 0);
    wxButton *buttons[length*length];
    
    for (int i=0; i<length*length; i++){
        buttons[i] = new wxButton(bgPanel, wxID_ANY);
        grid->Add(buttons[i], 1, wxEXPAND | wxALL);
        if (mineField[i]){
            Bind(wxEVT_BUTTON, &MainWindow::isMine, this);
        } else {
            Bind(wxEVT_BUTTON, &MainWindow::notMine, this);
        }
    
    }
    
    // Create a sizer for the panel and add the text control and grid to it.
    wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
    mainSizer->Add(text, wxSizerFlags(0).Expand().Border(wxALL));
    mainSizer->Add(grid,wxSizerFlags(1).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM));
    bgPanel->SetSizer(mainSizer);