Привязка данных к элементам управления и проверка корректности ввода данных в wxWidgets

Несколько раз пытался начать писать эту статью, но находил все новые и новые варианты доработки примера к ней, поэтому времени ушло много. Надеюсь, материал, изложенный ниже, принесет кому-нибудь пользу.

В этот раз я хочу рассказать о таком полезном явлении, как привязка данных и проверка вводимых значений.

Многие из нас помнят, какими неудобными были установка и получение значений, отображаемых элементами управления, с использованием WinAPI. Все эти GetWindowText/SetWindowText с последующей проверкой значений вручную… это ведь так долго. На это уходит столько времени.

К счастью для нас, в wxWidgets для обеспечения привязки данных и их автоматического отображения существуют специальная группа классов, называемых валидаторами (Validators).

Большинство элементов управления в wxWidgets поддерживают обмен данными через валидаторы. В wxWidgets существует механизм привязки данных к стандартным элементам управления (такие как wxTextCtrl, wxListBox, wxListCtrl, wxSpinCtrl и др.). Для этой цели используются классы wxGenericValidator и wxTextValidator.
С помощью класса wxGenericValidator возможна привязка данных типа int, bool, wxString, wxArrayInt (wxArrayInt используется для получения списка выделенных элементов например в wxListBox).

Для того, чтобы обеспечить привязку данных к элементу управления, необходимо
– Создать переменную-член класса формы
– После того, как элемент управления создан, привязать к нему данные с помощью метода SetValidator
– Валидатор можно задать и при создании элемента управления (в качестве параметра конструктора или метода Create)
Делается это довольно просто

class MyDialog : public wxDialog
{
	double m_Width;
...
};
...
wxTextCtrl width_textctrl = new wxTextCtrl(this, ID_QUANTITY_TEXTCTRL,
wxEmptyString, wxDefaultPosition, wxSize(150,-1), 
0, wxFPValidator(&m_Width));

Давайте создадим небольшое тестовое приложение и посмотрим, как работает механизм привязки данных.

Application.h

#ifndef _APPLICATION_H
#define _APPLICATION_H

#include 

class MyApp : public wxApp
{		
	wxImageList m_ImageList;
public:	
	virtual bool OnInit();
	wxImageList & GetImageList()
	{
		return m_ImageList;
	}
};

DECLARE_APP(MyApp);

#endif

Application.cpp

#include "Application.h"
#include "MainFrame.h"
#include 

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{		
	wxImage::AddHandler(new wxXPMHandler);
	m_ImageList.Create(15, 15);
	m_ImageList.Add(wxBitmap(wxT("package_ok.xpm"), wxBITMAP_TYPE_XPM));
	m_ImageList.Add(wxBitmap(wxT("package_error.xpm"), wxBITMAP_TYPE_XPM));
	MainFrame * frame = new MainFrame;
	SetTopWindow(frame);
	frame->Centre();
	frame->Show();
	return true;
}

MainFrame.h

#ifndef _MAIN_FRAME_H
#define _MAIN_FRAME_H

#include 

class MainFrame : public wxFrame
{		
public:
	MainFrame();
	DECLARE_EVENT_TABLE();	
	void OnShowStandardValidators(wxCommandEvent & event);
};

#endif

MainFrame.cpp

#include "MainFrame.h"

#include "DefaultValidatorsDialog.h"

enum
{
	ID_MANUAL_VALIDATION_BUTTON = 10001,
	ID_STANDARD_VALIDATORS_BUTTON,
	ID_EXTENDED_VALIDATORS_BUTTON
};

BEGIN_EVENT_TABLE(MainFrame, wxFrame)
EVT_BUTTON(ID_MANUAL_VALIDATION_BUTTON, MainFrame::OnShowManualValidation)
EVT_BUTTON(ID_STANDARD_VALIDATORS_BUTTON, MainFrame::OnShowStandardValidators)
EVT_BUTTON(ID_EXTENDED_VALIDATORS_BUTTON, MainFrame::OnShowExtendedValidators)
END_EVENT_TABLE()

MainFrame::MainFrame()
: wxFrame(NULL, wxID_ANY, _("wxFPValidator Test"), wxDefaultPosition, wxDefaultSize, 
		  wxCAPTION|wxCLOSE_BOX|wxSYSTEM_MENU|wxRAISED_BORDER)
{
#ifdef __WXMSW__
	SetIcon(wxIcon(wxT("MAIN_ICON"), wxBITMAP_TYPE_ICO_RESOURCE));
#else
	SetIcon(wxIcon(wxT("wxwin.ico"), wxBITMAP_TYPE_ICO));
#endif

	wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
	SetSizer(sizer);

	wxButton * standard_validators_button = new wxButton(this,
 ID_STANDARD_VALIDATORS_BUTTON, _("Standard Validators"));
	sizer->Add(standard_validators_button, 0, wxGROW|wxALL, 5);

	sizer->Fit(this);
	SetMinSize(GetSize());

	SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
}

void MainFrame::OnShowStandardValidators(wxCommandEvent & event)
{
	DefaultValidatorsDialog * dlg = new DefaultValidatorsDialog(this);
	if(dlg->ShowModal() == wxID_OK)
	{
		wxString result;
		double salary(0);
		if(!dlg->GetSalary().ToDouble(&salary))
		{
			wxMessageBox(wxT("Illagal value of 'Salary' field"));
			return;
		}
		result = wxString::Format(
_("Name = %s\r\nAge = %i\r\nSalary = %1.2f\r\nMarital Status = %s\r\nSkills = "),
			dlg->GetName().GetData(),
			dlg->GetAge(),
			salary,
			dlg->GetMaritalStatus() ? 
wxT("married") : wxT("not married"));
		wxString skill_list;
		for(size_t i = 0; i < dlg->GetSkills().Count(); i++)
		{
			if(!skill_list.IsEmpty()) skill_list += wxT(",");
			skill_list += wxString::Format(
wxT("%i"), dlg->GetSkills()[i]);
		}
		result += skill_list;
		wxMessageBox(result);
	}
	dlg->Destroy();
}

DefaultValidatorsDialog.h

#ifndef _DEFAULT_VALIDATORS_DIALOG_H
#define _DEFAULT_VALIDATORS_DIALOG_H

#include 

#define DefaultValidatorsDialogName _("Default Validators")

