Описание работы с Virtual Grid Control в Visual C++

в 22:11, , рубрики: c++, Grid, visual c++, Visual Studio, отчеты, разработка, таблицы, метки: , , , ,

Из-за небольшого количества стандартных элементов управления в Visual C++ большинство компонентов приходится дописывать самостоятельно, особенно если вопрос касается отображения табличных данных. Для боле менее серьезных систем стандартного ListCtrl уже не хватает. Покопавшись в интернете можно найти немалое количество коммерческих grid’ов, но со своими кровными не очень хочется расставаться, тут на помощь и приходит замечательный компонент от Yuriy Zabroda VirtualGridCtrl.

Данный компонент был написан еще в Visual C++ 6.0, но и по сегодняшний день его можно успешно использовать в более свежих версиях Visual C++. На самом деле при выборе для себя грида я выдвигал несколько требований:

— многострочная шапка грида (без этого сложно представить хоть сколько-нибудь сложный отчет)
— отображение многострочного текста
— возможность подменить в строках таблицы стандартный CEdit на свои элементы управления
— возможность выделять цветом отдельные записи
— скорость работы

Пожалуй, начнем с последнего пункта. Слово Virtual в названии означает, что данные хранятся не в самом гриде, а в какой-то структуре данных, например векторе. А при отображении данных на экране из этой структуры запрашиваются только те данные, которые необходимо отобразить на экране, этим и обеспечивается независимость скорости работы от количества элементов в списке.

С остальными возможностями разберемся, используя небольшой пример.
Допустим, у нас есть табличный документ следующего вида:

— № Номер
— Дата
— Клиент: Телефон, Адрес

Строки содержат обычный текст.

Получить нам нужно примерно следующий вид

Описание работы с Virtual Grid Control в Visual C++

В состав VirtualGridCtrl входит конструктор внешнего вида грида, называется он VirtualGridDemo.exe. Ничего сложного в его использовании нет, на закладке, нажимаем по кнопке «Columns and Headers constructor» и на закладке «Columns» удаляем все колонки и добавляем, столько колонок, сколько нужно, в нашем случае 4. Затем на вкладке «Headers» сформируем структуру шапки нашего грида.

Описание работы с Virtual Grid Control в Visual C++

Получится следующее

Описание работы с Virtual Grid Control в Visual C++

В панели справа так же можно установить дополнительные параметры для строк и колонок.

Теперь разберемся, как все это теперь использовать. Опять открываем окно настроек по кнопке «Columns and Headers constructor» и переходим на вкладку «Code generation» и щелкаем «Generate».

Создайте новый проект Visual C++ (я использую Visual Studio 2008) со стилем Dialog и подключите к проекту заголовочные файлы: VirtualGridCtrl.h, MemDC.h, TitleTip.h, GridListBox.h, GridEdit.h, GridButton.h. Добавьте на форму «Custom control», установите у него в свойстве «Class» значение «CVirtualGridCtrl» и добавьте переменную m_grid с типом «CVirtualGridCtrl».

Теперь в OnInitialDialog можно добавить код инициализации грида. Скопируем туда полученный нами код в конструкторе.

Сразу скажу недостатки кода, который мы получили, не все настройки которые мы устанавливали в конструкторе, попали в этот код, это недоработка, но этот код не сложно добавить и самим. Итак, разберем код, который мы получили:

// --------------- Required variables -----------------
CGridColumn *pColumn;
CGridHeaderSections *pSections;
CGridHeaderSection *pSection;
CGridHeaderSection *pUpperSection;

// ----------------- Let's add some columns --------------
m_grid.AddColumn(_T(""), 60, LVCFMT_CENTER); // Добавляются колонки, третий параметр означает выравнивание в стоках
m_grid.AddColumn(_T(""), 92, LVCFMT_CENTER);
m_grid.AddColumn(_T(""), 81, LVCFMT_CENTER);
m_grid.AddColumn(_T(""), 68, LVCFMT_CENTER);

// --------------- Set additional column properties ----------------

pColumn = m_grid.GetColumn(0);
pColumn->SetTabStop(FALSE);

// Выстраиваем структуру шапки грида
// --------------- Let's put the grid header into shape ------------
pSections = m_grid.GetHeader()->GetSections();
pUpperSection = pSections->GetSection(0);

pUpperSection->SetCaption(_T("Номер"));
pUpperSection->SetAlignment(LVCFMT_CENTER);
pUpperSection = pSections->GetSection(1);

