Dynamic panel creation and layout Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
normunds
Knows some wx things
Knows some wx things
Posts: 31
Joined: Mon Sep 03, 2007 8:14 am
Contact:

Dynamic panel creation and layout

Post by normunds »

Hi all,

I'm making an application where after button press one panel changes to another. Everything is working except the layout. New panel is not in old place (right and expanded) but in top left corner over the button. Can you help me put new panel in old place?

testMain.cpp

Code: Select all

#include "testMain.h"
#include "newpanel.h"
#include <wx/msgdlg.h>
#include <wx/intl.h>
#include <wx/string.h>

const long testFrame::ID_BUTTON1 = wxNewId();
const long testFrame::ID_BUTTON2 = wxNewId();
const long testFrame::ID_PANEL1 = wxNewId();

BEGIN_EVENT_TABLE(testFrame,wxFrame)
END_EVENT_TABLE()

testFrame::testFrame(wxWindow* parent,wxWindowID id)
{
    wxBoxSizer* BoxSizer2;
    wxBoxSizer* BoxSizer1;

    Create(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("id"));
    BoxSizer1 = new wxBoxSizer(wxHORIZONTAL);
    BoxSizer2 = new wxBoxSizer(wxVERTICAL);
    btCreate = new wxButton(this, ID_BUTTON1, _("Create"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON1"));
    BoxSizer2->Add(btCreate, 0, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
    btClose = new wxButton(this, ID_BUTTON2, _("Close"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON2"));
    BoxSizer2->Add(btClose, 0, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
    BoxSizer1->Add(BoxSizer2, 0, wxALL|wxEXPAND|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
    BoxSizer3 = new wxBoxSizer(wxHORIZONTAL);
    pnMain = new wxPanel(this, ID_PANEL1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL1"));
    pnMain->SetBackgroundColour(wxColour(255,255,128));
    BoxSizer3->Add(pnMain, 1, wxALL|wxEXPAND|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
    BoxSizer1->Add(BoxSizer3, 1, wxALL|wxEXPAND|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
    SetSizer(BoxSizer1);
    BoxSizer1->Fit(this);
    BoxSizer1->SetSizeHints(this);

    Connect(ID_BUTTON1,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&testFrame::OnbtCreateClick);
    Connect(ID_BUTTON2,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&testFrame::OnbtCloseClick);
}

testFrame::~testFrame()
{
}

void testFrame::OnbtCreateClick(wxCommandEvent& event)
{
    pnMain->Destroy();
    pnMain = (wxPanel*)new NewPanel(this);
}

void testFrame::OnbtCloseClick(wxCommandEvent& event)
{
    Close();
}

testMain.h

Code: Select all

#ifndef TESTMAIN_H
#define TESTMAIN_H

#include <wx/sizer.h>
#include <wx/panel.h>
#include <wx/button.h>
#include <wx/frame.h>

class testFrame: public wxFrame
{
    public:
        testFrame(wxWindow* parent,wxWindowID id = -1);
        virtual ~testFrame();
    private:
        void OnbtCreateClick(wxCommandEvent& event);
        void OnbtCloseClick(wxCommandEvent& event);

        static const long ID_BUTTON1;
        static const long ID_BUTTON2;
        static const long ID_PANEL1;

        wxPanel* pnMain;
        wxButton* btClose;
        wxButton* btCreate;
        wxBoxSizer* BoxSizer3;

        DECLARE_EVENT_TABLE()
};

#endif // TESTMAIN_H

newpanel.cpp

Code: Select all

#include "wx_pch.h"
#include "newpanel.h"

const long NewPanel::ID_STATICTEXT1 = wxNewId();
const long NewPanel::ID_TEXTCTRL1 = wxNewId();

BEGIN_EVENT_TABLE(NewPanel,wxPanel)
END_EVENT_TABLE()

NewPanel::NewPanel(wxWindow* parent,wxWindowID id,const wxPoint& pos,const wxSize& size)
{
	wxBoxSizer* BoxSizer1;
	
	Create(parent, id, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("id"));
	BoxSizer1 = new wxBoxSizer(wxVERTICAL);
	StaticText1 = new wxStaticText(this, ID_STATICTEXT1, _("Label"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT1"));
	BoxSizer1->Add(StaticText1, 0, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
	TextCtrl1 = new wxTextCtrl(this, ID_TEXTCTRL1, _("Text"), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE, wxDefaultValidator, _T("ID_TEXTCTRL1"));
	BoxSizer1->Add(TextCtrl1, 1, wxALL|wxEXPAND|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
	SetSizer(BoxSizer1);
	BoxSizer1->Fit(this);
	BoxSizer1->SetSizeHints(this);
}

NewPanel::~NewPanel()
{
}

newpanel.h

Code: Select all

#ifndef NEWPANEL_H
#define NEWPANEL_H

class NewPanel: public wxPanel
{
	public:
		NewPanel(wxWindow* parent,wxWindowID id=wxID_ANY,const wxPoint& pos=wxDefaultPosition,const wxSize& size=wxDefaultSize);
		virtual ~NewPanel();

		wxStaticText* StaticText1;
		wxTextCtrl* TextCtrl1;
	protected:
		static const long ID_STATICTEXT1;
		static const long ID_TEXTCTRL1;
	private:
		DECLARE_EVENT_TABLE()
};
#endif
valodas - free cross-platform language learning software
http://www.valodas.com
JimFairway
wxWorld Domination!
wxWorld Domination!
Posts: 1059
Joined: Sun Dec 30, 2007 6:40 pm
Location: Canada

Post by JimFairway »

Hi,

Based on the class definition of testFrame:

Code: Select all

        wxPanel* pnMain;
        wxButton* btClose;
        wxButton* btCreate;
        wxBoxSizer* BoxSizer3;

        DECLARE_EVENT_TABLE()

It looks like you should be using BoxSizer3 as the overall sizer for testFrame, but you are creating a new one (BoxSizer1) and using that instead.
Then somewhere during or after you create the new panel:

Code: Select all

NewPanel::NewPanel(wxWindow* parent,wxWindowID id,const wxPoint& pos,const wxSize& size)
{
        wxBoxSizer* BoxSizer1;
        
you need to put it's sizer (also called BoxSizer1) into the overall sizer for the frame to replace the one you added here:

Code: Select all

    BoxSizer3 = new wxBoxSizer(wxHORIZONTAL);
    pnMain = new wxPanel(this, ID_PANEL1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL1"));
    pnMain->SetBackgroundColour(wxColour(255,255,128));
    BoxSizer3->Add(pnMain, 1, wxALL|wxEXPAND|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
    BoxSizer1->Add(BoxSizer3, 1, wxALL|wxEXPAND|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
You could replace it in:

Code: Select all

void testFrame::OnbtCreateClick(wxCommandEvent& event)
{
// ... replace the sizer of the old panel here...
    wxSizer *oldSizer = pnMain->GetSizer();
    wxSizer *mySizer = this->GetSizer();
    mySizer->Remover(oldSizer);
    pnMain->Destroy();
    pnMain = (wxPanel*)new NewPanel(this);
    mySizer->Add = pnMain->GetSizer();

}
Summary, when you destroy the panel that you put into your frame, you need to put the new panel into the frame sizer, otherwise it will be off on it's own.

Jim
OS: Vista SP1, wxWidgets 2.8.7.
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Post by DavidHart »

Hi,

Code: Select all

void testFrame::OnbtCreateClick(wxCommandEvent& event)
{
    pnMain->Destroy();
    pnMain = (wxPanel*)new NewPanel(this);
}
To get things working this way, at a minimum you should use wxSizer::Detach to remove pnMain from BoxSizer3 before you destroy it, and then re-Add it when it's recreated. You will also need to do: BoxSizer1->Layout(); afterwards, to get the sizer system to resize itself appropriately.

However dynamic replacing of components isn't one of the sizer system's strong points, and what I just suggested sometimes doesn't work properly if the panels are of different sizes (if so, the most reliable solution is to use wxWindow::SetSize to increment one dimension of the frame by 1 pixel, then decrement it again; this kick-starts the process better).

In your situation, though, there's a better way of doing this. Create all the potential panels at the beginning, add them all to BoxSizer3, but use wxSizer::Hide to hide all but one. Then when the button is clicked just Show/Hide the appropriate panels and call BoxSizer1->Layout();

Regards,

David
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Post by DavidHart »

JimFairway wrote: Based on the class definition of testFrame:
//snip
It looks like you should be using BoxSizer3 as the overall sizer for testFrame, but you are creating a new one (BoxSizer1) and using that instead.
Hmm, I don't think that's valid reasoning. The sizer that should be used in SetSizer() is the topmost sizer of a nest of sizers. It doesn't matter whether or not it features in the class definition.
Then somewhere during or after you create the new panel:
//snip
you need to put its sizer (also called BoxSizer1) into the overall sizer for the frame
That isn't correct. A panel is a top-level window for sizer purposes. It contains its own sizer nest, which aren't themselves added to the frame sizer; it's the panel itself that gets added.
Summary, when you destroy the panel that you put into your frame, you need to put the new panel into the frame sizer, otherwise it will be off on it's own.
Agreed ;)
normunds
Knows some wx things
Knows some wx things
Posts: 31
Joined: Mon Sep 03, 2007 8:14 am
Contact:

Post by normunds »

10x, missing magic keyword was ->Layout()
valodas - free cross-platform language learning software
http://www.valodas.com
Post Reply