Александр (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();
/* ... */
Вот так в результате будет выглядеть наше приложение:
