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 » Thu Jul 31, 2008 11:02 am

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 » Fri Aug 15, 2008 3:35 am

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 » Wed Aug 20, 2008 11:55 am

I'll check it out. Thanks!

gtafan
Earned some good credits
Earned some good credits
Posts: 149
Joined: Wed Mar 29, 2017 9:52 am

Re:

Post by gtafan » Fri Jan 04, 2019 11:57 am

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: 2057
Joined: Sun Jan 03, 2010 5:45 pm

Re: Re:

Post by PB » Fri Jan 04, 2019 12:55 pm

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
Earned some good credits
Earned some good credits
Posts: 149
Joined: Wed Mar 29, 2017 9:52 am

Re: Re:

Post by gtafan » Mon Jan 07, 2019 11:17 am

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: 3889
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: Lose focus event for wxTextCtrl?

Post by DavidHart » Mon Jan 07, 2019 11:44 am

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: 2057
Joined: Sun Jan 03, 2010 5:45 pm

Re: Re:

Post by PB » Mon Jan 07, 2019 11:55 am

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
Earned some good credits
Earned some good credits
Posts: 149
Joined: Wed Mar 29, 2017 9:52 am

Re: Re:

Post by gtafan » Mon Jan 07, 2019 1:28 pm

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: 2057
Joined: Sun Jan 03, 2010 5:45 pm

Re: Re:

Post by PB » Mon Jan 07, 2019 1:30 pm

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
Earned some good credits
Earned some good credits
Posts: 149
Joined: Wed Mar 29, 2017 9:52 am

Re: Re:

Post by gtafan » Mon Jan 07, 2019 2:32 pm

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
Earned some good credits
Earned some good credits
Posts: 149
Joined: Wed Mar 29, 2017 9:52 am

Re: Lose focus event for wxTextCtrl?

Post by gtafan » Tue Jan 15, 2019 3:26 pm

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: 2057
Joined: Sun Jan 03, 2010 5:45 pm

Re: Lose focus event for wxTextCtrl?

Post by PB » Tue Jan 15, 2019 8:21 pm

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
Earned some good credits
Earned some good credits
Posts: 149
Joined: Wed Mar 29, 2017 9:52 am

Re: Lose focus event for wxTextCtrl?

Post by gtafan » Wed Jan 16, 2019 2:13 pm

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: 2057
Joined: Sun Jan 03, 2010 5:45 pm

Re: Lose focus event for wxTextCtrl?

Post by PB » Wed Jan 16, 2019 5:32 pm

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