Bind Dialog Event with a MainWindow handler 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
gmapp
Earned a small fee
Earned a small fee
Posts: 16
Joined: Wed Jan 24, 2018 4:39 pm

Bind Dialog Event with a MainWindow handler

Post by gmapp » Fri Apr 23, 2021 11:00 am

Hi,

I'm trying to develop a program with a main window and a dialog.
The general idea is that when I press a button in the dialog the text into a main window ctrltext changes.

I'm confused about how to bind the wxEVT_COMMAND_BUTTON_CLICKED with the main windows function/handler

Thanks for any feedback

PB
Part Of The Furniture
Part Of The Furniture
Posts: 3064
Joined: Sun Jan 03, 2010 5:45 pm

Re: Bind Dialog Event with a MainWindow handler

Post by PB » Fri Apr 23, 2021 11:34 am

I personally would not do some crazy passing around variables and exposing the internals of a class.

I would introduce a new event, which would be generated by the dialog and handled in the frame.

It could look like this, where the code passes around an editable string between a wxFrame (MyFrame) and a wxDialog (MyDialog), where MyFrame is the parent of MyDialog shown modal:
1. Declare and define a new wxCommandEvent-derived event (wxUPDATE_FROM_MYDIALOG).
2. In the frame ctor, bind the new event to its handler (MyFrame::OnUpdateFromDialog()).
2. When the frame needs to be updated from the dialog, the dialog sends the new event (MyDialog::OnUpdateMainFrame()), with the string stored in the event.

Code: Select all

#include <wx/wx.h>

// should be in the header file
wxDECLARE_EVENT(wxUPDATE_FROM_MYDIALOG, wxCommandEvent);

// and this in the source file
wxDEFINE_EVENT(wxUPDATE_FROM_MYDIALOG, wxCommandEvent);

class MyDialog : public wxDialog
{
public:
    MyDialog(wxWindow* parent, const wxString& value) 
        : wxDialog(parent, wxID_ANY, "MyDialog")
    {                                                                      
        wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
        
        m_textCtrl = new wxTextCtrl(this, wxID_ANY, value);
        mainSizer->Add(m_textCtrl, wxSizerFlags().Expand().DoubleBorder());

        wxButton* button = new wxButton(this, wxID_ANY, "Update MyFrame");
        button->Bind(wxEVT_BUTTON, &MyDialog::OnUpdateMainFrame, this);
        mainSizer->Add(button, wxSizerFlags().Expand().DoubleBorder());

        mainSizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), wxSizerFlags().Expand().DoubleBorder());                        
        SetSizerAndFit(mainSizer);
    }	

    wxString GetTextValue() const { return m_textCtrl->GetValue(); }

private:
    wxTextCtrl* m_textCtrl;

    void OnUpdateMainFrame(wxCommandEvent&)
    {
        wxCommandEvent event(wxUPDATE_FROM_MYDIALOG, GetId());
    
        event.SetString(GetTextValue());
        event.SetEventObject(this);    
        GetParent()->ProcessWindowEvent(event);    
    }
};

class MyFrame : public wxFrame
{
public:
    MyFrame() : wxFrame(nullptr, wxID_ANY, "MyFrame")
    {                       
        wxPanel*    mainPanel = new wxPanel(this);
        wxBoxSizer* mainPanelSizer = new wxBoxSizer(wxVERTICAL);
        
        m_textCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "Change me");
        mainPanelSizer->Add(m_textCtrl, wxSizerFlags().Expand().DoubleBorder());

        wxButton* button = new wxButton(mainPanel, wxID_ANY, "Show MyDialog...");
        button->Bind(wxEVT_BUTTON, &MyFrame::OnShowMyDialog, this);
        mainPanelSizer->Add(button, wxSizerFlags().Expand().DoubleBorder());
                        
        mainPanel->SetSizer(mainPanelSizer);

        Bind(wxUPDATE_FROM_MYDIALOG, &MyFrame::OnUpdateFromDialog, this);
    }    