class DefaultValidatorsDialog : public wxDialog
{
	wxString m_Name;
	int m_Age;
	wxString m_Salary;
	bool m_Married;
	wxArrayInt m_Skills;
	void CreateControls();
public:
	DefaultValidatorsDialog();
	DefaultValidatorsDialog(wxWindow * parent, wxWindowID id = wxID_ANY, 
		const wxString title = DefaultValidatorsDialogName);
	bool Create(wxWindow * parent, wxWindowID id = wxID_ANY, 
		const wxString title = DefaultValidatorsDialogName);

	wxString GetName() {return m_Name;}
	int GetAge() {return m_Age;}
	wxString GetSalary() {return m_Salary;}
	bool GetMaritalStatus() {return m_Married;}
	wxArrayInt & GetSkills() {return m_Skills;}
};

#endif

DefaultValidatorsDialog.cpp

#include "DefaultValidatorsDialog.h"
#include 
#include 
#include 
#include 
#include 

enum
{
	ID_NAME_TEXTCTRL = 10001,
	ID_AGE_SPINCTRL,
	ID_SALARY_TEXTCTRL,
	ID_MARITAL_CHECKBOX,
	ID_SKILLS_LISTBOX
};

DefaultValidatorsDialog::DefaultValidatorsDialog()
{
}

DefaultValidatorsDialog::DefaultValidatorsDialog(wxWindow * parent, wxWindowID id, const wxString title)
{
	Create(parent, id, title);
}

bool DefaultValidatorsDialog::Create(wxWindow * parent, wxWindowID id, const wxString title)
{
	bool res = wxDialog::Create(parent, id, title);
	if(res)
	{
		m_Name = wxEmptyString;
		m_Age = 20;
		m_Married = false;
		m_Skills.Clear();
		CreateControls();
		Centre();
	}
	return res;
}

void DefaultValidatorsDialog::CreateControls()
{
	wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
	SetSizer(sizer);

	wxFlexGridSizer * flexgrid_sizer = new wxFlexGridSizer(2,5);
	flexgrid_sizer->AddGrowableCol(1);

	wxStaticText * name_label = new wxStaticText(this, wxID_ANY, _("Name:"));
	wxStaticText * age_label = new wxStaticText(this, wxID_ANY, _("Age:"));
	wxStaticText * salary_label = new wxStaticText(this, wxID_ANY, _("Salary:"));
	wxStaticText * marital_label = new wxStaticText(this, wxID_ANY, _("Marital Status:"));
	wxStaticText * skills_label = new wxStaticText(this, wxID_ANY, _("Skills:"));

	wxTextCtrl * name_textctrl = new wxTextCtrl(this, ID_NAME_TEXTCTRL, wxEmptyString, 
		wxDefaultPosition, wxSize(150,-1));
	wxSpinCtrl * age_spinctrl = new wxSpinCtrl(this, ID_AGE_SPINCTRL);
	wxTextCtrl * salary_textctrl = new wxTextCtrl(this, ID_SALARY_TEXTCTRL, wxEmptyString, 
		wxDefaultPosition, wxSize(150,-1));
	wxCheckBox * marital_checkbox = new wxCheckBox(this, ID_MARITAL_CHECKBOX, wxT("Married"));
	wxArrayString skills;
	skills.Add(wxT("C/C++"));
	skills.Add(wxT("C#"));
	skills.Add(wxT("Delphi"));
	skills.Add(wxT("Visual Basic"));
	skills.Add(wxT("ASP.NET"));
	skills.Add(wxT("SQL"));
	wxListBox * skills_listbox = new wxListBox(this, ID_SKILLS_LISTBOX, 
		wxDefaultPosition, wxDefaultSize, skills, wxLB_MULTIPLE);

	name_textctrl->SetValidator(wxGenericValidator(&m_Name));
	age_spinctrl->SetValidator(wxGenericValidator(&m_Age));
	salary_textctrl->SetValidator(wxGenericValidator(&m_Salary));
	marital_checkbox->SetValidator(wxGenericValidator(&m_Married));
	skills_listbox->SetValidator(wxGenericValidator(&m_Skills));

	flexgrid_sizer->Add(name_label, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE, 5);
    flexgrid_sizer->Add(name_textctrl, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);

	flexgrid_sizer->Add(age_label, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE, 5);
    flexgrid_sizer->Add(age_spinctrl, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);

	flexgrid_sizer->Add(salary_label, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE, 5);
    flexgrid_sizer->Add(salary_textctrl, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);

	flexgrid_sizer->Add(marital_label, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE, 5);
	flexgrid_sizer->Add(marital_checkbox, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);

	flexgrid_sizer->Add(skills_label, 0, wxALIGN_LEFT|wxALIGN_TOP|wxALL|wxADJUST_MINSIZE, 5);
	flexgrid_sizer->Add(skills_listbox, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);

	sizer->Add(flexgrid_sizer, 0, wxEXPAND|wxALL, 10);

	wxBoxSizer * button_sizer = new wxBoxSizer(wxHORIZONTAL);
	wxButton * ok_button = new wxButton(this, wxID_OK, _("OK"));
	wxButton * cancel_button = new wxButton(this, wxID_CANCEL, _("Cancel"));
	button_sizer->Add(ok_button, 0, wxLEFT|wxBOTTOM, 5);
	button_sizer->Add(cancel_button, 0, wxLEFT|wxRIGHT|wxBOTTOM, 5);

	sizer->Add(button_sizer, 0, wxALIGN_CENTER|wxBOTTOM, 5);

	sizer->Fit(this);
}

У нас должно получиться что-то подобное:

Double value validator for wxWidgets - 1

  • Name – привязка строковой переменной
  • Age – привязка целочисленной переменной
  • Salary – к сожалению wxWidgets не предоставляет возможности привязать к элементу управления переменную типа double, поэтому мы делаем привязку строковой переменной и преобразуем ее в double после закрытия окна диалога.
  • Marital Status – привязка переменной типа bool
  • Skills – привязка массива индексов

Давайте проанализируем, что же у нас получилось.
1. В поле Name и Age ведем любое значение, в поле Salary введем корректное значение для типа double (например 123.25), отметим флаг Marital Status, выделим несколько элементов в списке Skills.

Double value validator for wxWidgets - 2

Теперь жмем OK и наблюдаем результат

Double value validator for wxWidgets - 3

