Під час вибірки великої кількості записів можна отримати помилку нестачі пам’яті, хоча сама cxGrid легко обробляє в кілька разів більше записів навіть у 32-бітній версії. Достатньо замінити стандартний менеджер пам’яті Delphi на Fast Memory Manager. У моєму випадку FastMM4 чудово допоміг розширити можливості, збільшивши граничну вибірку вп’ятеро.
Деякі прийоми роботи з компонентом cxGrid, зібрані з різних джерел і корисні особисто мені. (Оновлено 06.10.2026)
Зовнішній вигляд
Програмно забарвлювати комірки можна в подіях OnCustomDrawCell.
if (AViewInfo.GridRecord.Values[TableViewColumn1.Index])=1 then begin ACanvas.Brush.Color := clRed; ACanvas.Font.Style := [fsBold]; end;
Якщо потрібно забарвити лише окремі стовпці (а не весь рядок), потрібний стовпець в обробнику OnCustomDrawCell можна визначити так:
if TableView.Columns[AViewInfo.Item.Index].DataBinding.FieldName='COLUMN_1' then або if AViewInfo.Item = TableViewCOLUMN_1 then
Слід враховувати, що таке забарвлення не переноситься під час експорту в Excel, а колір виділення іноді вибирається невдало. Для цього потрібно забарвлювати комірки, виключно присвоюючи заздалегідь визначений «стиль» в обробниках типу TableViewStylesGet***Style:
procedure TMoneyMoveDetailForm.TableViewStylesGetContentStyle(Sender: TcxCustomGridTableView; ARecord: TcxCustomGridRecord; AItem: TcxCustomGridTableItem; out AStyle: TcxStyle); begin if not ARecord.IsData then Exit; // (AItem as TcxGridDBBandedColumn) якщо потрібно визначити, у якому стовпці ми перебуваємо if (ARecord.Values[TableViewID_MONEY.Index])=null then begin AStyle :=cxStyleBold; end; end;
Виведення зображення або піктограми в комірці
procedure TMsgForm.TableViewIMAGECustomDrawCell(Sender: TcxCustomGridTableView; ACanvas: TcxCanvas; AViewInfo: TcxGridTableDataCellViewInfo; var ADone: Boolean); Var r : TRect; iImageIndex : Integer; begin If (AViewInfo.GridRecord.Values[TableViewIMAGE.Index] = 1) then begin R := AViewInfo.Bounds; ACanvas.Brush.Color := AViewInfo.Params.Color; ACanvas.FillRect(R); // малюємо зображення. R := AViewInfo.Bounds; Inc(r.Top,1); ACanvas.DrawImage(ImageList,r.Left,r.Top,20,True); ADone := True ; end; end;
Але можна зробити й так:
procedure TFilesForm.TableViewIconCustomDrawCell(
Sender: TcxCustomGridTableView; ACanvas: TcxCanvas;
AViewInfo: TcxGridTableDataCellViewInfo; var ADone: Boolean);
begin
inherited;
with AViewInfo.ClientBounds do
cxImageList.Draw(ACanvas.Canvas, Left + 1, Top + 1, 0);
ADone := True;
end;
Є ще варіант: установити в Properties комірки компонент ButtonEdit, у якому можна додати одну чи більше кнопок. Якщо призначити кнопці Action, вона реагуватиме на натискання. Щоправда, лише за умови ввімкненого редагування. Недолік способу — комірка містить лише текст.
Забарвлення різних рівнів групування різними кольорами
procedure TPozSkladForm.TableViewStylesGetGroupStyle( Sender: TcxGridTableView; ARecord: TcxCustomGridRecord; ALevel: Integer; out AStyle: TcxStyle); begin if ALevel=0 then AStyle := cxStyleL0; // $00A0A0A0 if ALevel=1 then AStyle := cxStyleL1; // clSilver if ALevel=2 then AStyle := cxStyleL2; // $00E0E0E0 end;
Відчутний недолік забарвлення через стилі (Style) полягає в тому, що курсор перекриває його й кольору не видно. Це можна обійти, якщо в обробнику TableViewCustomDrawCell, наприклад, самостійно виділяти рядок (для режиму CellSelect=True):
if AViewInfo.GridRecord.Selected then begin
if AViewInfo.Selected then begin // під інверсним курсором зробимо білий шрифт
ACanvas.Font.Color := clWhite;
end else begin // решту рядка виділимо синім шрифтом, щоб не змінювати фонове забарвлення комірок
ACanvas.Font.Color := clBlue;
ACanvas.Font.Style := [fsBold];
end;
end;
Зверніть увагу! Під час доступу до даних рядка через GridRecord та індекс стовпця можна не отримати очікуваних значень у разі групування рядків, коли відображуваний рядок виявиться заголовком групи.
Відображення даних (фільтри, групування, сортування)
У TcxGrid приховати групування GroupByBox і прибрати область над грідом із написом “drag a column header here to group by ..”:
TableView.OptionsView.GroupByBox := false;
Програмно задати фільтр
TableView.DataController.Filter.BeginUpdate; TableView.DataController.Filter.Root.Clear; TableView.DataController.Filter.Root.AddItem(TableViewColumn1, cxFilter.foLike, BegString+"%", BegString+"%"); TableView.DataController.Filter.Active:=true; TableView.DataController.Filter.EndUpdate
Якщо додається більше однієї умови, перед додаванням наступної бажано вказати логічний оператор (типово буде AND). Можна задати OR: TableView.DataController.Filter.Root.BoolOperatorKind := fboOr;
Збереження позиції курсора в гріді після оновлення
TableView.BeginUpdate(); r := TableView.Controller.TopRowIndex; f := TableView.Controller.FocusedRowIndex; GridQuery.Close; // тут відкриваємо й закриваємо грід GridQuery.Open; TableView.Controller.TopRowIndex := r; TableView.Controller.FocusedRowIndex := f; TableView.EndUpdate;
Програмно розгорнути або згорнути групи
TableView.DataController.Groups.FullCollapse; // Згорнути TableView.DataController.Groups.FullExpand; // Розгорнути TableView.DataController.Options := TableView.DataController.Options + [dcoGroupsAlwaysExpanded]; // Закріпити розгорнутий стан
Програмно задати або прибрати групування
TableViewColumn1.GroupIndex := 1; // задати групування за стовпцем TableView.DataController.Groups.ClearGrouping; // Прибрати групування
Доступ до даних і метаданих гріда
Пройти в циклі всі видимі рядки:
for I:=0 to cxGrid.DataController.FilteredRecordCount - 1 do begin cxGrid.DataController.Values[cxGrid.DataController.FilteredRecordIndex[i],YourColumnName.Index])
Цикл за всіма виділеними рядками з отриманням ідентифікатора кожного
for i := 0 to GridView1.Controller.SelectedRecordCount-1 do begin ID_RASHODD := GridView1.DataController.Values[GridView1.Controller.SelectedRecords[i].RecordIndex, GridView1ID_RASHODD.Index]; end;
Отримати стовпець гріда за його назвою
TableView1.GetColumnByFieldName('FIELD_NAME')
Для таблиць типу TcxGridTableView або TcxGridBandedTableView (без “DB”) дані можна додавати й видаляти вручну. Наприклад, так:
// Видалення всіх рядків із таблиці while TableView.DataController.RowCount>0 do TableView.DataController.DeleteRecord(0); // Додавання рядка даних і заповнення комірок значеннями відповідно до їхнього типу r := TableView.DataController.AppendRecord; TableView.DataController.SetValue(r, TableViewColumn1.Index, 'test1'); TableView.DataController.SetValue(r, TableViewColumn2.Index, 'test2'); TableView.DataController.SetValue(r, TableViewColumn3.Index, 12345);
Редагування даних користувачем
Організація списку ComboBox у комірці:
У Properties задаємо ComboBox, заповнюємо список у подіях AfterScroll набору даних. Якщо намагатися робити це в onInitPopUp, відкривається попередній список (у коментарях підказали, що я робив неправильно). Приклад заповнення:
TcxComboBoxProperties(TableViewCNAME.Properties).Items.Clear; while not LookupQuery.Eof do begin TcxComboBoxProperties(TableViewCNAME.Properties).Items.Add(LookupQuery['CNAME']); LookupQuery.Next; end;
Вибір користувача перевіряється й використовується в методі OnValidate приблизно так:
if LookupQuery.Locate('CNAME',DisplayValue,[]) then begin
// тут записуємо в базу
TableView.BeginUpdate;
// тут оновлюємо таблицю за потреби
TableView.EndUpdate;
end else begin
ErrorText := 'Помилка вибору';
Error := True;
end;
TcxComboBoxProperties(TableViewCNAME.Properties).Items.Clear;
Редагування числа в гріді:
У Properties задаємо CalcEdit, а в обробнику OnEditValueChanged використовуємо (Sender as TcxCalcEdit).Value і поточне положення в наборі даних.
Перехопити натискання правої кнопки миші на певному стовпці:
У події OnCellClick:
if ACellViewInfo.Item.Name='DzTableViewNRM_NAME' then if AButton=mbRight then
Зробити функцію копіювання комірки в буфер обміну
Отримати значення поточної комірки можна так: := <TcxGridDBTableView>.Controller.FocusedRow.Values[FocusedColumn.Index]. Але якщо в таблиці є стовпці з LookupComboBox, замість видимого значення ви отримаєте ключ. Тому функція отримання значення має це враховувати й використовувати .GetDisplayLookupText. Цей метод прихований, тож для доступу потрібно у своїх оголошеннях успадкувати клас TcxLookupComboBoxProperties:
type
TcxLookupComboBoxPropertiesAccess = class(TcxLookupComboBoxProperties); // для доступу до .GetDisplayLookupText
// далі в коді
with <TcxGridDBTableView>.Controller do begin
if FocusedColumn.Properties is TcxLookupComboboxProperties then begin
Clipboard.AsText := (TcxLookupComboBoxPropertiesAccess(FocusedColumn.Properties).GetDisplayLookupText(FocusedRow.Values[FocusedColumn.Index]));
end else begin
Clipboard.AsText := FocusedRow.Values[FocusedColumn.Index];
end;
end;
Визначити положення курсора за подією MouseUp/MouseDown:
procedure TPlanForm.TableView1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
AHitTest: TcxCustomGridHitTest;
begin
inherited;
AHitTest := (Sender as TcxGridSite).GridView.ViewInfo.GetHitTest(X,Y); // Може бути одним із // TcxGridColumnHeaderHitTest, TcxGridRecordCellHitTest, TcxGridGroupByBoxHitTest
if AHitTest is TcxGridColumnHeaderHitTest then begin
if TcxGridColumnHeaderHitTest(AHitTest).Column = TableView1COLUMN1 then begin
// Визначили, що натиснули заголовок першого стовпця (але задавати тут сортування — погана ідея)
end;
end;
end;
Обробка позначки прапорця в комірці. У події OnChange:
if (Sender as TcxCheckBox).Checked then
Дізнатися, у якому стовпці відбувається редагування:
TableView.VisibleColumns[TableView.Controller.FocusedColumnIndex].DataBinding.FieldName
Зміна сусідніх комірок під час обробки редагування в OnEditValueChanged:
TableView.DataController.<strong>SetEditValue</strong>(TableViewFIELD_NAME.Index, new_Value, evsValue );
Слід сказати, що цей спосіб чомусь не завжди працює, але можна й інакше:
TableView1.DataController.DataSource.DataSet.Edit;
TableView1.DataController.DataSource.DataSet.FieldByName('FIELD_NAME').Value := new_Value;
TableView1.DataController.DataSource.DataSet.Post;
Використання drag&drop для перетягування на рядки таблиці
procedure TForm1.cxGrid2DBTableView1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
// Початок перетягування з таблиці-джерела (утім, можна просто ввімкнути Auto)
if ((Button = mbLeft) and (ssAlt in Shift)) or (ssRight in Shift) then
if (TcxGridSite(Sender).ViewInfo.GetHitTest(X, Y).HitTestCode in [htCell, htRecord])
then TcxGridSite(Sender).BeginDrag(False);
end;
var
ARecordIndex: Integer;
procedure TForm1.cxGrid2DBTableView1StartDrag(Sender: TObject; var DragObject: TDragObject);
var
AGridView: TcxGridDBTableView;
begin
// Отримуємо ARecordIndex запису, який перетягуємо
AGridView := TcxGridSite(Sender).GridView as TcxGridDBTableView;
ARecordIndex := AGridView.Controller.FocusedRecordIndex;
end;
procedure TForm1.cxGrid2DBTableView1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
// Дозвіл приймання
Accept := Source is TcxDragControlObject;
end;
procedure TForm1.cxGrid2DBTableView1DragDrop(Sender, Source: TObject; X, Y: Integer);
begin
// Виведемо текст із рядка, на якому виконали Drop
self.Caption := TcxGridRecordCellHitTest(TcxGridSite(Sender).ViewInfo.GetHitTest(X, Y)).GridRecord.Values[cxGrid1DBTableView1FULLNAME.Index];
end;
Зміна таблиці під час виконання
Додавання стовпця під час виконання:
var XCol : TcxGridDBBandedColumn; begin XCol := TableView.CreateColumn; XCol.Position.BandIndex := 1; XCol.DataBinding.FieldName:='quant'+sl[i]; XCol.DataBinding.ValueType := 'Float'; XCol.Caption := ss[i]; XCol.Width := 50; XCol.Tag := StrToInt(sl[i]); XCol.Summary.FooterKind := skSum; XCol.Summary.GroupFooterKind := skSum; XCol.Summary.GroupKind := skSum; XCol.Summary.FooterFormat := '0.##'; XCol.Summary.GroupFooterFormat := '0.##'; XCol.Summary.GroupFormat := '0.##';
Видалення стовпця під час виконання:
TableView.Columns[i].Destroy;
Додавання смуг (bands):
b0 := TableView.Bands.Add; b0.Position.BandIndex := b.Index;
Задати довільну формулу обчислення FooterSummary
Задати довільну формулу обчислення FooterSummary комірки з використанням інших комірок FooterSummary можна в події DataControllerSummaryAfterSummary. У події OnGetText самої комірки це не вдасться, оскільки на момент її виклику Summary ще не обчислено.
procedure T*****Form.TableViewDataControllerSummaryAfterSummary(ASender: TcxDataSummary); var itog_summa, kmsumma :Variant; begin inherited; itog_summa := ASender.FooterSummaryValues[ASender.FooterSummaryItems.IndexOfItemLink(TableViewITOG_SUMMA)]; kmsumma := ASender.FooterSummaryValues[ASender.FooterSummaryItems.IndexOfItemLink(TableViewKMSUMMA)]; ASender.FooterSummaryValues[ASender.FooterSummaryItems.IndexOfItemLink(TableViewPERCENT)] := itog_summa + kmsumma; end;
Щодо аналогічного обчислення для GroupSummary, тут доведеться рекурсивно пройти всі Level і на кожному обчислити потрібну формулу для всіх GroupSummary. Приклад реалізації.
Збої
Так, у такого складного компонента вони неминучі, й іноді з ними можна зіткнутися. Наприклад, у деяких випадках під час підсумовування у Footer у мене губилися старші розряди: замість 14.8 відображалося 4.8, незалежно від заданого формату. Тоді довелося написати власне обчислення суми й викликати його в події OnGetText для FooterSummary:
procedure T****Form.TableViewTcxGridDBDataControllerTcxDataSummaryFooterSummaryItems3GetText(
Sender: TcxDataSummaryItem; const AValue: Variant; AIsFooter: Boolean;
var AText: String);
var
s : Double;
i : Integer;
begin
s := 0;
for i:=0 to TableView.DataController.FilteredRecordCount - 1 do begin
try
s := s + TableView.DataController.GetValue(TableView.DataController.FilteredRecordIndex[i], Sender.Field.Index);
except
end
end;
AText := FormatFloat(Sender.Format, s);
end;
Так я обходив помилку раніше, але у 2026 році знайшов її: це виявилася помилка стандартної бібліотеки RTL Delphi 7! Докладніше про це тут.
TcxLookupComboBox
Показати підказку в самому полі введення (.TextHint). Виявляється, компонент має цю властивість, але вона прихована. Щоб відкрити до неї доступ, потрібно в оголошеннях успадкувати клас:
type TcxLookupComboBoxAccess = class(TcxLookupComboBox); // для доступу до прихованого поля .TextHint // А вже, наприклад, в OnCreate присвоїти підказку: TcxLookupComboBoxAccess(MyLookupCoboBox).TextHint := 'Текст підказки';
Додати власну додаткову кнопку в поле введення, наприклад для відкриття довідника або редагування. Це також працюватиме для всіх елементів керування, які мають Properties (список елементів керування).
with MycxLookupComboBox.Properties do begin
Images := cxImageList;
Result := Buttons.Add;
Result.Default := True;
Result.Kind := bkGlyph;
Result.LeftAlignment := False;
Result.Action := Action; // Це дія, яка виконуватиметься
end;

