Переопределение поведения стандартных компонентов. Делаем свой wxGrid

Александр (sandy) Илюшенко любезно предоставил статью о том, как настроить класc 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 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);
/* ... */
T-Rex

Share
Published by
T-Rex

Recent Posts

Разработка кроссплатформенных модульных приложений на C++ с библиотекой wxWidgets

Введение Уже долгое время не пишу статьи о разработке, хотя сам процесс написания мне очень…

11 years ago

wxWidgets App With Plugins (Windows/Linux/Mac) – Sample Source Code

I can see that there is still a lot of topics at wxWidgets forums related…

11 years ago

wxToolBox is Now Open-Source!

I've just published the source code of wxToolBox component and a couple of sample apps at…

11 years ago

Microsoft Kinect Helper Library and Sample for wxWidgets

Microsoft released their Kinect SDK several days ago. So, for those wxWidgets developers who are…

13 years ago

wxJSON 1.1.0 Released

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to…

15 years ago

wxRuby. Оно даже работает!

Вдохновленнный читаемой нынче книгой My Job Went to India: 52 Ways to Save Your Job…

15 years ago