Александр (sandy) Илюшенко любезно предоставил статью о создании редактора ячеек wxGrid:
Данная статья посвящена внедрению в грид ячеек для редактирования дат и времени. Сам котрол для дат существует – wxDatePickerCtrl
. Остается вопрос, как прикрутить его к гриду.
Я позаимствовал готовый контрол, за что спасибо автору, и переделал его под свои нужды, а именно, мне нужно было время в 24-часовом формате. Реализацию самого эдитора времени, который я назвал Бcode>wxTimeCtrl я приводить в статье не буду, т.к. много текста. Но обращу Ваше внимание на то, что макрос GRID_TIME_EDITOR
, объявленный в wxTextCtrl.h
, нужен исключительно для использования контрола в гриде, потому, если нужно будет использовать его в ином качестве, закоментируйте строку #define GRID_TIME_EDITOR
.
wxGridCell.h
#pragma once #include <wx/grid.h> #include <wx/generic/gridctrl.h> #include <wx/datectrl.h> #include <wx/dateevt.h> #include "wxTimeCtrl.h" /*** Renderers ***/ /*** Date Renderer ***/ class wxGridCellDateRenderer: public wxGridCellDateTimeRenderer { public: wxGridCellDateRenderer(wxString sOutFormat = wxT("%d.%m.%Y"), wxString sInputFormat = wxT("%Y%m%d")); }; /*** Time Renderer ***/ class wxGridCellTimeRenderer: public wxGridCellDateTimeRenderer { public: wxGridCellTimeRenderer(); }; /*** Editors ***/ /*** Date Editor ***/ class wxGridCellDateEditor: public wxGridCellEditor { const wxString m_Format; wxString m_startValue; void SetDateValue(); wxDatePickerCtrl* DateCtrl() const; protected: // void OnDateChanged(wxDateEvent& event); DECLARE_NO_COPY_CLASS(wxGridCellDateEditor) public: wxGridCellDateEditor(const wxString& format=wxT("%d.%m.%Y")); ~wxGridCellDateEditor(); void Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler); void PaintBackground(const wxRect& rectCell, wxGridCellAttr* attr); void SetSize(const wxRect& rectOrig); void BeginEdit(int row, int col, wxGrid* grid); bool EndEdit(int row, int col, wxGrid* grid); bool IsAcceptedKey(wxKeyEvent& event); void Reset(); wxString GetValue() const; wxGridCellEditor* Clone() const; }; /*** Time Editor ***/ class wxGridCellTimeEditor: public wxGridCellEditor { wxString m_startValue; void SetTimeValue(); wxTimeCtrl* TimeCtrl() const; protected: DECLARE_NO_COPY_CLASS(wxGridCellTimeEditor) public: wxGridCellTimeEditor(); ~wxGridCellTimeEditor(); void Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler); void PaintBackground(const wxRect& rectCell, wxGridCellAttr* attr); void SetSize(const wxRect& rectOrig); void BeginEdit(int row, int col, wxGrid* grid); bool EndEdit(int row, int col, wxGrid* grid); bool IsAcceptedKey(wxKeyEvent& event); void Reset(); wxString GetValue() const; wxGridCellEditor* Clone() const; };
Теперь несколько слов о предоставленных классах:
wxGridCellDateRenderer
– нужен для отображения дат, в том виде, как это нужно программисту, что определяется параметрамиwxString sOutFormat
– это формат собственно отображения даты, например,wxT("%d.%m.%Y")
, т.е. ДД.ММ.ГГГГ, иwxString sInputFormat
– это формат, в котором помещается в грид строка, содержащая датуwxGridCellTimeRenderer
– нужен для отображения времени. Пераметров он не имеет, принимает и отображает время в формате ЧЧ:ММ:ССwxGridCellDateEditor
– это редактор дат, который параметром принимает формат датыwxGridCellTimeEditor
– это редактор времени
wxGridCell.cpp
#include "wxGridCell.h" /*** Renderers ***/ /*** Date Renderer ***/ wxGridCellDateRenderer::wxGridCellDateRenderer(wxString sOutFormat, wxString sInputFormat) : wxGridCellDateTimeRenderer(sOutFormat,sInputFormat) { } /*** Time Renderer ***/ wxGridCellTimeRenderer::wxGridCellTimeRenderer() : wxGridCellDateTimeRenderer(wxT("%H:%M:%S"),wxT("%H:%M:%S")) { } /*** Editors ***/ /*** Date Editor ***/ wxGridCellDateEditor::wxGridCellDateEditor(const wxString& format) : wxGridCellEditor(), m_Format(format), m_startValue(wxT("")) { } wxGridCellDateEditor::~wxGridCellDateEditor() { if (m_control) { m_control->Destroy(); m_control = NULL; } } void wxGridCellDateEditor::Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler) { m_control = new wxDatePickerCtrl(parent, id, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, wxDP_SPIN | wxDP_SHOWCENTURY); wxGridCellEditor::Create(parent, id, evtHandler); } wxDatePickerCtrl* wxGridCellDateEditor::DateCtrl() const { return (wxDatePickerCtrl*)m_control; } void wxGridCellDateEditor::PaintBackground(const wxRect& rectCell, wxGridCellAttr* attr) { wxGridCellEditor::PaintBackground(rectCell, attr); } void wxGridCellDateEditor::SetSize(const wxRect& rectOrig) { wxRect rect(rectOrig); #if defined(__WXGTK__) if (rect.x != 0) { rect.x+=1; rect.y+=1; rect.width-=1; rect.height-=1; } #elif defined(__WXMSW__) // rect.x-=2; // rect.y-=2; // rect.width+=2; rect.height+=2; #else int extra_x=(rect.x>2)?2:1; int extra_y=(rect.y>2)?2:1; #if defined(__WXMOTIF__) extra_x*=2; extra_y*=2; #endif rect.SetLeft(wxMax(0, rect.x-extra_x)); rect.SetTop(wxMax(0, rect.y-extra_y)); rect.SetRight(rect.GetRight()+2*extra_x); rect.SetBottom(rect.GetBottom()+2*extra_y); #endif wxGridCellEditor::SetSize(rect); } void wxGridCellDateEditor::SetDateValue() { wxDateTime tmp; if (!tmp.ParseFormat(m_startValue.c_str(),m_Format.c_str())) { tmp.SetToCurrent(); } DateCtrl()->SetValue(tmp); } void wxGridCellDateEditor::BeginEdit(int row, int col, wxGrid* grid) { wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); m_startValue = grid->GetTable()->GetValue(row, col); SetDateValue(); DateCtrl()->SetFocus(); } bool wxGridCellDateEditor::EndEdit(int row, int col, wxGrid* grid) { wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); bool changed = false; wxDateTime tmp = DateCtrl()->GetValue(); wxString s=tmp.Format(m_Format.c_str()); if (s != m_startValue) changed = true; if (changed) grid->GetTable()->SetValue(row, col, s); m_startValue = wxEmptyString; tmp.SetToCurrent(); DateCtrl()->SetValue(tmp); return changed; } bool wxGridCellDateEditor::IsAcceptedKey(wxKeyEvent& event) { if (wxGridCellEditor::IsAcceptedKey(event)) { int keycode=event.GetKeyCode(); if (keycode<128 && (wxIsdigit(keycode) || keycode=='.')) { return true; } } return false; } void wxGridCellDateEditor::Reset() { wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); SetDateValue(); } wxString wxGridCellDateEditor::GetValue() const { wxDateTime tmp = DateCtrl()->GetValue(); wxString s=tmp.Format(m_Format.c_str()); return s; } wxGridCellEditor* wxGridCellDateEditor::Clone() const { return new wxGridCellDateEditor(m_Format); } /*** Time Editor ***/ wxGridCellTimeEditor::wxGridCellTimeEditor() : wxGridCellEditor(), m_startValue(wxT("")) { } wxGridCellTimeEditor::~wxGridCellTimeEditor() { if (m_control) { m_control->Destroy(); m_control = NULL; } } void wxGridCellTimeEditor::Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler) { m_control = new wxTimeCtrl(parent, id); wxGridCellEditor::Create(parent, id, evtHandler); } void wxGridCellTimeEditor::PaintBackground(const wxRect& rectCell, wxGridCellAttr* attr) { wxGridCellEditor::PaintBackground(rectCell, attr); } wxTimeCtrl* wxGridCellTimeEditor::TimeCtrl() const { return (wxTimeCtrl*)m_control; } void wxGridCellTimeEditor::SetSize(const wxRect& rectOrig) { wxRect rect(rectOrig); #if defined(__WXGTK__) if (rect.x != 0) { rect.x+=1; rect.y+=1; rect.width-=1; rect.height-=1; } #elif defined(__WXMSW__) // rect.x-=2; // rect.y-=2; // rect.width+=2; rect.height+=2; #else int extra_x=(rect.x>2)?2:1; int extra_y=(rect.y>2)?2:1; #if defined(__WXMOTIF__) extra_x*=2; extra_y*=2; #endif rect.SetLeft(wxMax(0, rect.x-extra_x)); rect.SetTop(wxMax(0, rect.y-extra_y)); rect.SetRight(rect.GetRight()+2*extra_x); rect.SetBottom(rect.GetBottom()+2*extra_y); #endif wxGridCellEditor::SetSize(rect); } void wxGridCellTimeEditor::SetTimeValue() { wxString s=m_startValue; if (s.IsEmpty()) { s=wxTimeCtrl::GetCurrentTime(); } TimeCtrl()->SetValue(s); } void wxGridCellTimeEditor::BeginEdit(int row, int col, wxGrid* grid) { wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); m_startValue = grid->GetTable()->GetValue(row, col); SetTimeValue(); TimeCtrl()->SetFocus(); } bool wxGridCellTimeEditor::EndEdit(int row, int col, wxGrid* grid) { wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); bool changed = false; wxString s=TimeCtrl()->GetValue(); if (s != m_startValue) changed = true; if (changed) grid->GetTable()->SetValue(row, col, s); m_startValue = wxEmptyString; TimeCtrl()->SetValue(m_startValue); return changed; } bool wxGridCellTimeEditor::IsAcceptedKey(wxKeyEvent& event) { if (wxGridCellEditor::IsAcceptedKey(event)) { int keycode=event.GetKeyCode(); if (keycode<128 && (wxIsdigit(keycode) || keycode=='.')) { return true; } } return false; } void wxGridCellTimeEditor::Reset() { wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); SetTimeValue(); } wxString wxGridCellTimeEditor::GetValue() const { return TimeCtrl()->GetValue(); } wxGridCellEditor* wxGridCellTimeEditor::Clone() const { return new wxGridCellTimeEditor(); }
Как это все работает? Вот пример, в котором я использую wxGridCtrl из предыдущей статьи.
Example.cpp
#include "wxGridCtrl.h" #include "wxGridCell.h" /* ... */ m_pGrid = new wxGridCtrl(this, IDC_CHILD_GRID); m_pGrid->CreateGrid(0,0); m_pGrid->SetRowLabelSize(20); m_pGrid->SetColLabelSize(20); m_pGrid->DisableDragRowSize(); m_pGrid->SetDefaultCellBackgroundColour(wxColour(128,128,128)); m_pGrid->AppendCols(3); wxGridCellAttr* pDateAttr = new wxGridCellAttr; pDateAttr->SetBackgroundColour(wxColour(255,255,255)); pDateAttr->SetAlignment(wxALIGN_RIGHT,wxALIGN_CENTRE); pDateAttr->SetRenderer(new wxGridCellDateRenderer()); pDateAttr->SetEditor(new wxGridCellDateEditor(wxT("%Y%m%d"))); wxGridCellAttr* pTimeAttr = new wxGridCellAttr; pTimeAttr->SetBackgroundColour(wxColour(255,255,255)); pTimeAttr->SetAlignment(wxALIGN_RIGHT,wxALIGN_CENTRE); pTimeAttr->SetRenderer(new wxGridCellTimeRenderer()); pTimeAttr->SetEditor(new wxGridCellTimeEditor()); m_pGrid->SetColLabelValue(0,wxT("DateCells")); m_pGrid->SetColLabelValue(1,wxT("TimeCells")); m_pGrid->SetColAttr(0,pDateAttr); m_pGrid->SetColAttr(1,pTimeAttr); m_pGrid->Refresh(); /* ... */
Вот так в результате будет выглядеть наше приложение: