Lose focus event for wxTextCtrl? 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.
suckerformimi
Experienced Solver
Experienced Solver
Posts: 62
Joined: Sun May 06, 2007 5:45 pm

Lose focus event for wxTextCtrl?

Post by suckerformimi »

How can I find out when my text control loses focus?

I'm trying to avoid making the user hit return before accepting their text input. I just want to grab the text out of the control when the user finishes typing and tabs out or clicks a button.
protocol
Moderator
Moderator
Posts: 680
Joined: Wed Jan 18, 2006 6:13 pm
Location: Dallas, TX
Contact:

Post by protocol »

What I would do is, subclass wxTextCtrl, then override wxWindow::SetFocus().

SetFocus is called when focus is set and "killed".

It is virtual so it is "override-ready".

http://docs.wxwidgets.org/stable/wx_wxw ... owsetfocus

Then use wxWindow::FindFocus() within the method to see if your/this control has the keyboard focus.

Regards.
/* UIKit && wxWidgets 2.8 && Cocoa && .Net */
QuRegExmm
wxPCRE & ObjPCRE - Regex It!
suckerformimi
Experienced Solver
Experienced Solver
Posts: 62
Joined: Sun May 06, 2007 5:45 pm

Post by suckerformimi »

I'll check it out. Thanks!
gtafan
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 201
Joined: Wed Mar 29, 2017 9:52 am

Re:

Post by gtafan »

protocol wrote:What I would do is, subclass wxTextCtrl, then override wxWindow::SetFocus().

SetFocus is called when focus is set and "killed".

It is virtual so it is "override-ready".

http://docs.wxwidgets.org/stable/wx_wxw ... owsetfocus

Then use wxWindow::FindFocus() within the method to see if your/this control has the keyboard focus.

Regards.
Is there no other way to get the event when textctrl looses focus? I really want to use wxTextCtrl and not some other custom class.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Re:

Post by PB »

gtafan wrote:Is there no other way to get the event when textctrl looses focus? I really want to use wxTextCtrl and not some other custom class.
I may be missing something but using wxEVT_KILL_FOCUS seems to work just fine?

Code: Select all

#include <wx/wx.h>

class MyDialog : public wxDialog
{
public:
    MyDialog () : wxDialog(NULL, wxID_ANY, "Test")
    {                                      
        wxPanel* mainPanel = new wxPanel(this);
        
        wxTextCtrl* textCtrl = new wxTextCtrl(mainPanel , wxID_ANY);
        textCtrl->Bind(wxEVT_KILL_FOCUS, &MyDialog::OnKillFocus, this);        

        wxTextCtrl* logCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 
            wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);     
        wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl)); 

        wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
        mainSizer->Add(textCtrl, wxSizerFlags().Expand().Border());
        mainSizer->Add(new wxButton(mainPanel, wxID_ANY, "Button"), wxSizerFlags().Expand().Border());
        mainSizer->Add(logCtrl, wxSizerFlags().Expand().Border().Proportion(1));
        mainPanel->SetSizer(mainSizer);                 
    }	
private:    
    void OnKillFocus(wxFocusEvent& event)
    {
        wxLogMessage("textCtrl has just lost focus");      
        event.Skip();
    }
};

class MyApp : public wxApp
{
public:	
    bool OnInit()
    {
        MyDialog().ShowModal();
        return false;
    }
}; wxIMPLEMENT_APP(MyApp);
gtafan
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 201
Joined: Wed Mar 29, 2017 9:52 am

Re: Re:

Post by gtafan »

PB wrote:
gtafan wrote:Is there no other way to get the event when textctrl looses focus? I really want to use wxTextCtrl and not some other custom class.
I may be missing something but using wxEVT_KILL_FOCUS seems to work just fine?

Code: Select all

#include <wx/wx.h>

class MyDialog : public wxDialog
{
public:
    MyDialog () : wxDialog(NULL, wxID_ANY, "Test")
    {                                      
        wxPanel* mainPanel = new wxPanel(this);
        
        wxTextCtrl* textCtrl = new wxTextCtrl(mainPanel , wxID_ANY);
        textCtrl->Bind(wxEVT_KILL_FOCUS, &MyDialog::OnKillFocus, this);        

        wxTextCtrl* logCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 
            wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);     
        wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl)); 

        wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
        mainSizer->Add(textCtrl, wxSizerFlags().Expand().Border());
        mainSizer->Add(new wxButton(mainPanel, wxID_ANY, "Button"), wxSizerFlags().Expand().Border());
        mainSizer->Add(logCtrl, wxSizerFlags().Expand().Border().Proportion(1));
        mainPanel->SetSizer(mainSizer);                 
    }	
private:    
    void OnKillFocus(wxFocusEvent& event)
    {
        wxLogMessage("textCtrl has just lost focus");      
        event.Skip();
    }
};

class MyApp : public wxApp
{
public:	
    bool OnInit()
    {
        MyDialog().ShowModal();
        return false;
    }
}; wxIMPLEMENT_APP(MyApp);
Seems not to worck for me, the only diference I am using Connect instead of Bind:

Code: Select all

Connect(ID_TEXTCTRL1,wxEVT_KILL_FOCUS,(wxObjectEventFunction)&MyFrame::OnKillFocus);
Can Connect be the problem? If yes, why?
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: Lose focus event for wxTextCtrl?

Post by DavidHart »

Can Connect be the problem? If yes, why?
Yes. You don't show the context of your Connect(), but I would guess you're doing it inside the MyFrame class. Focus events don't propagate, so the frame will never see them.

Try textCtrl->Connect(ID_TEXTCTRL1,wxEVT_KILL_FOCUS,(wxObjectEventFunction)&MyFrame::OnKillFocus);
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Re:

Post by PB »

gtafan wrote:Seems not to worck for me, the only diference I am using Connect instead of Bind:

Code: Select all

Connect(ID_TEXTCTRL1,wxEVT_KILL_FOCUS,(wxObjectEventFunction)&MyFrame::OnKillFocus);
Actually, it seems you made another important change, which broke the code. You now call Connect() on MyFrame, not on the wxTextCtrl instance (the call in my code was textCtrl->Bind()). wxEVT_KILL_FOCUS is not a command event, therefore it does not propagate upwards. Change the call accordingly, it should fix it.

BTW, using Connect() in new code is not recommended, unless you have a good reason for it. Bind() is more flexible, less error prone and it does not require the ugly cast.
gtafan
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 201
Joined: Wed Mar 29, 2017 9:52 am

Re: Re:

Post by gtafan »

PB wrote:
gtafan wrote:Seems not to worck for me, the only diference I am using Connect instead of Bind:

Code: Select all

Connect(ID_TEXTCTRL1,wxEVT_KILL_FOCUS,(wxObjectEventFunction)&MyFrame::OnKillFocus);
Actually, it seems you made another important change, which broke the code. You now call Connect() on MyFrame, not on the wxTextCtrl instance (the call in my code was textCtrl->Bind()). wxEVT_KILL_FOCUS is not a command event, therefore it does not propagate upwards. Change the call accordingly, it should fix it.

BTW, using Connect() in new code is not recommended, unless you have a good reason for it. Bind() is more flexible, less error prone and it does not require the ugly cast.
So it was really Connect(), that cased the problem?
The reason I am using Connect() is because I am using wxSmith and there Connect() is always used.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Re:

Post by PB »

gtafan wrote:So it was really Connect(), that cased the problem?
No, the problem was as both David and me wrote: You changed the control on which the method (Connect) is called, from wxTextCtrl to (probably) its parent. If you did the same to Bind(), it would be wrong as well.
gtafan
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 201
Joined: Wed Mar 29, 2017 9:52 am

Re: Re:

Post by gtafan »

PB wrote:
gtafan wrote:So it was really Connect(), that cased the problem?
No, the problem was as both David and me wrote: You changed the control on which the method (Connect) is called, from wxTextCtrl to (probably) its parent. If you did the same to Bind(), it would be wrong as well.
OK, now I see, normaly wxSmith does the really complicated stuff for me, so I am not so familiar with the way event handlers are initialized. Thanks for the explanation.
gtafan
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 201
Joined: Wed Mar 29, 2017 9:52 am

