Класс wxGridCtrl - производный от wxGrid

Захотелось мне как-то, чтобы в гриде были не номера строк, а маркер. К тому же очень хотелось, чтобы незаполненное пространство грида было не белым, а, примерно, как на рисунке ниже.

Навеяно это было в основном аналогичными и другими классами, предоставляемыми MFC. Тут же и вспомнилось, что подобные классы также прдоставляют очень полезные методы для хранения дополнительных не отображаемых данных, такие как SetData() или GetData(). Вначеле даже была мысль, использовать для этого wxGrid::SetRowLabelValue(), с учетом того, что перерисовку лэйбла я собирался переопределить. Но, покурив исходники грида, я обнаружил, что в функциях bool InsertRows(int pos = 0, int numRows = 1, bool updateLabels = true) и bool DeleteRows(int pos = 0, int numRows = 1, bool updateLabels = true), третий аргумент не используется. А это значит, что при вставке или удалении строки у меня все посыпется.

К тому же неизвестно, как в будущем этот аргумент будет использоваться. Протому не нашел я ничего лучшего, как переопределить грид и снабдить его необходимыми функциями.

 

wxGridCtrl.h


#pragma once
#include <wx/wx.h>
#include <wx/grid.h>
#include <map>

class wxGridCtrl: public wxGrid {
std::map<int,int> m_mRowData;
DECLARE_DYNAMIC_CLASS(wxGridCtrl)
protected:
virtual void DrawRowLabel(wxDC& dc, int row);
virtual void OnLabelLeftClick(wxGridEvent& event);
virtual void OnSelectCell(wxGridEvent& event);

DECLARE_EVENT_TABLE();
public:
wxGridCtrl();
wxGridCtrl(wxWindow* parent, const long& id);
/// Override
bool CreateGrid(int numRows, int numCols,
wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
bool AppendRows(int numRows = 1, bool updateLabels = true);
bool InsertRows(int pos = 0, int numRows = 1, bool updateLabels = true);
bool DeleteRows(int pos = 0, int numRows = 1, bool updateLabels = true);
/// New
void SetRowData(const int& row, const int& data);
int GetRowData(const int& row);
};

Теперь, как это все реализовано

DrawRowLabel - это функция, реализующая перерисовку лэйбла грида, а именно, рисующая маркер в лэйбле.

OnLabelLeftClick - усталавливает визуальный курсор в первую колонку, выделенной строки. Это понадобилось для того, чтобы маркер не оставался на предыдущей строке, когда кликаем по следующей.

OnSelectCell - функция, без которой я не смог обойтись, чтобы все хорошо работало:)

Несколько слов о контейнере std::map<int,int> m_mRowData;.

Этот контейнер, имеющий ключем номер строки, и содержит значения, присваиваемые функцией SetRowData. И, чтобы данные соответствовали строками после создания грида, добаления, вставки и удаления записей, пришлось переопределить соответсвующие функции: CreateGrid, AppendRows, InsertRows, DeleteRows.

Получить данные для соответствующей строки, как и сами догадываетесь, можно функцией GetRowData.

 

wxGridCtrl.cpp


#include "wxGridCtrl.h"
#include "cursor.xpm"

IMPLEMENT_DYNAMIC_CLASS(wxGridCtrl, wxGrid);

wxGridCtrl::wxGridCtrl()
{
}

wxGridCtrl::wxGridCtrl(wxWindow* parent, const long& id):
wxGrid(parent,id,wxDefaultPosition,wxDefaultSize)
{
}

BEGIN_EVENT_TABLE(wxGridCtrl, wxGrid)
EVT_GRID_LABEL_LEFT_CLICK(wxGridCtrl::OnLabelLeftClick)
EVT_GRID_SELECT_CELL(wxGridCtrl::OnSelectCell)
END_EVENT_TABLE();

