Несколько раз пытался начать писать эту статью, но находил все новые и новые варианты доработки примера к ней, поэтому времени ушло много. Надеюсь, материал, изложенный ниже, принесет кому-нибудь пользу.
В этот раз я хочу рассказать о таком полезном явлении, как привязка данных и проверка вводимых значений.
Многие из нас помнят, какими неудобными были установка и получение значений, отображаемых элементами управления, с использованием WinAPI. Все эти GetWindowText/SetWindowText
с последующей проверкой значений вручную… это ведь так долго. На это уходит столько времени.
К счастью для нас, в wxWidgets для обеспечения привязки данных и их автоматического отображения существуют специальная группа классов, называемых валидаторами (Validators).
Большинство элементов управления в wxWidgets поддерживают обмен данными через валидаторы. В wxWidgets существует механизм привязки данных к стандартным элементам управления (такие как wxTextCtrl
, wxListBox
, wxListCtrl
, wxSpinCtrl
и др.). Для этой цели используются классы wxGenericValidator
и wxTextValidator
.
С помощью класса wxGenericValidator
возможна привязка данных типа int
, bool
, wxString
, wxArrayInt
(wxArrayInt
используется для получения списка выделенных элементов например в wxListBox
).
Для того, чтобы обеспечить привязку данных к элементу управления, необходимо
– Создать переменную-член класса формы
– После того, как элемент управления создан, привязать к нему данные с помощью метода SetValidator
– Валидатор можно задать и при создании элемента управления (в качестве параметра конструктора или метода Create
)
Делается это довольно просто
1 2 3 4 5 6 7 8 9 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #ifndef _APPLICATION_H #define _APPLICATION_H #include <wx/wx.h> class MyApp : public wxApp { wxImageList m_ImageList; public : virtual bool OnInit(); wxImageList & GetImageList() { return m_ImageList; } }; DECLARE_APP(MyApp); #endif |
Application.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include "Application.h" #include "MainFrame.h" #include <wx/image.h> 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #ifndef _MAIN_FRAME_H #define _MAIN_FRAME_H #include <wx/wx.h> class MainFrame : public wxFrame { public : MainFrame(); DECLARE_EVENT_TABLE(); void OnShowStandardValidators(wxCommandEvent & event); }; #endif |
MainFrame.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | #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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #ifndef _DEFAULT_VALIDATORS_DIALOG_H #define _DEFAULT_VALIDATORS_DIALOG_H #include <wx/wx.h> #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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | #include "DefaultValidatorsDialog.h" #include <wx/textctrl.h> #include <wx/spinctrl.h> #include <wx/checkbox.h> #include <wx/listbox.h> #include <wx/valgen.h> 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 ); } |
У нас должно получиться что-то подобное:
- Name – привязка строковой переменной
- Age – привязка целочисленной переменной
- Salary – к сожалению wxWidgets не предоставляет возможности привязать к элементу управления переменную типа
double
, поэтому мы делаем привязку строковой переменной и преобразуем ее в double после закрытия окна диалога. - Marital Status – привязка переменной типа
bool
- Skills – привязка массива индексов
Давайте проанализируем, что же у нас получилось.
1. В поле Name и Age ведем любое значение, в поле Salary введем корректное значение для типа double (например 123.25), отметим флаг Marital Status, выделим несколько элементов в списке Skills.
Теперь жмем OK и наблюдаем результат
2. Теперь проделаем те же действия, но в поле Salary введем заведомо некорректное значение
Жмем ОК и смотрим, что же у нас получилось.
Данное сообщение мы имеем возможность лицезреть только потому, что мы самостоятельно в тексте программы делаем проверку корректности преобразования wxString
в double
1 2 3 4 5 6 | double salary(0); if (!dlg->GetSalary().ToDouble(&salary)) { wxMessageBox(wxT( "Illagal value of 'Salary' field" )); return ; } |
И только так мы можем узнать о том, что пользователь ввел некорректное значение.
Из этого следует вывод: стандартные валидаторы wxWidgets обеспечивают привязку данных, но не обеспечивают их проверку в процессе ввода данных, что само по себе не очень удобно т.к. требует написания кода проверки значения для каждой переменной.
Что можно сделать? Можно немного «доработать напильником» и избавить себя от большого количества проблем в будущем. Этим мы сейчас и займемся.
Под «этим» я имею в виду написание собственного валидатора с поддкржкой типа double.
wxFPValidator.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #ifndef _WX_FP_VALIDATOR_H #define _WX_FP_VALIDATOR_H #include <wx/wx.h> #include </validate.h> 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? чтобы отобразить значения в элементах управления. Например
12345 m_Name = wxEmptyString;
m_Age = 20;
m_Married =
false
;
m_Skills.Clear();
TransferDataToWindow();
Метод TransferToWindow
вызывается когда мы в нашей программе вызываем TransferDataFromWindow
чтобы получить значения, введенные пользователем.
При работе с диалогами, после закрытия диалогового окна, введенные значения автоматически записываются в переменные, ассоциированные с элементами управления.
123456789101112 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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 #include </wxFPValidator/wxFPValidator.h>
#include <math.h>
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
123456789 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
12345678910111213 #ifndef _MAIN_FRAME_H
#define _MAIN_FRAME_H
#include </wx.h>
class
MainFrame :
public
wxFrame
{
...
void
OnShowManualValidation(wxCommandEvent & event);
...
};
#endif
MainFrame.cpp
12345678910111213141516171819202122232425262728 ...
#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
12345678910111213141516171819202122232425262728293031323334353637383940 #ifndef _MANUAL_VALIDATION_DIALOG_H
#define _MANUAL_VALIDATION_DIALOG_H
#include </wx.h>
#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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 #include "ManualValidationDialog.h"
#include </imaglist.h>
#include </valgen.h>
#include </wxFPValidator/wxFPValidator.h>
#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);
}
}
Теперь пробуем собрать проект. Должно получиться что-то подобное
Теперь пытаемся ввести некорректное значение в поле Width (например буквы) и в поле Quantity (буквы или дробное значение)
Теперь немного о том, как это работает:
Мы сделали обработчик нажатия клавиш для каждого поля ввода ManualValidationDialog::OnTextCtrlUpdate
и в этом обработчике изменяем картинку для соответствующего элементаwxStaticBitmap
.
Также мы сделали обработчик события wxEVT_UPDATE_UI
для кнопки Submit где делаем кнопку активной только в случае успешной проверки введенных значений во всех дочерних элементах управления.
Смотрим на окошки – красиво. Смотрим на исходник – не очень, т.к. опять много кода не связанного напрямую с функционалом приложения.
Сейчас мы попробуем немного автоматизировать отображенис коррекнтости ввода значения. Для этого создадим компонент, которые будет содержать в себе элемент управления и пиктограмму для отображения текущего статуса
wxInputBox.h
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465 #ifndef _WX_INPUT_BOX_H
#define _WX_INPUT_BOX_H
#include <wx/wx.h>
#include <wx/validate.h>
#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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 #include </wxInputBox/wxInputBox.h>
#include </image.h>
#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
12345678910111213 #ifndef _MAIN_FRAME_H
#define _MAIN_FRAME_H
...
class
MainFrame :
public
wxFrame
{
public
:
...
DECLARE_EVENT_TABLE();
...
void
OnShowExtendedValidators(wxCommandEvent & event);
};
#endif
MainFrame.cpp
12345678910111213141516171819202122232425262728293031323334353637383940414243 ...
#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
1234567891011121314151617181920212223242526272829303132 #ifndef _EXTENDED_VALIDATORS_DIALOG_H
#define _EXTENDED_VALIDATORS_DIALOG_H
#include </wx.h>
#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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 #include "ExtendedValidatorsDialog.h"
#include <wx/textctrl.h>
#include <wx/spinctrl.h>
#include <wx/checkbox.h>
#include <wx/listbox.h>
#include <wx/valgen.h>
#include <wx/wxFPValidator/wxFPValidator.h>
#include <wx/wxInputBox/wxInputBox.h>
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());
}
Как видно, из дополнительного кода остался только обработчик события wxEVT_UPDATE_UI
для кнопки. Все остальное реализует наш новій компонент wxInputBox
Подведем итоги
- Мы создали новый валидатор, позволяющий выполнять привязку переменных типа double и выполнять проверку корректности ввода значений
- Создали динамическую индикацию корректности ввода данных
- Разработали компонент, позволяющий автоматизировать работу, связанную с индикацией корректности ввода данных
- Продемонстрировали работу компонента на примере

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