Добрый день!
Не подскажите, как можно сохранить настройки и расположение колонок в реестре или ini? В dxMasterView это было из коробки, а вот в cxGrid я не нашел такого способа. Смысл, чтобы сохранить стили, размеры и порядок колонок в cxGridTableView.
Юзайте TcxPropertiesStore, но в гридах вьюшку надо заменить на Banded , т.е. вместо например TcxDBGridTableView , надо заменить на TCxDbBandedTableView. Это надо что бы было доступно свойство position (т.е. расположение столбца в гриде)
Отличная статья! У меня задача: заменить значения чисел на текстовые значения. Спасибо.
Спасибо вам за статью.
Но есть замечание относительно пункта “Редактирование данных пользователем”, подпункт “Организация списка ComboBox в ячейке”. На самом деле в OnInitPopup тоже можно сделать выпадающий список и он будет актуальным, нужно просто обращаться не к столбцу, а к Sender’-у. Что-то типа этого:
procedure TrdbXXX.YYYPropertiesInitPopup( Sender: TObject); begin if XQuery.Active then begin (Sender as TcxComboBox).Properties.Items.Clear; XQuery.First; while notXQuery.Eof do begin (Sender as TcxComboBox).Properties.Items.Add(XQuery['FieldName']); XQuery.Next; end; end; end;a7in: Спасибо за замечание!