void wxGridCtrl::DrawRowLabel(wxDC& dc, int row)
{
if (GetRowHeight(row)<=0 || m_rowLabelWidth<=0)
return;
wxRect rect;
#ifdef __WXGTK20__
rect.SetX(1);
rect.SetY(GetRowTop(row)+1);
rect.SetWidth(m_rowLabelWidth-2);
rect.SetHeight(GetRowHeight(row)-2);
CalcScrolledPosition(0, rect.y, NULL, &rect.y);
wxWindowDC *win_dc=(wxWindowDC*)&dc;
wxRendererNative::Get().DrawHeaderButton(win_dc->m_owner, dc, rect, 0);
#else
int rowTop=GetRowTop(row),
rowBottom=GetRowBottom(row)-1;
dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID));
dc.DrawLine(m_rowLabelWidth - 1, rowTop, m_rowLabelWidth - 1, rowBottom);
dc.DrawLine(0, rowTop, 0, rowBottom);
dc.DrawLine(0, rowBottom, m_rowLabelWidth, rowBottom);
dc.SetPen(*wxWHITE_PEN);
dc.DrawLine(1, rowTop, 1, rowBottom);
dc.DrawLine(1, rowTop, m_rowLabelWidth - 1, rowTop);
#endif
if (row==GetGridCursorRow()) {
dc.DrawBitmap(wxBitmap(cursor_xpm),0,GetRowTop(row),true);
}
}

void wxGridCtrl::OnLabelLeftClick(wxGridEvent& event)
{
if (event.GetRow() != -1) {
SetGridCursor(event.GetRow(),0);
}
event.Skip();
}

void wxGridCtrl::OnSelectCell(wxGridEvent& event)
{
GetGridRowLabelWindow()->Refresh();
event.Skip();
}

/// Override
bool wxGridCtrl::CreateGrid(int numRows, int numCols,
wxGrid::wxGridSelectionModes selmode)
{
bool bCreate=wxGrid::CreateGrid(numRows,numCols,selmode);
if (bCreate) {
for (int i=0;i<numRows;i++) {
m_mRowData[i]=0;
}
}
return bCreate;
}

bool wxGridCtrl::AppendRows(int numRows, bool updateLabels)
{
int nRow=GetNumberRows();
bool bAdd=wxGrid::AppendRows(numRows,updateLabels);
if (bAdd) {
while (nRow<GetNumberRows()) {
m_mRowData[nRow]=0;
nRow++;
}
}
return bAdd;
}

bool wxGridCtrl::InsertRows(int pos, int numRows, bool updateLabels)
{
std::map<int,int> mRow;
for (int i=0;i<GetNumberRows();i++) {
mRow[i]=GetRowData(i);
}
bool bIns=wxGrid::InsertRows(pos,numRows,updateLabels);
if (bIns) {
for (int i=pos+numRows;i<GetNumberRows();i++) {
m_mRowData[i]=mRow[i-numRows];
}
for (int i=pos;i<pos+numRows;i++) {
m_mRowData[i]=0;
}
}
return bIns;
}

bool wxGridCtrl::DeleteRows(int pos, int numRows, bool updateLabels)
{
std::map<int,int> mRow;
for (int i=0;i<GetNumberRows();i++) {
mRow[i]=GetRowData(i);
}
bool bDel=wxGrid::DeleteRows(pos,numRows,updateLabels);
if (bDel) {
for (int i=pos;i<GetNumberRows();i++) {
m_mRowData[i]=mRow[i+numRows];
}
}
return bDel;
}

/// New
void wxGridCtrl::SetRowData(const int& row, const int& data)
{
if (row<GetNumberRows()) {
m_mRowData[row]=data;
}
}

int wxGridCtrl::GetRowData(const int& row)
{
int data=0;
if (row<GetNumberRows() && m_mRowData.find(row)!=m_mRowData.end()) {
data=m_mRowData[row];
}
return data;
}

Что касается цветов, это я не включил в класс. Мало ли как кому нужно будет?

Пример использования примерно следующий:

 

Exmple.cpp


#include "wxGridCtrl.h"
/* ... */
m_pGrid = new wxGridCtrl(this, wxID_ANY);
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);
m_pGrid->SetColLabelValue(0,wxT("A"));
m_pGrid->SetColLabelValue(1,wxT("B"));
m_pGrid->SetColLabelValue(2,wxT("C"));

wxGridCellAttr* pAttr = new wxGridCellAttr;
pAttr->SetBackgroundColour(wxColour(255,255,255));

wxGridCellAttr* pAttrBool = new wxGridCellAttr;
pAttrBool->SetBackgroundColour(wxColour(255,255,255));
pAttrBool->SetRenderer(new wxGridCellBoolRenderer());
pAttrBool->SetEditor(new wxGridCellBoolEditor());
pAttrBool->SetAlignment(wxALIGN_CENTRE,wxALIGN_CENTRE);

m_pGrid->SetColAttr(0,pAttr);
m_pGrid->SetColAttr(1,pAttr);
m_pGrid->SetColAttr(2,pAttrBool);
/* ... */

Автор: Александр (sandy) Илюшенко



Опубликовал admin
13 Июн, Суббота 2009г.



Программирование для чайников.