2. Теперь проделаем те же действия, но в поле Salary введем заведомо некорректное значение

Double value validator for wxWidgets - 4

Жмем ОК и смотрим, что же у нас получилось.

Double value validator for wxWidgets - 5

Данное сообщение мы имеем возможность лицезреть только потому, что мы самостоятельно в тексте программы делаем проверку корректности преобразования wxString в double

double salary(0);
if(!dlg->GetSalary().ToDouble(&salary))
{
	wxMessageBox(wxT("Illagal value of 'Salary' field"));
	return;
}

И только так мы можем узнать о том, что пользователь ввел некорректное значение.
Из этого следует вывод: стандартные валидаторы wxWidgets обеспечивают привязку данных, но не обеспечивают их проверку в процессе ввода данных, что само по себе не очень удобно т.к. требует написания кода проверки значения для каждой переменной.

Что можно сделать? Можно немного «доработать напильником» и избавить себя от большого количества проблем в будущем. Этим мы сейчас и займемся.

Под «этим» я имею в виду написание собственного валидатора с поддкржкой типа double.

wxFPValidator.h

#ifndef _WX_FP_VALIDATOR_H
#define _WX_FP_VALIDATOR_H

#include 
#include 

class wxFPValidator : public wxValidator
{
protected:
	double * m_pDouble;
	int * m_pInt;
	wxString * m_pString;
	void Initialize();
public:	
	wxFPValidator(double * valPtr);
	wxFPValidator(int * valPtr);
	wxFPValidator(wxString * valPtr);
	wxFPValidator(const wxFPValidator & validator);
	~wxFPValidator();
	virtual wxValidator* Clone() const;
	virtual bool TransferFromWindow();
	virtual bool TransferToWindow();
	virtual bool Validate(wxWindow * parent);
};

#endif

Описание методов:

  • TransferToWindow – предназначен для отображения значения переменной в элементе управления
  • TransfedFromWindow – предназначен для получения значения из элемента управления в переменную
  • Validate – предназначен для проверки значения введеного пользователем

Метод TransferToWindow вызывается когда мы в нашей программе после установки значений переменных вызываем TransferDataToWindow>/code? чтобы отобразить значения в элементах управления. Например

m_Name = wxEmptyString;
m_Age = 20;
m_Married = false;
m_Skills.Clear();
TransferDataToWindow();

Метод TransferToWindow вызывается когда мы в нашей программе вызываем TransferDataFromWindow чтобы получить значения, введенные пользователем.

При работе с диалогами, после закрытия диалогового окна, введенные значения автоматически записываются в переменные, ассоциированные с элементами управления.

DefaultValidatorsDialog * dlg = new DefaultValidatorsDialog(this);
if(dlg->ShowModal() == wxID_OK)
{
	wxString result;
	double salary(0);
	if(!dlg->GetSalary().ToDouble(&salary))
	{
		wxMessageBox(wxT("Illagal value of 'Salary' field"));
		return;
	}
	...
}

wxFPValidator.cpp

#include 
#include 

wxFPValidator::wxFPValidator(double * valPtr)
{
	Initialize();
	m_pDouble = valPtr;	
}

wxFPValidator::wxFPValidator(int * valPtr)
{
	Initialize();
	m_pInt = valPtr;	
}

wxFPValidator::wxFPValidator(wxString * valPtr)
{
	Initialize();
	m_pString = valPtr;	
}

wxFPValidator::wxFPValidator(const wxFPValidator & validator)
: m_pDouble(validator.m_pDouble), m_pInt(validator.m_pInt),
m_pString(validator.m_pString)
{
}

wxFPValidator::~wxFPValidator()
{
}

void wxFPValidator::Initialize()
{
	m_pDouble = NULL;
	m_pInt = NULL;
	m_pString = NULL;
}

wxValidator* wxFPValidator::Clone() const
{
	return new wxFPValidator(*this);
}

bool wxFPValidator::TransferFromWindow()
{
	if(!m_validatorWindow) 
	{
		return false;
	}
#if wxUSE_TEXTCTRL
    if (m_validatorWindow->IsKindOf(CLASSINFO(wxTextCtrl)) )
    {
        wxTextCtrl * pControl = (wxTextCtrl*) m_validatorWindow;
		if(m_pDouble)
		{
			return pControl->GetValue().ToDouble(m_pDouble);
		}
		else if(m_pInt)
		{
			double tmp;
			bool res = pControl->GetValue().ToDouble(&tmp);
			if(tmp >= INT_MIN && tmp < = INT_MAX && (fmod(tmp,1) == 0))
			{
				*m_pInt = (int)tmp;
			} else res = false;
			return res;
		}
		else if(m_pString)
		{
			wxString tmp = pControl->GetValue();
			if(!tmp.IsEmpty())
			{
				*m_pString = tmp;
				return true;
			}
			return false;
		}
	}
#endif
	return false;
}

bool wxFPValidator::TransferToWindow()
{	
	if(!m_validatorWindow) 
	{
		return false;
	}
#if wxUSE_TEXTCTRL
    if (m_validatorWindow->IsKindOf(CLASSINFO(wxTextCtrl)) )
    {
        wxTextCtrl * pControl = (wxTextCtrl*) m_validatorWindow;
		if(m_pDouble)
		{
			pControl->SetValue(wxString::Format(wxT("%1.2f"), *m_pDouble));
			return true;
		}
		else if(m_pInt)
		{
			pControl->SetValue(wxString::Format(wxT("%i"), *m_pInt));
			return true;
		}
		else if(m_pString)
		{
			pControl->SetValue(*m_pString);
			return true;
		}
	}
#endif
	return false;
}

bool wxFPValidator::Validate(wxWindow * parent)
{	
	if(!m_validatorWindow) 
	{
		return false;
	}
#if wxUSE_TEXTCTRL
    if (m_validatorWindow->IsKindOf(CLASSINFO(wxTextCtrl)) )
    {
        wxTextCtrl * pControl = (wxTextCtrl*) m_validatorWindow;		
		if(m_pDouble != NULL)
		{			
			return pControl->GetValue().ToDouble(m_pDouble);
		}
		else if(m_pInt != NULL)
		{
			double tmp;
			bool res = pControl->GetValue().ToDouble(&tmp);
			if(tmp >= INT_MIN && tmp < = INT_MAX && (fmod(tmp,1) == 0))
			{
				*m_pInt = (int)tmp;
			} else res = false;
			return res;
		}
		else if(m_pString != NULL)
		{
			return !pControl->GetValue().IsEmpty();
		}
	}
#endif	
	return false;
}

Как видно из исходного кода, наш валидатор позволяет выполнять привязку переменных типа wxString, int и double.
Обработка выполняется только для элементов управления типа wxTextCtrl. Для этого существует соответствующая проверка с помощью RTTI

if (m_validatorWindow->IsKindOf(CLASSINFO(wxTextCtrl)) )
{
      wxTextCtrl * pControl = (wxTextCtrl*) m_validatorWindow;		
if(m_pDouble != NULL)
	{			
		return pControl->GetValue().ToDouble(m_pDouble);
	}
	...
}

Для других типов элементов управления соответветствующий функционал необходимо писать самостоятельно (или же посмотреть реализацию класса wxGenericValidator и взять исходный код оттуда).

Хотелось бы заметить, что для стандартного wxGenericValidator метод Validate всегда возвращает true не зависимо от того, коррктно ли введенное значение. Функционал проверки значений просто не реализован и его реализация ложится на плечи разработчика (т.е. на наши с вами).

Теперь у нас есть валидатор с привязкой переменных типа double да еще и с проверкой введенного значения. Но было бы неплохо организовать проверку прямо при вводе данных. Это намного удобнее чем выдавать сообщение об ошибке уже после закрытия окна ввода (такая ситуация особенно неприятна при заполнении больших форм).

MainFrame.h

#ifndef _MAIN_FRAME_H
#define _MAIN_FRAME_H

#include 

class MainFrame : public wxFrame
{	
	...	
	void OnShowManualValidation(wxCommandEvent & event);
	...
};

#endif

MainFrame.cpp

...
#include "ManualValidationDialog.h"
...
BEGIN_EVENT_TABLE(MainFrame, wxFrame)
...
EVT_BUTTON(ID_MANUAL_VALIDATION_BUTTON, MainFrame::OnShowManualValidation)
...
END_EVENT_TABLE()
...
MainFrame::MainFrame()
: wxFrame(NULL, wxID_ANY, _("wxFPValidator Test"), wxDefaultPosition, wxDefaultSize, 
		  wxCAPTION|wxCLOSE_BOX|wxSYSTEM_MENU|wxRAISED_BORDER)
{
...
	wxButton * manual_validation_button = new wxButton(this,
 ID_MANUAL_VALIDATION_BUTTON, _("Extended Validators - Manual Validation"));
	sizer->Add(manual_validation_button, 0, wxGROW|wxALL, 5);
	...
}
...
void MainFrame::OnShowManualValidation(wxCommandEvent & event)
{
	ManualValidationDialog * dlg = new ManualValidationDialog(this);
	if(dlg->ShowModal())
	{
	}
	dlg->Destroy();
}

ManualValidationDialog.h

#ifndef _MANUAL_VALIDATION_DIALOG_H
#define _MANUAL_VALIDATION_DIALOG_H

#include 

#define ManualValidationDialogName _("Manual Validation")

class ManualValidationDialog : public wxDialog
{
	double m_Width;
	double m_Height;
	int m_Quantity;

	wxStaticBitmap * m_WidthStatusBitmap;
	wxStaticBitmap * m_HeightStatusBitmap;
	wxStaticBitmap * m_QuantityStatusBitmap;
	wxTextCtrl * m_WidthTextCtrl;
	wxTextCtrl * m_HeightTextCtrl;
	wxTextCtrl * m_QuantityTextCtrl;

	void CreateControls();
public:
	ManualValidationDialog();
	ManualValidationDialog(wxWindow * parent, wxWindowID id = wxID_ANY, 
		const wxString title = ManualValidationDialogName);
	bool Create(wxWindow * parent, wxWindowID id = wxID_ANY, 
		const wxString title = ManualValidationDialogName);

	double GetWidth() {return m_Width;}
	double GetHeight() {return m_Height;}
	int GetQuantity() {return m_Quantity;}

	DECLARE_EVENT_TABLE();
	void OnTextCtrlUpdate(wxCommandEvent & event);
	void OnSubmitButtonUpdateUI(wxUpdateUIEvent & event);
	void OnSubmit(wxCommandEvent & event);

};

#endif

ManualValidationDialog.cpp

#include "ManualValidationDialog.h"

#include 
#include 
#include 

#include "Application.h"

enum
{
	ID_WIDTH_TEXTCTRL = 10001,
	ID_HEIGHT_TEXTCTRL,
	ID_QUANTITY_TEXTCTRL,
	ID_WIDTH_STATIC_BITMAP,
	ID_HEIGHT_STATIC_BITMAP,
	ID_QUANTITY_STATIC_BITMAP,	
	ID_SUBMIT_BUTTON,	
};

BEGIN_EVENT_TABLE(ManualValidationDialog, wxDialog)
EVT_TEXT(ID_WIDTH_TEXTCTRL, ManualValidationDialog::OnTextCtrlUpdate)
EVT_TEXT(ID_HEIGHT_TEXTCTRL, ManualValidationDialog::OnTextCtrlUpdate)
EVT_TEXT(ID_QUANTITY_TEXTCTRL, ManualValidationDialog::OnTextCtrlUpdate)
EVT_UPDATE_UI(ID_SUBMIT_BUTTON, ManualValidationDialog::OnSubmitButtonUpdateUI)
EVT_BUTTON(ID_SUBMIT_BUTTON, ManualValidationDialog::OnSubmit)
END_EVENT_TABLE()

ManualValidationDialog::ManualValidationDialog(wxWindow * parent, 
						wxWindowID id, const wxString title)
{
	m_Width = 1024;
	m_Height = 768;
	m_Quantity = 10;
	Create(parent, id, title);
}

bool ManualValidationDialog::Create(wxWindow * parent, 
					wxWindowID id, const wxString title)
{
	bool res = wxDialog::Create(parent, id, title);
	if(res)
	{
		SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
		CreateControls();
		Centre();
	}
	return res;
}

void ManualValidationDialog::CreateControls()
{
	wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
	SetSizer(sizer);

	wxFlexGridSizer * flex_grid_sizer = new wxFlexGridSizer(2, 3, 2, 2);	

	wxStaticText * label_width = new wxStaticText(this, wxID_ANY, _("Width:"));
	wxStaticText * label_height = new wxStaticText(this, wxID_ANY, _("Height:"));
	wxStaticText * label_quantity = new wxStaticText(this, wxID_ANY, _("Quantity:"));

	m_WidthTextCtrl = new wxTextCtrl(this, ID_WIDTH_TEXTCTRL, wxEmptyString,
		wxDefaultPosition, wxSize(150,-1), 0, wxFPValidator(&m_Width));
	m_HeightTextCtrl = new wxTextCtrl(this, ID_HEIGHT_TEXTCTRL, wxEmptyString,
		wxDefaultPosition, wxSize(150,-1), 0, wxFPValidator(&m_Height));
	m_QuantityTextCtrl = new wxTextCtrl(this, ID_QUANTITY_TEXTCTRL, wxEmptyString,
		wxDefaultPosition, wxSize(150,-1), 0, wxFPValidator(&m_Quantity));

	wxBitmap tmp_bitmap(15, 15);
	m_WidthStatusBitmap = new wxStaticBitmap(this, 
		ID_WIDTH_STATIC_BITMAP, tmp_bitmap);
	m_HeightStatusBitmap = new wxStaticBitmap(this, 
		ID_HEIGHT_STATIC_BITMAP, tmp_bitmap);
	m_QuantityStatusBitmap = new wxStaticBitmap(this, 
		ID_HEIGHT_STATIC_BITMAP, tmp_bitmap);
	
    flex_grid_sizer->Add(label_width, 0, 
		wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE, 5);
    flex_grid_sizer->Add(m_WidthTextCtrl, 0, 
		wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);
    flex_grid_sizer->Add(m_WidthStatusBitmap, 0, 
		wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5);

	flex_grid_sizer->Add(label_height, 0, 
		wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE, 5);
    flex_grid_sizer->Add(m_HeightTextCtrl, 0, 
		wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);
    flex_grid_sizer->Add(m_HeightStatusBitmap, 0, 
		wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5);

	flex_grid_sizer->Add(label_quantity, 0, 
		wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE, 5);
    flex_grid_sizer->Add(m_QuantityTextCtrl, 0, 
		wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);
    flex_grid_sizer->Add(m_QuantityStatusBitmap, 0, 
		wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5);

	flex_grid_sizer->AddGrowableCol(1);
	sizer->Add(flex_grid_sizer, 0, wxEXPAND);

	wxButton * submit_button = new wxButton(this, ID_SUBMIT_BUTTON, _("Submit"));
	sizer->Add(submit_button, 0, wxGROW|wxALL, 5);

	sizer->Fit(this);
	SetMinSize(GetSize());
}

void ManualValidationDialog::OnTextCtrlUpdate(wxCommandEvent & event)
{
	wxWindow * target = wxDynamicCast(event.GetEventObject(), wxWindow);
	int bitmapIndex(0);
	wxStaticBitmap * static_bitmap(NULL);
	if(target)
	{
		wxValidator * validator = target->GetValidator();
		if(validator)
		{
			if(!validator->Validate(target))
			{
				bitmapIndex = 1;			
			}
			switch(target->GetId())
			{
			case ID_WIDTH_TEXTCTRL:
				static_bitmap = m_WidthStatusBitmap;
				break;
			case ID_HEIGHT_TEXTCTRL:
				static_bitmap = m_HeightStatusBitmap;
				break;
			case ID_QUANTITY_TEXTCTRL:
				static_bitmap = m_QuantityStatusBitmap;
				break;
			default:
				wxLogTrace(wxTraceMask(), 
					_("Illegal call of ManualValidationDialog::OnTextCtrlUpdate"));
				break;
			}
			if(static_bitmap)
			{
				static_bitmap->SetBitmap(
					wxGetApp().GetImageList().GetBitmap(bitmapIndex));
			}
		}
	}
}

void ManualValidationDialog::OnSubmitButtonUpdateUI(wxUpdateUIEvent & event)
{	
	event.Enable(Validate());
}

void ManualValidationDialog::OnSubmit(wxCommandEvent & event)
{
	if(TransferDataFromWindow())
	{
		wxString message = wxString::Format(
			wxT("Width = %1.2f\r\nHeight = %1.2f\r\nQuantity = %i"), 
			m_Width, m_Height, m_Quantity);
		wxMessageBox(message);
	}
}

Теперь пробуем собрать проект. Должно получиться что-то подобное

Double value validator for wxWidgets - 6

Теперь пытаемся ввести некорректное значение в поле Width (например буквы) и в поле Quantity (буквы или дробное значение)

Double value validator for wxWidgets - 7

Теперь немного о том, как это работает:

Мы сделали обработчик нажатия клавиш для каждого поля ввода ManualValidationDialog::OnTextCtrlUpdate и в этом обработчике изменяем картинку для соответствующего элементаwxStaticBitmap.
Также мы сделали обработчик события wxEVT_UPDATE_UI для кнопки Submit где делаем кнопку активной только в случае успешной проверки введенных значений во всех дочерних элементах управления.

Смотрим на окошки – красиво. Смотрим на исходник – не очень, т.к. опять много кода не связанного напрямую с функционалом приложения.

Сейчас мы попробуем немного автоматизировать отображенис коррекнтости ввода значения. Для этого создадим компонент, которые будет содержать в себе элемент управления и пиктограмму для отображения текущего статуса

wxInputBox.h

#ifndef _WX_INPUT_BOX_H
#define _WX_INPUT_BOX_H

#include 
#include 

#define wxInputBoxName wxT("wxInputBox")

enum wxInputBoxBitmapType
{
	wxINPUTBOX_OK,
	wxINPUTBOX_ERROR
};