private:
    wxTextCtrl* m_textCtrl;

    void OnShowMyDialog(wxCommandEvent&)
    {
        MyDialog dlg(this, m_textCtrl->GetValue());

        if ( dlg.ShowModal() == wxID_OK )
            m_textCtrl->SetValue(dlg.GetTextValue());
    }

    void OnUpdateFromDialog(wxCommandEvent& event)
    {
        m_textCtrl->SetValue(event.GetString());
    }
};

class MyApp : public wxApp
{
public:
    bool OnInit() override
    {     
        (new MyFrame())->Show();               
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
It would be best to run the code, to see how it works.

gmapp
Earned a small fee
Earned a small fee
Posts: 16
Joined: Wed Jan 24, 2018 4:39 pm

Re: Bind Dialog Event with a MainWindow handler

Post by gmapp » Fri Apr 23, 2021 2:37 pm

Exactly what I was looking for. Great!
I really understood a lot of things better today ;-)

Thank you

ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 5227
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Bind Dialog Event with a MainWindow handler

Post by ONEEYEMAN » Fri Apr 23, 2021 5:15 pm

Hi,
This is all depends on whether you want to keep dialog open after the button press or close it and return the control to the main frame.

If you want the dialog to belosed and then update the text - you don't need any extra code.

And even if you don't - just call "GetParent()->GetTextCtrl()->SetValue()".

Something like this:

Code: Select all

class MyDialog : public wxDialog
{
public:
    MyDialog(wxWindow *parent, ...);
    void OnButtonClick(wxCommandEvent &event);
private:
    wxButton *myButton;
};

MyDialog::MyDialog(wxWindow *parent, ...) : wxDialog( parent, ... )
{
// Create all controls
    Bind( wxEVT_BUTTON, &MyDialog::OnButtonClick, this );
}

void MyDialog::OnButtonClick(wxCommandEvent &event)
{
    GetParent()->GetTextCtrl()->SetValue( "My new text" );
}

Code: Select all

class MyFrame : public wxFrame
{
public:
    MyFrame(...);
    wxTextCtrl *GetTextCtrl() { return m_textCtrl; };
private:
    wxTextCtrl *m_textCtrl;
}

MyFrame::MyFrame(...) : public wxFrame
{
// Create all controls
}
Thank you.

PB
Part Of The Furniture
Part Of The Furniture
Posts: 3064
Joined: Sun Jan 03, 2010 5:45 pm

Re: Bind Dialog Event with a MainWindow handler

Post by PB » Fri Apr 23, 2021 6:52 pm

ONEEYEMAN wrote:
Fri Apr 23, 2021 5:15 pm
And even if you don't - just call "GetParent()->GetTextCtrl()->SetValue()".
This would not work. To make it work, one would have to make even tighter class coupling than in my code. A circular dependency where MyFrame and MyDialog are aware of each other must be introduced and then in the code fragment above wxWindow* returned by GetParent() must be cast to MyFrame*.

But it is simpler then my code and I guess often good enough.

ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 5227
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Bind Dialog Event with a MainWindow handler

Post by ONEEYEMAN » Fri Apr 23, 2021 7:08 pm

@PB,
Yes - KISS principle.
And it is almost straightforward.

And if the OP doesn't need the dialog to remain open - the code will be even simpler.

Thank you.

PB
Part Of The Furniture
Part Of The Furniture
Posts: 3064
Joined: Sun Jan 03, 2010 5:45 pm

Re: Bind Dialog Event with a MainWindow handler

Post by PB » Fri Apr 23, 2021 7:24 pm

ONEEYEMAN wrote:
Fri Apr 23, 2021 7:08 pm
@PB,
Yes - KISS principle.
And it is almost straightforward.
But it goes against principles of good software design, where tight class coupling is for obvious reasons undesirable. In fact, in a well written software, such scenarios are usually implemented using one of those Model-View-<something> patterns.

In programming, we should not strive for just for (deceivingly) simple. We should strive for working, efficient, and maintainable; where each of the three is important.

The above is just my two cents, and I am certainly no expert on anything.

Post Reply