Marco DeFreitas
2011-08-21 20:31:18 UTC
I have an application where I have multiple wxTextCtrls on a panel. I
would like to validate the contents to be sure they are valid (i.e,
positive floating point number). On an invalid entry, I would like to
display a wxMessageBox with the error. When the user dismisses the
message box I would like to set focus to the text field in question
and select the text in it.
It was no problem to validate when the user hits the enter key.
I also wanted to validate when the focus is lost on the (possibly bad)
text field, for example the user might enter text in one field and
then click on another text field.
I was having problems changing focus within a focus event handler;
that is, when focus is lost on a text field with bad data, I was
having problems setting the focus back to the text field with bad
data. However, I got things working reasonably well by just flagging
the error in the "Kill Focus" event handler and then changing the
focus in an "Idle" event handler that checked for the flagged error.
The problem is as follows:
1) I enter bad data in text field #1
2) I click on text field #2
3) I get a wxMessageBox with an error msg; I click OK
4) The focus is now on text field #1 with its text selected (as I
wanted), but the text appears and disappears on text field #2 as I
swipe the cursor back and forth on text field #2. This keeps happening
until I click on text field #2. It seems as though the system thinks
there is still some sort of "psuedo focus" on text field #2? Note that
this problem does not occur if I do not pop up the wxMessageBox.
I tried to recreate the problem in this test program. In this sample,
the text in text field #2 does not appear and disappears, it gets
highlighted/unhighlighted as I swipe the cursor over it; I assume this
is a symptom of the same problem.
Is this a bug? Am I doing something wrong? Is there a better way to
accomplish what I am trying to do?
The sample is as follows... it just have two text fields that take a
valid floating point number:
__________________________________
#include <iostream>
using namespace std;
#include "wx/wx.h"
#define TC_ID_1 wxID_HIGHEST + 1
#define TC_ID_2 TC_ID_1 + 1
class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title);
bool ValidateMe(int id);
void OnTextEntered(wxCommandEvent& e);
void OnIdle(wxIdleEvent& e);
void OnLostFocus(wxFocusEvent& e);
private:
wxTextCtrl *m_tc1;
wxTextCtrl *m_tc2;
wxTextCtrl *m_err_tc;
bool m_err_ack;
DECLARE_EVENT_TABLE()
};
DECLARE_APP(MyApp)
IMPLEMENT_APP(MyApp)
bool MyApp::OnInit()
{
MyFrame *frame = new MyFrame(wxT("Minimal App"));
frame->Show(true);
return true;
}
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_TEXT_ENTER(TC_ID_1, MyFrame::OnTextEntered)
EVT_TEXT_ENTER(TC_ID_2, MyFrame::OnTextEntered)
EVT_IDLE( MyFrame::OnIdle)
END_EVENT_TABLE()
MyFrame::MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY,
title)
{
m_err_tc = 0;
m_err_ack = false;
m_tc1 = new wxTextCtrl(this, TC_ID_1, "0",
wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
m_tc2 = new wxTextCtrl(this, TC_ID_2, "0",
wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
m_tc1->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(
MyFrame::OnLostFocus), NULL, this);
m_tc2->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(
MyFrame::OnLostFocus), NULL, this);
wxBoxSizer *s = new wxBoxSizer(wxVERTICAL);
s->Add(m_tc1, 0, wxEXPAND|wxALL, 5);
s->Add(m_tc2, 0, wxEXPAND|wxALL, 5);
SetSizerAndFit(s);
}
void MyFrame::OnTextEntered(wxCommandEvent& e)
{
ValidateMe(e.GetId());
}
void MyFrame::OnLostFocus(wxFocusEvent& e)
{
ValidateMe(e.GetId());
e.Skip();
}
void MyFrame::OnIdle(wxIdleEvent& e)
{
if (m_err_ack) {
wxTextCtrl *tc = m_err_tc;
m_err_tc = 0;
m_err_ack = false;
tc->SetFocus();
tc->SetSelection(-1, -1);
}
e.Skip();
}
bool MyFrame::ValidateMe(int id)
{
bool ok = true;
wxTextCtrl *tc = 0;
if (id == TC_ID_1) {
tc = m_tc1;
} else if (id == TC_ID_2) {
tc = m_tc2;
}
if (tc) {
wxString s = tc->GetValue();
double d;
if (!s.ToDouble(&d)) {
ok = false;
tc->ChangeValue("0");
m_err_tc = tc;
// Comment out the next showing of the dialog box and the
// error does not occur
wxMessageDialog d(tc, "Not a valid float!", "Error",
wxOK|wxICON_ERROR);
d.ShowModal();
m_err_ack = true;
}
}
return ok;
}
would like to validate the contents to be sure they are valid (i.e,
positive floating point number). On an invalid entry, I would like to
display a wxMessageBox with the error. When the user dismisses the
message box I would like to set focus to the text field in question
and select the text in it.
It was no problem to validate when the user hits the enter key.
I also wanted to validate when the focus is lost on the (possibly bad)
text field, for example the user might enter text in one field and
then click on another text field.
I was having problems changing focus within a focus event handler;
that is, when focus is lost on a text field with bad data, I was
having problems setting the focus back to the text field with bad
data. However, I got things working reasonably well by just flagging
the error in the "Kill Focus" event handler and then changing the
focus in an "Idle" event handler that checked for the flagged error.
The problem is as follows:
1) I enter bad data in text field #1
2) I click on text field #2
3) I get a wxMessageBox with an error msg; I click OK
4) The focus is now on text field #1 with its text selected (as I
wanted), but the text appears and disappears on text field #2 as I
swipe the cursor back and forth on text field #2. This keeps happening
until I click on text field #2. It seems as though the system thinks
there is still some sort of "psuedo focus" on text field #2? Note that
this problem does not occur if I do not pop up the wxMessageBox.
I tried to recreate the problem in this test program. In this sample,
the text in text field #2 does not appear and disappears, it gets
highlighted/unhighlighted as I swipe the cursor over it; I assume this
is a symptom of the same problem.
Is this a bug? Am I doing something wrong? Is there a better way to
accomplish what I am trying to do?
The sample is as follows... it just have two text fields that take a
valid floating point number:
__________________________________
#include <iostream>
using namespace std;
#include "wx/wx.h"
#define TC_ID_1 wxID_HIGHEST + 1
#define TC_ID_2 TC_ID_1 + 1
class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title);
bool ValidateMe(int id);
void OnTextEntered(wxCommandEvent& e);
void OnIdle(wxIdleEvent& e);
void OnLostFocus(wxFocusEvent& e);
private:
wxTextCtrl *m_tc1;
wxTextCtrl *m_tc2;
wxTextCtrl *m_err_tc;
bool m_err_ack;
DECLARE_EVENT_TABLE()
};
DECLARE_APP(MyApp)
IMPLEMENT_APP(MyApp)
bool MyApp::OnInit()
{
MyFrame *frame = new MyFrame(wxT("Minimal App"));
frame->Show(true);
return true;
}
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_TEXT_ENTER(TC_ID_1, MyFrame::OnTextEntered)
EVT_TEXT_ENTER(TC_ID_2, MyFrame::OnTextEntered)
EVT_IDLE( MyFrame::OnIdle)
END_EVENT_TABLE()
MyFrame::MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY,
title)
{
m_err_tc = 0;
m_err_ack = false;
m_tc1 = new wxTextCtrl(this, TC_ID_1, "0",
wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
m_tc2 = new wxTextCtrl(this, TC_ID_2, "0",
wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
m_tc1->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(
MyFrame::OnLostFocus), NULL, this);
m_tc2->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(
MyFrame::OnLostFocus), NULL, this);
wxBoxSizer *s = new wxBoxSizer(wxVERTICAL);
s->Add(m_tc1, 0, wxEXPAND|wxALL, 5);
s->Add(m_tc2, 0, wxEXPAND|wxALL, 5);
SetSizerAndFit(s);
}
void MyFrame::OnTextEntered(wxCommandEvent& e)
{
ValidateMe(e.GetId());
}
void MyFrame::OnLostFocus(wxFocusEvent& e)
{
ValidateMe(e.GetId());
e.Skip();
}
void MyFrame::OnIdle(wxIdleEvent& e)
{
if (m_err_ack) {
wxTextCtrl *tc = m_err_tc;
m_err_tc = 0;
m_err_ack = false;
tc->SetFocus();
tc->SetSelection(-1, -1);
}
e.Skip();
}
bool MyFrame::ValidateMe(int id)
{
bool ok = true;
wxTextCtrl *tc = 0;
if (id == TC_ID_1) {
tc = m_tc1;
} else if (id == TC_ID_2) {
tc = m_tc2;
}
if (tc) {
wxString s = tc->GetValue();
double d;
if (!s.ToDouble(&d)) {
ok = false;
tc->ChangeValue("0");
m_err_tc = tc;
// Comment out the next showing of the dialog box and the
// error does not occur
wxMessageDialog d(tc, "Not a valid float!", "Error",
wxOK|wxICON_ERROR);
d.ShowModal();
m_err_ack = true;
}
}
return ok;
}