Re: Lose focus event for wxTextCtrl?

Post by gtafan »

Since I have more then 1 textCtrl, I am geting problems to acces the textCtrl again once it lost focus. After comenting out this line:

Code: Select all

textCtrl->Bind(wxEVT_KILL_FOCUS, &MyDialog::OnKillFocus, this);
everithing worcks fine again.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Lose focus event for wxTextCtrl?

Post by PB »

What do you mean by "problems to acces the textCtrl"? Do you mean that the user cannot set focus to the control with keyboard and/or mouse?

Do you call event.Skip() in the wxEVT_KILL_FOCUS handler (see my previous code)?

Can you reproduce the issue with the MyDialog code below?

Code: Select all

#include <wx/wx.h>

class MyDialog : public wxDialog
{
public:
    MyDialog () : wxDialog(NULL, wxID_ANY, "Test", wxDefaultPosition, wxSize(800, 500))
    {                                     
        wxPanel* mainPanel = new wxPanel(this);
        wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
       
        for ( size_t i = 0; i < 5; ++i )
            AddTextCtrl(mainPanel, mainSizer, wxString::Format("wxTextCtrl #%zu", i + 1));
        
        mainSizer->Add(new wxButton(mainPanel, wxID_ANY, "Button"), wxSizerFlags().Expand().Border());
        
        wxTextCtrl* logCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
            wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);     
        wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl));        
        mainSizer->Add(logCtrl, wxSizerFlags().Expand().Border().Proportion(1));
        
        mainPanel->SetSizer(mainSizer);                 
    }   
private:   
    void AddTextCtrl(wxWindow* parent, wxSizer* sizer, const wxString& name)
    {
        sizer->Add(new wxStaticText(parent, wxID_ANY, name), 
            wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT));

        wxTextCtrl* textCtrl = new wxTextCtrl(parent, wxID_ANY);
        textCtrl->Bind(wxEVT_KILL_FOCUS, &MyDialog::OnKillFocus, this);       
        textCtrl->SetName(name);
        sizer->Add(textCtrl, wxSizerFlags().Expand().Border(wxLEFT | wxBOTTOM | wxRIGHT));
    }

    void OnKillFocus(wxFocusEvent& event)
    {
        wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>(event.GetEventObject());

        wxLogMessage("wxTextCtrl named '%s' has just lost focus", textCtrl->GetName());            
        event.Skip();
    }
};

class MyApp : public wxApp
{
public:   
    bool OnInit()
    {
        MyDialog().ShowModal();
        return false;
    }
}; wxIMPLEMENT_APP(MyApp);
gtafan
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 201
Joined: Wed Mar 29, 2017 9:52 am

Re: Lose focus event for wxTextCtrl?

Post by gtafan »

PB wrote:What do you mean by "problems to acces the textCtrl"? Do you mean that the user cannot set focus to the control with keyboard and/or mouse?

Do you call event.Skip() in the wxEVT_KILL_FOCUS handler (see my previous code)?

Can you reproduce the issue with the MyDialog code below?

Code: Select all

#include <wx/wx.h>

class MyDialog : public wxDialog
{
public:
    MyDialog () : wxDialog(NULL, wxID_ANY, "Test", wxDefaultPosition, wxSize(800, 500))
    {                                     
        wxPanel* mainPanel = new wxPanel(this);
        wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
       
        for ( size_t i = 0; i < 5; ++i )
            AddTextCtrl(mainPanel, mainSizer, wxString::Format("wxTextCtrl #%zu", i + 1));
        
        mainSizer->Add(new wxButton(mainPanel, wxID_ANY, "Button"), wxSizerFlags().Expand().Border());
        
        wxTextCtrl* logCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
            wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);     
        wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl));        
        mainSizer->Add(logCtrl, wxSizerFlags().Expand().Border().Proportion(1));
        
        mainPanel->SetSizer(mainSizer);                 
    }   
private:   
    void AddTextCtrl(wxWindow* parent, wxSizer* sizer, const wxString& name)
    {
        sizer->Add(new wxStaticText(parent, wxID_ANY, name), 
            wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT));

        wxTextCtrl* textCtrl = new wxTextCtrl(parent, wxID_ANY);
        textCtrl->Bind(wxEVT_KILL_FOCUS, &MyDialog::OnKillFocus, this);       
        textCtrl->SetName(name);
        sizer->Add(textCtrl, wxSizerFlags().Expand().Border(wxLEFT | wxBOTTOM | wxRIGHT));
    }

    void OnKillFocus(wxFocusEvent& event)
    {
        wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>(event.GetEventObject());

        wxLogMessage("wxTextCtrl named '%s' has just lost focus", textCtrl->GetName());            
        event.Skip();
    }
};

class MyApp : public wxApp
{
public:   
    bool OnInit()
    {
        MyDialog().ShowModal();
        return false;
    }
}; wxIMPLEMENT_APP(MyApp);
Forgot about that event.Skip(), but an other problem seems to be because the textCtrls are inside notebook page and switching between textCtrls using tab not always worcking.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Lose focus event for wxTextCtrl?

Post by PB »

Please do not quote whole posts, perhaps no quoting is necessary when responding to the last post.

Are you sure the notebook issue is caused by handling the kill focus event, i.e., it persists even when you do not bind the handler for the event? TBH, I am not sure what the issue is exactly, and what is "not always"? Also make sure you do not do things you are not supposed to do in a handler for such events, remember the trouble you had using a message box in a wxEVT_SPINCTRL handler.

I did not observe any issues during cursory testing the simple code below, can you?

Code: Select all

#include <wx/wx.h>
#include <wx/notebook.h>

class MyDialog : public wxDialog
{
public:
    MyDialog () : wxDialog(NULL, wxID_ANY, "Test", wxDefaultPosition, wxSize(800, 500), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
    {                                             
        wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
       
        wxNotebook* notebook = new wxNotebook(this, wxID_ANY);
        AddPage(notebook, "Page 1");
        AddPage(notebook, "Page 2");
        AddPage(notebook, "Page 3");

        mainSizer->Add(notebook, wxSizerFlags().Expand().Border().Proportion(2));
        
        wxTextCtrl* logCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
            wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);     
        wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl));        
        mainSizer->Add(logCtrl, wxSizerFlags().Expand().Border().Proportion(1));
        
        SetSizer(mainSizer);                 
    }   
private:       
    void AddPage(wxNotebook* notebook, const wxString& pageName)
    {
        wxPanel* panel = new wxPanel(notebook);
        wxBoxSizer* panelSizer = new wxBoxSizer(wxVERTICAL);

        for ( size_t i = 0; i < 5; ++i )
            AddTextCtrl(panel, panelSizer, wxString::Format("%s: wxTextCtrl #%zu", pageName, i + 1));        
        
        panelSizer->Add(new wxButton(panel, wxID_ANY, "Button"), wxSizerFlags().Expand().Border());
        
        panel->SetSizer(panelSizer);
        
        notebook->AddPage(panel, pageName);
    }
    
    void AddTextCtrl(wxWindow* parent, wxSizer* sizer, const wxString& name)
    {
        sizer->Add(new wxStaticText(parent, wxID_ANY, name), 
            wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT));

        wxTextCtrl* textCtrl = new wxTextCtrl(parent, wxID_ANY);
        textCtrl->Bind(wxEVT_KILL_FOCUS, &MyDialog::OnKillFocus, this);       
        textCtrl->SetName(name);
        sizer->Add(textCtrl, wxSizerFlags().Expand().Border(wxLEFT | wxBOTTOM | wxRIGHT));
    }

    void OnKillFocus(wxFocusEvent& event)
    {
        wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>(event.GetEventObject());

        wxLogMessage("wxTextCtrl lost focus: '%s'.", textCtrl->GetName());            
        event.Skip();
    }
};

class MyApp : public wxApp
{
public:   
    bool OnInit()
    {
        MyDialog().ShowModal();
        return false;
    }
}; wxIMPLEMENT_APP(MyApp);
Post Reply