class wxInputBox : public wxControl
{
	DECLARE_DYNAMIC_CLASS(wxInputBox);
	wxStaticBitmap * m_StatusBitmap;
	wxWindow * m_Editor;
	wxBitmap m_OKBitmap;
	wxBitmap m_ErrorBitmap;
public:
	wxInputBox();
	wxInputBox(wxWindow * parent, wxWindowID id,
		const wxPoint & pos = wxDefaultPosition,
		const wxSize & size = wxDefaultSize,
		long style = wxNO_BORDER,
		const wxValidator & validator = wxDefaultValidator,
		const wxString & name = wxInputBoxName);
	wxInputBox(wxWindow * parent, wxWindowID id, wxWindow * editor, 
		const wxBitmap & ok_bitmap = wxNullBitmap, 
		const wxBitmap & error_bitmap = wxNullBitmap,
		const wxPoint & pos = wxDefaultPosition,
		const wxSize & size = wxDefaultSize,
		long style = wxNO_BORDER,
		const wxValidator & validator = wxDefaultValidator,
		const wxString & name = wxInputBoxName);
	bool Create(wxWindow * parent, wxWindowID id = wxID_ANY, 
		wxWindow * editor = NULL, 
		const wxBitmap & ok_bitmap = wxNullBitmap, 
		const wxBitmap & error_bitmap = wxNullBitmap,
		const wxPoint & pos = wxDefaultPosition,
		const wxSize & size = wxDefaultSize,
		long style = wxNO_BORDER,
		const wxValidator & validator = wxDefaultValidator,
		const wxString & name = wxInputBoxName);

	virtual bool Validate();
	virtual void SetValidator(const wxValidator& validator);

	static wxBitmap GetDefaultStatusBitmap(wxInputBoxBitmapType bitmap_type);
	void SetEditor(wxWindow * editor);

	wxBitmap GetStatusBitmap(wxInputBoxBitmapType bitmap_type);
	void SetStatusBitmap(wxInputBoxBitmapType bitmap_type, 
		const wxBitmap & bitmap);

	wxValidator * GetValidator() const;

	DECLARE_EVENT_TABLE()
	void OnValueChanged(wxCommandEvent & event);
	void OnSize(wxSizeEvent & event);
};

#endif

wxInputBox.cpp

#include 
#include 

#include "inputbox_ok.xpm"
#include "inputbox_error.xpm"

IMPLEMENT_DYNAMIC_CLASS(wxInputBox, wxControl);

BEGIN_EVENT_TABLE(wxInputBox, wxControl)
EVT_SIZE(wxInputBox::OnSize)
END_EVENT_TABLE()

wxInputBox::wxInputBox()
: wxControl(), m_Editor(NULL)
{
}

wxInputBox::wxInputBox(wxWindow * parent, wxWindowID id, wxWindow * editor, 
		const wxBitmap & ok_bitmap, const wxBitmap & error_bitmap,
		const wxPoint & pos, const wxSize & size, long style, 
		const wxValidator & validator, const wxString & name)
: m_Editor(NULL)
{
	Create(parent, id, editor, ok_bitmap, error_bitmap, 
		pos, size, style, validator, name);
}

wxInputBox::wxInputBox(wxWindow * parent, wxWindowID id, const wxPoint & pos,
		const wxSize & size, long style, 
		const wxValidator & validator, const wxString & name)
: m_Editor(NULL)
{
	Create(parent, id, NULL, wxNullBitmap, wxNullBitmap, 
		pos, size, style, wxDefaultValidator, name);	
}

bool wxInputBox::Create(wxWindow * parent, wxWindowID id, wxWindow * editor, 
		const wxBitmap & ok_bitmap, const wxBitmap & error_bitmap,
		const wxPoint & pos, const wxSize & size, long style, 
		const wxValidator & validator, const wxString & name)
{
	bool res = wxWindow::Create(parent, id, pos, size, style, name);
	if(res)
	{		
		m_OKBitmap = ok_bitmap;
		m_ErrorBitmap = error_bitmap;
		if(!m_OKBitmap.Ok()) m_OKBitmap = 
			wxInputBox::GetDefaultStatusBitmap(wxINPUTBOX_OK);
		if(!m_ErrorBitmap.Ok()) m_ErrorBitmap = 
			wxInputBox::GetDefaultStatusBitmap(wxINPUTBOX_ERROR);
		wxBoxSizer * sizer = new wxBoxSizer(wxHORIZONTAL);
		SetSizer(sizer);
		if(editor)
		{
			SetEditor(editor);
			m_Editor->SetValidator(validator);
		}		
	}
	return res;
}

void wxInputBox::SetEditor(wxWindow * editor)
{
	if(GetSizer())
	{
		GetSizer()->Clear(true);
	}
	wxBoxSizer * sizer = new wxBoxSizer(wxHORIZONTAL);
	SetSizer(sizer);
	wxBitmap bitmap;
	m_StatusBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap);
	if(editor)
	{
		m_Editor = editor;
		m_Editor->Reparent(this);
		sizer->Add(m_Editor, 1, wxEXPAND|wxRIGHT, 5);
		if(m_Editor->IsKindOf(CLASSINFO(wxTextCtrl)))
		{
			m_Editor->Connect(m_Editor->GetId(), 
				wxEVT_COMMAND_TEXT_UPDATED, 
				(wxObjectEventFunction)&wxInputBox::OnValueChanged);
		}
		if(m_Editor->IsKindOf(CLASSINFO(wxListBox)))
		{
			m_Editor->Connect(m_Editor->GetId(), 
				wxEVT_COMMAND_LISTBOX_SELECTED, 
				(wxObjectEventFunction)&wxInputBox::OnValueChanged);
		}
		if(m_Editor->GetValidator())
		{
			if(m_Editor->GetValidator()->TransferFromWindow())
			{
				bitmap = GetStatusBitmap(wxINPUTBOX_OK);
			}
			else bitmap = GetStatusBitmap(wxINPUTBOX_ERROR);
		} else bitmap = GetStatusBitmap(wxINPUTBOX_OK);
	}
	else
	{
		bitmap = wxInputBox::GetStatusBitmap(wxINPUTBOX_ERROR);
		sizer->Add(100, -1, 1, wxEXPAND|wxRIGHT, 5);
	}	
	sizer->Add(m_StatusBitmap, 0, wxALIGN_CENTER);
	sizer->Fit(this);
	SetBestSize(GetSize());	
}

wxBitmap wxInputBox::GetStatusBitmap(wxInputBoxBitmapType bitmap_type)
{
	switch(bitmap_type)
	{
	case wxINPUTBOX_OK:
		if(m_OKBitmap.Ok())
		{
			return m_OKBitmap;
		}	
	case wxINPUTBOX_ERROR:
		if(m_ErrorBitmap.Ok())
		{
			return m_ErrorBitmap;
		}
	default:
		break;
	}
	return wxInputBox::GetDefaultStatusBitmap(bitmap_type);
}