pUpperSection->SetCaption(_T("Дата"));
pUpperSection->SetAlignment(LVCFMT_CENTER);
pUpperSection = pSections->GetSection(2);
pUpperSection->SetCaption(_T("Клиент"));
pUpperSection->SetAlignment(LVCFMT_CENTER);
pUpperSection->SetWordWrap(TRUE); // Установим перенос текста в 3-й колонке

pSection = pUpperSection->Add();
pSection->SetCaption(_T("Телефон"));
pSection->SetAlignment(LVCFMT_CENTER);
pUpperSection = pSection;
pUpperSection = pSection->GetParent();

pSection = pUpperSection->Add();
pSection->SetCaption(_T("Адрес"));
pSection->SetAlignment(LVCFMT_CENTER);
pUpperSection = pSection;
pUpperSection = pSection->GetParent();
m_grid.GetHeader()->SynchronizeSections();

// -------------- Some additional initializations... ------

// Добавим код вручную

CGridHeader *pHeader = m_grid.GetHeader();
pHeader->SetSectionHeight(25); // Установим высоту шапки
pHeader->SynchronizeSections();
m_grid.SetFixedCount(1); // Можем сделать первую колонку фиксированной

m_grid.SetRowSelect(); // Выделять строки полностью
m_grid.SetRowHeight(25); // Установить высоту строк

Как установить дополнительные параметры можно посмотреть в примере, который идет с VirtualGrid.

Разберемся теперь как работать с данными. Создадим структуру:

struct sKlient
{
int nomer;
COleDateTime date;
CString tel;
CString adres;
};

И объявим переменную m_data следующим образом:

vector m_data;

И в OnInitialDialog инициализируем структуру:

// Инициализация нашей структуры данных
sKlient tmp;
tmp.nomer = 1;
tmp.date = COleDateTime(2012,1,1,0,0,0);
tmp.tel = L"12-34-56";
tmp.adres = L"Адрес 1";
m_data.push_back(tmp);

tmp.nomer = 2;
tmp.date = COleDateTime(2011,2,3,0,0,0);
tmp.tel = L"78-12-45";
tmp.adres = L"Адрес 2";
m_data.push_back(tmp);

m_grid.SetRowCount(m_data.size()); // Обязательно установим количество строк в гриде

Теперь добавим функцию вывода данных в грид, для этого в наш класс добавим определение функции:

afx_msg void OnGridGetDispInfo(NMHDR *pNMHDR, LRESULT *pResult);

в .cpp файл в BEGIN_MESSAGE_MAP добавим:

ON_NOTIFY(VGN_GETDISPINFO, IDC_CUSTOM1, OnGridGetDispInfo)

Обработчик события VGN_GETDISPINFO работает следующим образом:

VG_DISPINFO *pDispInfo = (VG_DISPINFO *)pNMHDR;

CString st;

if (pDispInfo->item.mask & LVIF_TEXT) // Так устанавливается текст в строках
{
switch (pDispInfo->item.nColumn)
{
case 0:
{// №
st.Format(L"%d", m_data[pDispInfo->item.nRow].nomer);
pDispInfo->item.strText = st;
}
break;
}
}

// А так можно установить цвет фона
if (pDispInfo->item.mask & LVIF_COLOR)
{
if (pDispInfo->item.nColumn == 1) // Установить фон во второй колонке
{
pDispInfo->item.pDC->SetBkColor(RGB(202, 228, 255));
}
}

Аналогично можно добавить функцию на обработку двойного щелчка мыши по строке:

ON_NOTIFY(WM_LBUTTONDBLCLK, IDC_CUSTOM1, OnNMDblclkGrid)

void CCVirtaGridTestDlg::OnNMDblclkGrid(NMHDR *pNMHDR, LRESULT *pResult)
{
int n;
n = m_grid.GetRow();
if (!m_grid.GetRowCount() || (n > m_grid.GetRowCount()-1))
return;

::AfxMessageBox(m_data[n].adres);
}

Или обработчики любых других событий.

Данный грид довольно универсальный и его можно применять в различных приложениях для отображения списков в красивом и наглядном виде. При необходимости его несложно доработать, т. к. исходники имеют четкую и понятную структуру.

P. S.: В приложении грид в стандартном своем виде, и пример из статьи с немного доработанным гридом (исправлены небольшие ошибки, добавлены градиенты при отрисовке строк и заголовков), так что исодники лучше брать из него.

Автор: DenisT


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js