void wxInputBox::SetStatusBitmap(wxInputBoxBitmapType bitmap_type, 
								 const wxBitmap & bitmap)
{
	switch(bitmap_type)
	{
	case wxINPUTBOX_OK:
		m_OKBitmap = bitmap;
		break;
	case wxINPUTBOX_ERROR:
		m_ErrorBitmap = bitmap;
		break;
	default:
		break;
	}
}

wxBitmap wxInputBox::GetDefaultStatusBitmap(wxInputBoxBitmapType bitmap_type)
{
	if(!wxImage::FindHandler(wxBITMAP_TYPE_XPM))
	{
		wxImage::AddHandler(new wxXPMHandler);
	}
	switch(bitmap_type)
	{
	case wxINPUTBOX_OK:
		return wxBitmap(inputbox_ok_xpm);
	case wxINPUTBOX_ERROR:
		return wxBitmap(inputbox_error_xpm);
	default:
		break;
	}

	return wxBitmap(16,16);
}

void wxInputBox::OnValueChanged(wxCommandEvent & event)
{	
	wxWindow * target = wxDynamicCast(event.GetEventObject(), wxWindow);
	if(target)
	{
		wxInputBox * parent = wxDynamicCast(target->GetParent(), wxInputBox);
		if(parent)
		{		
			wxValidator * validator = target->GetValidator();
			wxInputBoxBitmapType bitmap_type = wxINPUTBOX_ERROR;
			if(validator)
			{
				if(validator->Validate(this))
				{
					bitmap_type = wxINPUTBOX_OK;
				}
				parent->m_StatusBitmap->SetBitmap(
					parent->GetStatusBitmap(bitmap_type));
			}		
		}
	}
}

void wxInputBox::OnSize(wxSizeEvent & event)
{
	Layout();
}

bool wxInputBox::Validate()
{
	if(m_Editor)
	{
		wxValidator * validator = m_Editor->GetValidator();		
		if(validator)
		{
			return validator->Validate(this);
		}
	}
	return true;
}

void wxInputBox::SetValidator(const wxValidator& validator)
{
	if(m_Editor)
	{
		m_Editor->SetValidator(validator);
	}
}

Как видно из исходного кода, наш компонент производит замену обработчика события wxEVT_COMMAND_TEXT_UPDATED (или wxEVT_COMMAND_LISTBOX_SELECTED) в ассоциированном с ним элементе управления. Это значит, что если ваш элемент управления имеет функционально важный обработчик указанного выше типа, то применять такой подход нельзя. Вместо этого можно, например переопределить обработку события wxEVT_UPDATE_UI для пиктограммы (и в нем выполнять проверку введенного значения) и этим решить проблему. Но такой подход более ресурсоемкий.
Внесем дополнительные изменения в класс окна

MainFrame.h

#ifndef _MAIN_FRAME_H
#define _MAIN_FRAME_H
...
class MainFrame : public wxFrame
{		
public:
	...	
DECLARE_EVENT_TABLE();	
	...
	void OnShowExtendedValidators(wxCommandEvent & event);
};

#endif

MainFrame.cpp

...
#include "ExtendedValidatorsDialog.h"
...
BEGIN_EVENT_TABLE(MainFrame, wxFrame)
...
EVT_BUTTON(ID_EXTENDED_VALIDATORS_BUTTON, MainFrame::OnShowExtendedValidators)
END_EVENT_TABLE()
...
MainFrame::MainFrame()
: wxFrame(NULL, wxID_ANY, _("wxFPValidator Test"), wxDefaultPosition, wxDefaultSize, 
		  wxCAPTION|wxCLOSE_BOX|wxSYSTEM_MENU|wxRAISED_BORDER)
{

	...
	wxButton * extended_validators_button = new wxButton(this, 
ID_EXTENDED_VALIDATORS_BUTTON, _("Extended Validators - wxInputBox"));
	sizer->Add(extended_validators_button, 0, wxGROW|wxALL, 5);
...
}
...
void MainFrame::OnShowExtendedValidators(wxCommandEvent & event)
{
	ExtendedValidatorsDialog * dlg = new ExtendedValidatorsDialog(this);
	if(dlg->ShowModal() == wxID_OK)
	{
		wxString result;
		result = wxString::Format(
			_("Name = %s\r\nAge = %i\r\nSalary = %1.2f\r\nMarital Status = %s\r\nSkills = "),
			dlg->GetName().GetData(),
			dlg->GetAge(),
			dlg->GetSalary(),
			dlg->GetMaritalStatus() ? wxT("married") : wxT("not married"));
		wxString skill_list;
		for(size_t i = 0; i < dlg->GetSkills().Count(); i++)
		{
			if(!skill_list.IsEmpty()) skill_list += wxT(",");
			skill_list += wxString::Format(wxT("%i"), dlg->GetSkills()[i]);
		}
		result += skill_list;
		wxMessageBox(result);
	}
	dlg->Destroy();
}

ExtendedValidatorsDialog.h

#ifndef _EXTENDED_VALIDATORS_DIALOG_H
#define _EXTENDED_VALIDATORS_DIALOG_H

#include 

#define ExtendedValidatorsDialogName _("Extended Validators")

class ExtendedValidatorsDialog : public wxDialog
{
	wxString m_Name;
	int m_Age;
	double m_Salary;
	bool m_Married;
	wxArrayInt m_Skills;
	void CreateControls();
public:
	ExtendedValidatorsDialog();
	ExtendedValidatorsDialog(wxWindow * parent, wxWindowID id = wxID_ANY, 
		const wxString title = ExtendedValidatorsDialogName);
	bool Create(wxWindow * parent, wxWindowID id = wxID_ANY, 
		const wxString title = ExtendedValidatorsDialogName);

	wxString GetName() {return m_Name;}
	int GetAge() {return m_Age;}
	double GetSalary() {return m_Salary;}
	bool GetMaritalStatus() {return m_Married;}
	wxArrayInt & GetSkills() {return m_Skills;}
	DECLARE_EVENT_TABLE()
	void OnOKUpdateUI(wxUpdateUIEvent & event);
};

#endif

ExtendedValidatorsDialog.cpp

#include "ExtendedValidatorsDialog.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

enum
{
	ID_NAME_TEXTCTRL_EX = 11001,
	ID_AGE_SPINCTRL_EX,
	ID_SALARY_TEXTCTRL_EX,
	ID_MARITAL_CHECKBOX_EX,
	ID_SKILLS_LISTBOX_EX
};

BEGIN_EVENT_TABLE(ExtendedValidatorsDialog, wxDialog)
EVT_UPDATE_UI(wxID_OK, ExtendedValidatorsDialog::OnOKUpdateUI)
END_EVENT_TABLE()

ExtendedValidatorsDialog::ExtendedValidatorsDialog()
{
}

ExtendedValidatorsDialog::ExtendedValidatorsDialog(wxWindow * parent, wxWindowID id, const wxString title)
{
	Create(parent, id, title);
}

bool ExtendedValidatorsDialog::Create(wxWindow * parent, wxWindowID id, const wxString title)
{
	bool res = wxDialog::Create(parent, id, title);
	if(res)
	{
		SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
		m_Name = wxEmptyString;
		m_Age = 20;
		m_Salary = 500;
		m_Married = false;
		m_Skills.Clear();
		CreateControls();
		Centre();
	}
	return res;
}

void ExtendedValidatorsDialog::CreateControls()
{
	wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
	SetSizer(sizer);

	wxFlexGridSizer * flexgrid_sizer = new wxFlexGridSizer(2,5);
	flexgrid_sizer->AddGrowableCol(1);

	wxStaticText * name_label = new wxStaticText(this, wxID_ANY, _("Name:"));
	wxStaticText * age_label = new wxStaticText(this, wxID_ANY, _("Age:"));
	wxStaticText * salary_label = new wxStaticText(this, wxID_ANY, _("Salary:"));
	wxStaticText * marital_label = new wxStaticText(this, wxID_ANY, _("Marital Status:"));
	wxStaticText * skills_label = new wxStaticText(this, wxID_ANY, _("Skills:"));

	wxTextCtrl * name_textctrl = new wxTextCtrl(this, ID_NAME_TEXTCTRL_EX, wxEmptyString, 
		wxDefaultPosition, wxSize(150,-1));
	wxTextCtrl * age_textctrl = new wxTextCtrl(this, ID_AGE_SPINCTRL_EX, wxEmptyString);
	wxTextCtrl * salary_textctrl = new wxTextCtrl(this, ID_SALARY_TEXTCTRL_EX, wxEmptyString);
	wxCheckBox * marital_checkbox = new wxCheckBox(this, ID_MARITAL_CHECKBOX_EX, wxT("Married"));
	wxArrayString skills;
	skills.Add(wxT("C/C++"));
	skills.Add(wxT("C#"));
	skills.Add(wxT("Delphi"));
	skills.Add(wxT("Visual Basic"));
	skills.Add(wxT("ASP.NET"));
	skills.Add(wxT("SQL"));
	wxListBox * skills_listbox = new wxListBox(this, ID_SKILLS_LISTBOX_EX, 
		wxDefaultPosition, wxDefaultSize, skills, wxLB_MULTIPLE);

	name_textctrl->SetValidator(wxFPValidator(&m_Name));
	age_textctrl->SetValidator(wxFPValidator(&m_Age));
	salary_textctrl->SetValidator(wxFPValidator(&m_Salary));
	marital_checkbox->SetValidator(wxGenericValidator(&m_Married));
	skills_listbox->SetValidator(wxGenericValidator(&m_Skills));

	wxInputBox * name_inputbox = new wxInputBox(this, wxID_ANY);
	wxInputBox * age_inputbox = new wxInputBox(this, wxID_ANY);	
	wxInputBox * salary_inputbox = new wxInputBox(this, wxID_ANY);	
	wxInputBox * skills_inputbox = new wxInputBox(this, wxID_ANY);

	name_inputbox->SetEditor(name_textctrl);
	age_inputbox->SetEditor(age_textctrl);
	salary_inputbox->SetEditor(salary_textctrl);
	skills_inputbox->SetEditor(skills_listbox);

	flexgrid_sizer->Add(name_label, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE, 5);
	
    flexgrid_sizer->Add(name_inputbox, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);

	flexgrid_sizer->Add(age_label, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE, 5);
	flexgrid_sizer->Add(age_inputbox, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);

	flexgrid_sizer->Add(salary_label, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE, 5);
	
    flexgrid_sizer->Add(salary_inputbox, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);

	flexgrid_sizer->Add(marital_label, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE, 5);
	flexgrid_sizer->Add(marital_checkbox, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);

	flexgrid_sizer->Add(skills_label, 0, wxALIGN_LEFT|wxALIGN_TOP|wxALL|wxADJUST_MINSIZE, 5);
	
	flexgrid_sizer->Add(skills_inputbox, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5);

	sizer->Add(flexgrid_sizer, 0, wxEXPAND|wxALL, 10);

	wxBoxSizer * button_sizer = new wxBoxSizer(wxHORIZONTAL);
	wxButton * ok_button = new wxButton(this, wxID_OK, _("OK"));
	wxButton * cancel_button = new wxButton(this, wxID_CANCEL, _("Cancel"));
	button_sizer->Add(ok_button, 0, wxLEFT|wxBOTTOM, 5);
	button_sizer->Add(cancel_button, 0, wxLEFT|wxRIGHT|wxBOTTOM, 5);

	sizer->Add(button_sizer, 0, wxALIGN_CENTER|wxBOTTOM, 5);

	TransferDataToWindow();

	sizer->Fit(this);
}

void ExtendedValidatorsDialog::OnOKUpdateUI(wxUpdateUIEvent & event)
{
	event.Enable(Validate());
}

Запускаем, смотрим:
Double value validator for wxWidgets - 8

Как видно, из дополнительного кода остался только обработчик события wxEVT_UPDATE_UI для кнопки. Все остальное реализует наш новій компонент wxInputBox

Подведем итоги

  • Мы создали новый валидатор, позволяющий выполнять привязку переменных типа double и выполнять проверку корректности ввода значений
  • Создали динамическую индикацию корректности ввода данных
  • Разработали компонент, позволяющий автоматизировать работу, связанную с индикацией корректности ввода данных
  • Продемонстрировали работу компонента на примере 🙂

К слову сказать, что попмимо стандартных валидаторов, существуют еще валидаторы от сторонних разработчиков:

wxFormatValidator
и
wxRegExValidator

Скачать исходный код примера к этой статье.

Leave a Reply

Your email address will not be published. Required fields are marked *

Please leave these two fields as-is: