Difference between revisions of "Grids Reference Page/ru"

From Free Pascal wiki
Jump to navigationJump to search
Line 438: Line 438:
 
*'''goRowSelect''' выбирает [для выделения] полную строку вместо отдельных ячеек
 
*'''goRowSelect''' выбирает [для выделения] полную строку вместо отдельных ячеек
 
*'''goFixedRowNumbering''' если [свойство] установлено, сетка будет нумеровать строки в первом фиксированном столбце
 
*'''goFixedRowNumbering''' если [свойство] установлено, сетка будет нумеровать строки в первом фиксированном столбце
*'''goHeaderHotTracking''' если [свойство] установлено, сетка попытается показать другой курсор мыши, когда он будет находиться над любой фиксированной ячейкой. Для того, чтобы это сработало, желаемая зона ячейки должна быть включена с помощью свойства HeaderHotZones. Попробуйте комбинировать этот параметр с свойством TitleStyle:=tsNative, чтобы получить подсвеченный курсора мыши [соответственно] теме [виджета].
+
*'''goHeaderHotTracking''' если [свойство] установлено, сетка попытается показать другой курсор мыши, когда он будет находиться над любой фиксированной ячейкой. Для того, чтобы это сработало, желаемая зона ячейки должна быть включена с помощью свойства HeaderHotZones. Попробуйте комбинировать этот параметр с свойством TitleStyle:=tsNative, чтобы получить подсвеченный курсор мыши [соответственно] теме [виджета].
 
*'''goHeaderPushedLook''' если этот параметр установлен, этот элемент включает [эффект] "нажатия" при клике [мышью] любой фиксированной ячейки. Зона "нажатия" ячеек становится доступной при использовании свойства HeaderPusedZones.
 
*'''goHeaderPushedLook''' если этот параметр установлен, этот элемент включает [эффект] "нажатия" при клике [мышью] любой фиксированной ячейки. Зона "нажатия" ячеек становится доступной при использовании свойства HeaderPusedZones.
  

Revision as of 18:24, 1 October 2018

Deutsch (de) English (en) español (es) polski (pl) русский (ru)



ENG: AT THE MOMENT THIS PAGE IS UNDER TRANSLATION.
RUS: В НАСТОЯЩИЙ МОМЕНТ СТРАНИЦА НАХОДИТСЯ В ПРОЦЕССЕ ПЕРЕВОДА.




-- Zoltanleo 23:47, 26 September 2018 (CEST): Ввиду сложности дословного перевода текста с английского на русский слова, требующиеся по смыслу, но отсутствующие в английской версии, указаны в квадратных скобках.



Назначение

Этот текст попытается показать пользователю некоторые аспекты использования компонентов сеток (Grid) в Lazarus. Он также предназначен для использования в качестве руководства для пользователей, которые никогда не использовали сетки (опытные пользователи тут обычно получают только информацию по новой функциональности).

Таким образом, этот текст пытается достичь следующих целей:

  1. Введение в сеточные компоненты для людей с небольшим опытом или без опыта после работы с Delphi.
  2. Документирование различий в использовании компонентов сеток в Delphi.
  3. Документирование новой функциональности сеток в Lazarus
  4. Описание по созданию и примеры использования таких компонентов в Lazarus

Введение

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

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

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

Например, есть сетка предназначенная для отображения текста: StringGrid. Документация для этой сетки может быть найдена здесь

Дерево наследования

                      [TCustomControl]           
                              |                    
                              |                    
                         TCustomGrid               
                              |                    
                +-------------+------------+       
                |                          |       
          TCustomDrawGrid             TCustomDbGrid
                |                          |       
       +--------+--------+                 |       
       |                 |              TDbGrid    
   TDrawGrid      TCustomStringGrid                
                         |                         
                         |                         
                    TStringGrid                    

Начальный пример

Поскольку одной из целей этой страницы является помощь людям с небольшими или отсутствующими предшествующими знаниями Lazarus'а, давайте сделаем быстрый стартовый пример сетки в действии. Почему бы и нет, давайте сделаем традиционный пример "hello world", используя компонент TStringGrid.

  1. Создайте новое приложение.
    • В главном меню выберите: project->New Project
    • В диалоговом окне Create New Project нажмите кнопку "Create"
    • Будет показана новая пустая форма..
  2. Поместите сетку в форму
    • В палитре компонентов выберите вкладку "additional"
    • Нажмите значок TStringGrid []
    • Нажмите на форму рядом с левым верхним углом. Появится новая пустая сетка.
  3. Поместите кнопку в форму
    • с помощью палитры компонентов выберите вкладку "Standard"
    • Нажмите значок TButton []
    • Нажмите на пустую область формы. Появится новая кнопка.
  4. Дважды нажмите кнопку из шага 3 и запишите следующий код в обработчике щелчка кнопки:
    • Stringgrid1.Cells[1,1] := 'Привет, Мир!';
  5. Запустите программу, щелкнув значок воспроизведения []
    • по нажатию кнопки button1, текст приветствия будет отображаться в столбце 1 ячейки 1 (обратите внимание, что левый столбец и верхняя строка равны 0)

Различия между сетками Lazarus и Delphi

Текущий компонент сетки отличается от сетки Delphi по нескольким направления. При начальной разработке сетки Lazarus создавались с нуля без какой-либо попытки сделать их полностью совместимыми с Delphi.

На более позднем этапе совместимость с сетками Delphi стала желаемой целью, и сетки Lazarus стали более тесно соответствовать интерфейсу сетки Delphi. Однако даже это было сделано без попытки сделать каждое свойство или метод сетки Lazarus сопоставимым с его аналогом в Delphi. Кроме того (поскольку внутренняя структура сетки Lazarus сильно отличается от внутренних элементов сетки Delphi), некоторая функциональность Delphi невозможна или должна быть эмулирована иначе в сетке Lazarus [в отличие от того], как это будет сделано в сетке Delphi. Мы достигли большей совместимости с Delphi в процессе эволюции сетки Lazarus, и это соблазнительно.

Различия

Известные различия перечислены ниже, ни в каком специальном порядке.

  • Редакторы ячеек
  • Поведение в designtime
  • Отрисовка ячеек имеет некоторые отличия, см. раздел настройки сетки.

Новая функциональность

  • Пользовательские столбцы
  • События
  • Редактор сетки

Способы, которыми вы можете сделать сетку Lazarus более совместимой с Delphi

Вы можете сделать сетку Lazarus похожей и ведущей себя подобно [сетке] Delphi. Ниже перечислены параметры свойств, которые [позволят] достигнуть этого. Эти корректировки основаны на вновь созданной сетке. [Свойства,] помеченные тэгом [Code], должны быть определены в коде, тэгом [Design] могут быть изменены во время разработки.

  • [Design] TitleStyle := tsStandard;
  • [Design] DefaultRowHeight := 24;
  • [Code] EditorBorderStyle := bsNone; // это может работать только в Windows.
  • [Code] UseXORFeatures := true;
  • [Code] AllowOutBoundEvents := False; {SVN-ревизия 10992 или старше}
  • [Code] FastEditing := False; (поддерживается в dbgrid. StringGrid требует SVN-ревизию 10992 или старше)
  • [Design] AutoAdvance := aaNone;

Справочник по сеткам

Информация

Отправной точкой для [получения] справки о TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid и TStringGrid является справка по модулю Grids.pas

Для TCustomDBGrid и TDBgrid это справка по модулю DBGrids.pas

В общем, любая ссылка Delphi о сетках должна помочь нам использовать сетки Lazarus (но не забывайте, что есть несколько различий между сетками Delphi и Lazarus, которые мы задокументировали); имея это в виду и в качестве временного места для справочной информации, это место будет использоваться для документирования тех вещей, которые не работают аналогично Delphi, а также любых новых функций.

TODO: остальная часть этого раздела должна исчезнуть; его содержание будет перенесено в справку по модулю Grids.pas

TCustomGrid

См. полную справку TCustomGrid

свойство AllowOutboundEvents

Protected[-свойство] в TCustomGrid, public[-свойство] в TCustomDrawGrid и его наследниках. Обычно, когда пользователь нажимает в точку над пустым пространством после ячеек (например, если сетка имеет три строки, но пользователь нажимает на воображаемую четвертую строку), текущий фокус ячейки перемещается ячейку, ближайшую к только что щелкнутой точке. Мы называем это исходящим событием. Значение по умолчанию - true, так как это изначальное поведение сетки. Это свойство было добавлено для моделирования поведения Delphi, когда исходящие события недоступны, поэтому для обеспечения совместимости с Delphi это свойство имеет значение false.

свойство Columns

Lazarus включает свойства columns в сетки TStringGrid и TDrawGrid. Это свойство добавляет так называемые пользовательские столбцы. Пользовательские столбцы представляют собой набор объектов, которые содержат свойства, применяемые к целым столбцам, например заголовки столбцов (для StringGrid он переопределяет значение, указанное в соответствующем свойстве Cells [ColumnIndex, RowTitle]), выравнивание текста, цвет фона, предпочтительный редактор и т.д.

Пользовательские столбцы добавляют дополнительные свойства или заменяют значения свойств по умолчанию в столбцах стандартной сетки. Но не только это; значение grid.ColCount может увеличиваться или уменьшаться, чтобы учесть количество настраиваемых столбцов, которые привязаны к сетке. На этом этапе это означает, что grid.ColCount = grid.FixedCols + grid.Columns.Count.

Например, если к базовой сетке с ColCount=5 и FixedCols=2 мы:

  • Добавим 3 пользовательских столбца, базовая сетка будет точно такой же, как раньше, 2 фиксированных столбца и 3 обычных столбца.
  • Добавим 1 пользовательский столбец, базовая сетка будет иметь ColCount=3, то есть 2 фиксированных столбца и 1 нормальный столбец.
  • Добавим 4 пользовательских столбца, базовая сетка будет иметь ColCount=6, то есть 2 фиксированных столбца и 4 нормальных столбца.

Из этого можно сделать вывод, что:

  • Свойства фиксированного столбца или счетчики не увеличиваются или не изменяются соответственно пользовательскими столбцами.
  • grid.ColCount обычно отличается от grid.Columns.Count (grid.ColCount=grid.Columns.Count, только когда FixedCols=0)

Во время разработки пользователь может получить доступ к свойству columns в Object Inspector, чтобы вызвать редактор столбцов. Оттуда вы можете добавлять, удалять или изменять настраиваемые столбцы. Редактор отображает список текущих пользовательских столбцов; путем выбора элементов в этом списке инспектор объектов заполняется свойствами, доступными для каждого столбца. Список настраиваемых столбцов также доступен в представлении дерева компонентов Object Inspector, где столбцы можно добавлять, удалять или изменять. Они появляются на нижнем уровне под сеткой контейнера.

В runtime столбцы могут быть изменены с помощью кода:

  var
    c: TGridColumn;
  begin
    // Добавляем пользовательский столбец в сетку
    c := Grid.Columns.Add;
    // модифицируем
    c.title.caption := 'Цена';        // Устанавливаем заголовок столбца
    c.align := taRightJustify;        // Выравниваем содержимое столбца по правому краю
    c.color := clMoneyGreen;          // Изменяем цвет по умолчанию на clMoneyGreen
    c.Index := 0;                     // Делаем этот столбец первым
    // доступ к существующему столбцу
    grid.columns[0].Width := 60;      // Изменяем ширину столбца 0 на 60 пикселей
    // удаляем существующий столбец
    grid.columns.delete(0);           // Удаляем столбец 0
    ....
  end;

Кроме того, когда задействуются пользовательские столбцы, сетки не допускают прямой модификации [свойства] grids.colcount; добавление или удаление столбцов должно выполняться с использованием свойства columns. Объяснение состоит в том, что существует некоторая несогласованность в постепенном удалении пользовательских столбцов с использованием ColCount: когда [значение] ColCount становится равным FixedCols, [это означает, что] сетка не имеет больше настраиваемых столбцов. Если теперь увеличить ColCount, новый столбец будет не настраиваемым, а нормальным столбцом.

В настоящее время нет планов, чтобы сетки использовали только настраиваемые столбцы.

TCustomDBGrid

TCustomDBGrid основан на TDBGrid.

Они не отображают свойств Col и Row. Чтобы перейти к определенному столбцу, используйте, например, свойство SelectedIndex.

Интересным public-методом является AutoSizeColumns.

процедура AutoSizeColumns

Эта процедура устанавливает ширину столбца по размеру максимально широкого найденного текста. Его можно использовать после загрузки набора данных/установки [свойства] Active.

Однако, в противоположность [процедуре] TCustomStringGrid.AutoSizecolumns (см. ниже), эта [процедура] будет устанавливать столбцы максимально широко, пока вы не задействуете [параметр грида] dgAutoSizeColumns.

процедура InplaceEditor

См. пример из bug 23103 - и вставьте объяснение того, что он делает/почему это необходимо. Проверить входные значения? Изменить то, что показано?

procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: char);
var
  S: String;
begin
  if (Key in [',','.']) then
  begin
    //в отличие от Delphi не все InPlaceEditor'ы - это редакторы для строкового типа, поэтому проверьте!
    if (DBGrid1.InplaceEditor is TStringCellEditor) then
    begin
      S := TStringCellEditor(DBGrid1.InplaceEditor).EditText;
      if Pos(',',S) > 0 then
        Key := #0
      else
        Key := ',';
    end;
  end;
end;

TCustomStringGrid

TCustomStringGrid служит основой для TStringGrid. Он может использоваться для [написания] производных компонентов [типа] TStringGrid, которые хотят скрыть published-свойства. Дополнительную информацию см. в new intermediate grids.

Следующие public-свойства или public-методы являются доступными для TStringGrid.

См. полную справку TCustomStringGrid

процедура AutoSizeColumn(aCol: Integer);

Эта процедура устанавливает ширину столбца [в соответствии с] размером самого широкого текст, который был ею найден во всех строках столбца aCol. Совет: см. параметр goDblClickAutoSize, чтобы позволить автоматически изменять размер столбцов при двойном нажатии на границу столбца.

процедура AutoSizeColumns;

Автоматически изменяет размеры всех столбцов так, чтобы они растягивались под самый длинный текст для каждого столбца. Это быстрый способ применения AutoSizeColumn() для каждого столбца в сетке.

процедура Clean; overload;

Очищает все ячейки в сетке, [будь то] фиксированные или нет.

процедура Clean(CleanOptions: TGridZoneSet); overload;

Очищает все ячейки в сетке в соответствии с данными CleanOptions. Дополнительную информацию см. в TGridZoneSet. Некоторые примеры:

  • Очистить все ячейки: grid.Clean([]); (аналогично grid.clean)
  • Очистить все нефиксированные ячейки: grid.Clean([gzNormal]);
  • Очистить все ячейки, но не касается заголовков столбцов сетки: Grid.Clean([gzNormal, gzFixedRows]);

процедура Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TGridZoneSet); overload;

делает то же самое, что и Clean(CleanOptions: TGridZoneSet), но ограничивается данными StartCol, StartRow, EndCol и EndRow. Примеры:

  • Очистить индекс столбца с 4 по 6, но не касаться заголовков столбцов сетки, много вариантов:
Grid.Clean (4,Grid.FixedRows, 6,Grid.RowCount-1,[]); //или Grid.Clean(4,0,6,Grid.RowCount-1, [gzNormal]);

и т.п.

процедура Clean(aRect: TRect; CleanOptions: TGridZoneSet); overload;

То же самое, что и Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions), просто берется [прямоугольник] TRect вместо отдельных координат ячейки. Для очистки выделения полезно [сделать]:

grid.Clean(Grid.Selection,[]);

процедура SaveToCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true);

Сохраняет содержимое сетки в файл формата данных с разделителями-запятыми (CSV) (добавлен в Lazarus с r32179).

Аргумент AFilename указывает имя файла, в котором содержимое будет сохранено. Если файл существует, содержимое будет перезаписано. Если он не существует, файл будет создан.

ADelimiter (необязательный аргумент) используется для предоставления настраиваемого разделителя, если требуется. По умолчанию создается формат CSV (то есть ADelimiter: = ',') для TAB-разделяемого файла ADelimiter должен быть #9.

Параметр WithHeader используется для определения необходимости включения или отключения "row header"[(заголовка строки)]. Заголовок строки - это список имен полей в начале выходного файла; его содержимое извлекается из последней фиксированной строки в сетке. Существует исключение из этого правила: если сетка имеет пользовательские столбцы, содержимое заголовка строки извлекается из заголовков пользовательских столбцов, а не из содержимого ячейки фиксированной строки.

Если WithHeader имеет значение true, а сетка не содержит фиксированную строку или настраиваемые столбцы, содержимое заголовка строки будет взято из первой строки в сетке.

Вывод CSV-данных обычно начинается с первой нефиксированной строки в сетке.

процедура LoadFromCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true);

Загружает содержимое сетки из файла формата данных с разделителями-запятыми (CSV) (добавлен в Lazarus с r32179).

Столбцы будут добавлены в сетку или удалены из нее по мере необходимости, в соответствии с количеством полей, включенных в каждую строку файла CSV. Загрузка CSV-файла не будет изменять количество фиксированных строк, которые уже существовали в сетке.

Аргумент Afilename указывает имя исходного файла с содержимым CSV.

Дополнительный параметр ADelimiter может использоваться для указания другого разделителя. Например: для файла с TAB-разделителями ADelimiter должен быть #9. Другим популярным файловым форматом является файл с разделителем-запятой.

Параметр WithHeader используется для определения того, следует ли считать первую строку в файле CSV "строкой заголовка" или нет. Если сетка имеет фиксированные строки, а WithHeader - [значение] true, заголовки столбцов для последней фиксированной строки будут взяты из строки заголовка. Однако, обратите внимание, что если сетка имеет пользовательские столбцы, строка заголовка будет использоваться как источник заголовков столбцов, а заголовки столбцов всегда [будут] отображаются в первой фиксированной строке сетки или [будут] скрыты, если в сетке не [окажется] фиксированных строк.

Если в процедуре LoadFromCSVFile возникла проблема с загрузкой файла CSV (например, кавычки или пробелы начинают неправильно интерпретироваться), вы можете загрузить сетку вручную, например, CsvDocument ... и, конечно, патч для LoadFromCSVFile всегда приветствуется.

свойство Cols[index: Integer]: TStrings read GetCols write SetCols;

Получает/устанавливает список строк из/в индекса столбца данной сетки, начиная с индекса строки от 0 до RowCount-1.

Примеры
  • Пример определения [содержимого строки]:
//задать содержимое третьего столбца в сетке из списка ListBox
Grid.Cols[2] := ListBox1.Items;
  • Пример получения [содержимого строки]:
//задать содержимое Listbox, [получив значение] из столбца сетки с индексом 4:
procedure TForm1.FillListBox1;
var 
  StrTempList: TStringList;
begin
  StrTempList := TStringList(Grid.Cols[4]);
  if StrTempList<>nil then begin
    ListBox1.Items.Assign(StrTempList);
    StrTempList.Free;
  end;
end;
На заметку

Это свойство работает по-разному в Lazarus и Delphi при получении данных из сетки. В Lazarus создается временный объект TStringList для извлечения содержимого столбца. Пользователь должен освобождать этот объект после использования.

Это также означает, что изменения в возвращенном списке не будут влиять на содержимое или макет сетки.

См. пример получения [содержимого строки] выше.

свойство Rows[index: Integer]: TStrings read GetRows write SetRows;

Получает/определяет список строк из/в индекса строки данной сетки, начиная с индекса столбца 0 до столбца ColCount-1.

На заметку

Это свойство работает по-разному в Lazarus и Delphi при получении данных из сетки. В Lazarus создается временный объект TStringList для извлечения содержимого строки. Пользователь должен освобождать этот объект после использования.

Это также означает, что изменения в возвращенном списке не будут влиять на содержимое или макет сетки.

Примеры
  • Пример определения [содержимого строки]:
//задать содержимое третьего столбца в сетке из списка ListBox
Grid.Rows[2] := ListBox1.Items;
  • Пример получения [содержимого строки]:
//задать содержимое Listbox, [получив значение] из столбца сетки с индексом 4:
procedure TForm1.FillListBox1;
var 
  StrTempList: TStringList;
begin
  StrTempList := TStringList(Grid.Rows[4]);
  if StrTempList<>nil then begin
    ListBox1.Items.Assign(StrTempList);
    StrTempList.Free;
  end;
end;
  • Пример, который не работает, и его исправление: список извлеченных строк - только для чтения
// это не сработает и вызовет утечку памяти
// потому что возвращаемый StringList не освобождается
Grid.Rows[1].CommaText := '1,2,3,4,5';
Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g'; 
 
//исправление первого случая
Lst:=TStringList.Create;
Lst.CommaText := '1,2,3,4,5';
Grid.Rows[1] := Lst;
Lst.Free;

свойство UseXORFeatures;

Boolean свойство, значение по умолчанию: False;

Это свойство [отвечает за то], как прямоугольник фокуса отображается в сетке. Когда [его значение равно] True, прямоугольник отрисовывается [вокруг ячейки] с использованием растровой операции XOR. Это позволяет нам видеть прямоугольник фокуса, вне зависимости от того, какой цвет фона [имеют] ячейки. Когда [его значение равно] False, пользователь может управлять цветом точек фокуса прямоугольника с помощью свойства FocusColor property

Он также контролирует внешний вид изменения размера столбца/строки. Когда [его значение равно] True, линия визуально показывает размер, который [только еще] будет иметь столбец или строка, если пользователь завершит операцию. Когда [его значение равно] False, изменение размера столбца или строки вступает в силу [сразу же], как [только] пользователь [начнет] перетаскивать мышь.

TValueListEditor

TValueListEditor - это элемент управления, полученный из TCustomStringGrid для редактирования пар ключ-значение.

свойство DisplayOptions

Управляет различными аспектами внешнего вида TValueListEditor.

свойство TitleCaptions

Устанавливает значения подписи заголовков (если [свойство] doColumnTitles [включено в набор свойств] DisplayOptions). Если DisplayOptions не [содержит] значения doColumnTitles, то используются заголовки [с надписями] по умолчанию.

свойство Strings

Предоставляет доступ к списку строк, которые содержат пары Key-Value.
Пары «ключ-значение» должны быть в форме:
KeyName=Value

свойство ItemProps

Вы можете использовать это свойство для управления тем, как элементы в столбцах [со значением] "Value" можно редактировать.
Это контролируется установкой свойств EditStyle и ReadOnly [свойства] ItemProp.

свойство KeyOptions

KeyOptions - это набор TKeyOptions, определяющий, может ли пользователь изменять содержимое столбца "Key".

  • KeyEdit: пользователь может редактировать имя ключа
  • KeyAdd: пользователь может добавить ключи (нажав Insert в сетке). KeyAdd требует KeyEdit.
  • KeyDelete: пользователь может удалить пары Key-Value (нажав Ctrl+Delete).
  • KeyUnique: если установлено, то ключи должны иметь уникальные имена. Попытка ввести дубликат ключа вызовет исключение.

свойство DropDownRows

Если редактор ячейки является списком выбора (ValueEdit1.ItemProps['key1'].EditStyle=esPickList), это свойство устанавливает DropDownCount[(высоту выпадающего)] отображаемого списка. По умолчанию используется значение 8.

функция DeleteRow

Удаляет пару Key-Value индексированной строки, полностью удаляя строку.

функция InsertRow

Вставляет строку в сетку и устанавливает пару Key-Value. Возвращает индекс вновь вставленной строки.

функция IsEmptyRow

Возвращает [значение] true, если ячейки индексированных строк пусты (Keys[aRrow]=; Valves[aRrow]=).

функция FindRow

Возвращает строку с указанным именем ключа.

функция RestoreCurrentRow

Отменяет редактирование в текущей строке (если редактор все еще в фокусе). [Событие] случается, когда пользователь нажимает клавишу Escape.

Измененное поведение некоторых свойств, унаследованных из TCustomStringGrid

Параметры Options

В соответствии с особенностями TValueListEditor его свойство Options имеет определенные ограничения

  • goColMoving не доступно в Options (вы не можете установить его).
  • goAutoAddRows может быть установлен только в том случае, если KeyAdd отмечен в KeyOptions. Установка KeyAdd автоматически установит goAutoAddRows.
  • goAutoAddRowsSkipContentCheck недоступен (на данный момент это приводит к сбою в TValueListeditor: требуется исправление).
свойство FixedRows

Может [иметь значение] только 1 (показать названия столбцов) или 0 (не показывать заголовки столбцов)

свойство ColCount

Всегда [будет значение] 2.

Общие замечания по использованию TValueListEditor

При манипулировании содержимым ValueListEditor (сетка) рекомендуется манипулировать лежащим в основе свойством Strings.
Если вы хотите вставлять или удалять строки, то либо делайте это путем прямого доступа к свойству Strings, либо используйте общедоступные методы из TValueListEditor: DeleteRow(), InsertRow(), MoveRow() и ExchangeRow().
Попытка использовать методы предка для управления строками или столбцами (например, Columns.Add) может привести к сбою.

Работа с сетками

Настройка сеток

Сетка - это компоненты, полученные из класса TCustomControl и не имеющие родственного виджета, связанного с ними, что означает, что сетки не ограничены по виду текущей темы интерфейса. Это может быть как преимуществом, так и недостатком: обычно программисты хотят создавать приложение с одинаково выглядящим интерфейсом. Хорошей новостью является то, что сетки Lazarus достаточно гибки, чтобы получить что-то из обоих миров; программисты могут легко сделать сетки похожими на другие собственные элементы управления, или они могут настроить сетку в мельчайших деталях, чтобы они могли получать почти одинаковый вид в любом интерфейсе платформы или виджета (то есть, за исключением полос прокрутки, поскольку их внешний вид все еще определяется текущей темой).

Свойства и События для настройки сеток

Некоторые свойства могут влиять на то, как выглядит сетка, действуя, когда ячейка собирается быть отрисованной в PrepareCanvas/OnPrepareCanvas, изменяя свойства холста по умолчанию, такие как цвет кисти или шрифт. Ниже приведен список таких свойств:

  • AlternateColor. Этим [свойством] пользователь может изменить цвет фона, появляющийся в чередующихся строках. Это позволяет легко считывать данные строк сетки.
  • Color. Это [свойство] устанавливает основной цвет, используемый для отрисовки фона нефиксированных ячеек.
  • FixedColor. Это цвет, используемый для создания фона фиксированных ячеек.
  • Flat. Это [свойство] устраняет трехмерный вид фиксированных ячеек [(т.е. делает их плоскими)].
  • TitleFont. Шрифт, используемый для отрисовки текста в фиксированных ячейках.
  • TitleStyle. Это свойство изменяет трехмерный вид неподвижных ячеек, имеет 3 значения [стилей]:
    • tsLazarus. Это стандартный вид.
    • tsNative. Это [свойство] пытается установить внешний вид, соответствующий текущей теме виджетов.
    • tsStandard. Этот стиль представляет собой более контрастный вид, как у сетки Delphi.
  • AltColorStartNormal. Boolean[-свойство]. Если [имеет значение] true: чередующийся цвет всегда находится во второй строке после фиксированных строк, первая строка после фиксированных строк будет всегда цветной. Если [имеет значение] false: цвет по умолчанию установлен в первую строку, как если бы не было фиксированных строк.
  • BorderColor. Это [свойство] устанавливает цвет границы сетки, используемую, когда Flat:=True и BorderStyle:=bsSingle;
  • EditorBorderStyle. Если установлено значение bsNone под [ОС] windows, редакторы ячейки не будут иметь границы, как в delphi по умолчанию для bsSingle, потому что граница может быть специфичной для темы в некоторых виджетах и обеспечивать единообразный внешний вид.
  • FocusColor. Цвет, используемый для отрисовки текущего фокуса ячейки, если [свойство] UseXORFeatures не установлено, по умолчанию это clRed.
  • FocusRectVisible. Включает/Выключает отрисовку фокуса ячейки.
  • GridLineColor. Цвет линий сетки в нефиксированной области.
  • GridLineStyle. Стиль Pen используется для отрисовки линий в нефиксированной области, возможны следующие варианты: psSolid, psDash, psDot, psDashDot, psDashDotDot, psinsideFrame, psPattern,psClear. По умолчанию - psSolid.
  • SelectedColor. Цвет, используемый для отрисовки фона ячейки на выбранных ячейках.
  • UseXORFeatures. Если установлено, прямоугольник фокуса рисуется с использованием режима XOR, поэтому он должен сделать видимым прямоугольник фокуса в сочетании с любым цветом ячейки. Это также влияет на просмотр движущихся столбцов.
  • DefaultDrawing. Boolean[-свойство]. Обычно сетки готовят холст сетки с использованием некоторых свойств в соответствии с видом окрашиваемой ячейки. Если пользователь пишет обработчик событий OnDrawCell, набор DefaultDrawing также рисует фон ячейки. Если пользователь сам рисует ячейку, лучше отключить это свойство, чтобы отрисовка не дублировалась. В StringGrid набор DefaultDrawing рисует текст в каждой ячейке.
  • AutoAdvance. [свойство определяет,] куда установится курсор ячейки при нажатии [клавиши] enter или после редактирования.
  • TabAdvance. [свойство определяет,] куда переместится курсор ячейки при нажатии [клавиши] Tab или Shift-Tab.
  • ExtendedColSizing. Если [значение равно] true, пользователь может изменять размер столбцов не только в заголовках, но и по высоте столбцов.


Другие свойства, которые также влияют на то, как выглядят сетки.

Options.

Свойство Options - это набор некоторых элементов для обеспечения разнообразной функциональности, но некоторые из них напрямую связаны с внешним видом сетки. Эти параметры можно установить в режиме разработки(designtime) или времени выполнения(runtime).
  • goFixedVertLine, goFixedHorzLine [свойство] рисует вертикальную или горизонтальную линию, соответственно разграничивая ячейки или столбцы в фиксированной области, по умолчанию [значение равно] active.
  • goVertLine, goHorzLine [свойство аналогично] предыдущему, но для нормальной просматриваемой области. Сетка может быть создана для имитации списка, [если] отключить оба этих элемента.
  • goDrawFocusSelected если этот элемент включен, фон выделения отрисовывается в сфокусированной ячейке в дополнение к пунктирному прямоугольнику фокуса (обратите внимание, что это не работает, когда установлена опция goRowSelect, в этом случае строка всегда отрисовывается так, как если бы была выбрана опция goDrawFocusSelected)
  • goRowSelect выбирает [для выделения] полную строку вместо отдельных ячеек
  • goFixedRowNumbering если [свойство] установлено, сетка будет нумеровать строки в первом фиксированном столбце
  • goHeaderHotTracking если [свойство] установлено, сетка попытается показать другой курсор мыши, когда он будет находиться над любой фиксированной ячейкой. Для того, чтобы это сработало, желаемая зона ячейки должна быть включена с помощью свойства HeaderHotZones. Попробуйте комбинировать этот параметр с свойством TitleStyle:=tsNative, чтобы получить подсвеченный курсор мыши [соответственно] теме [виджета].
  • goHeaderPushedLook если этот параметр установлен, этот элемент включает [эффект] "нажатия" при клике [мышью] любой фиксированной ячейки. Зона "нажатия" ячеек становится доступной при использовании свойства HeaderPusedZones.

(напишите еще)

Описание процесса отрисовки сетки

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

Процесс заключается в следующем:

  • Сначала определяется область видимых ячеек: каждая строка проверяется, пересекает ли она область отсечения холста; если все в порядке, то видимая область окрашивается путем рисования столбцов каждой строки.
  • Значения столбцов и строк используются для идентификации ячейки, которая должна быть окрашена, и снова каждый столбец проверяется на пересечение с областью отсечения; если все в порядке, некоторые дополнительные свойства, такие как прямоугольная протяженность ячейки и визуальное состояние, передаются в качестве аргументов методу DrawCell.
  • Когда процесс отрисовки выполняется, визуальное состояние каждой ячейки настраивается в соответствии с параметрами сетки и расположением в сетке. Визуальное состояние сохраняется в виде типа TGridDrawState, который представляет собой набор со следующими элементами:
    • gdSelected У ячейки будет выделенный вид.
    • gdFocused Ячейка будет иметь [рамку] фокуса.
    • gdFixed Ячейка будет нарисована [как] фиксированная.
    • gdHot [когда] мышь находится над этой ячейкой, то [курсор] подсвечивается.
    • gdPushed [когда по] ячейке [кликают мышью], она отрисовывается как нажимаемая [(кликабельная)]
  • DrawCell. Метод DrawCell является виртуальным и может быть переопределен в унаследованных сетках для выполнения пользовательского рисования. Информация, переданная DrawCell, помогает идентифицировать конкретную ячейку, которая окрашивается, физическую область, занятую экраном, и ее видимый статус. Подробнее см. ссылку DrawCell. Для каждой ячейки происходит следующее:
  • PrepareCanvas. В этом методе, если установлено свойство DefaultDrawing, холст сетки настраивается со свойствами по умолчанию для кисти и шрифта на основе текущего визуального состояния. Для нескольких свойств дизайн-тайма и времени выполнения выравнивание текста устанавливается в соответствии с выбором программиста в пользовательских столбцах, если они существуют. Если DefaultDrawing [имеет значение] false, цвет кисти устанавливается в clWindow и цвет шрифта в clWindowText, выравнивание текста задается значением defaultTextStyle сетки.
  • OnPrepareCanvas. Если программист написал обработчик событий для события OnPrepareCanvas, он вызывается в этот момент. Это событие можно использовать для простой настройки, например: изменения цвета фона ячейки, свойств шрифта, таких как цвет, шрифт и стиль, расположение текста, как различные комбинации левого, центрального, верхнего, нижнего, правого выравнивания и т.д. Любые изменения, сделанные на холсте [ранее] в этом событии будут потеряны, потому что следующая перерисовка ячейки снова сбросит содержимое холста до состояния по умолчанию. Поэтому безопасно делать изменения только для конкретной ячейки или клеток и забыть об этом для остальных. Использование этого события иногда помогает избежать использования события сетки OnDrawCell, где пользователи будут вынуждены дублировать код отрисовки сетки.
    Todo: образцы того, что можно сделать и что оставить для OnDrawCell? ...
  • OnDrawCell. Next if no handler for OnDrawCell event was specified, the grid calls the DefaultDrawCell method which simply paints the cell background using the current canvas brush color and style. If the OnDrawCell handler exists, the grid first paints the cell background but only if DefaultDrawing property was set, then it calls OnDrawCell event to do custom cell painting. Usually programmers want to do custom drawing only for particular cells, but standard drawing for others; in this case, they can restrict custom operation to certain cell or cells by looking into ACol, ARow and AState arguments, and for other cells simply call DefaultDrawCell method and let the grid to take care of it.
  • Text. At this point (only for TStringGrid) if DefaultDrawing property is true, the cell's text content is painted.
  • Grid lines. The last step for each cell is to paint the grid lines: if grid options goVertLine, goHorzLine, goFixedVertLine and goFixedHorzLine are specified the cell grid is drawn at this point. Grids with only rows or only cols can be obtained by changing these options. If the programmer elected to have a "themed" look it is done at this point also (see property TitleStyle).
  • FocusRect. When all columns of current row have been painted it is time to draw the focus rectangle for the current selected cell or for the whole row if goRowSelect option is set.

Differences with Delphi

  • In Lazarus TCustomGrid.DrawCell method is not abstract and its default implementation does basic cell background filling.
  • In Delphi, the cell's text is drawn before entering the OnDrawCell event (see bug report #9619).

Grid's cell selection

The location of a grid's current (focused) cell (or row) can be changed using keyboard, mouse or through code. In order to change cell focus successfully to another position, we must test the target position to see if it is allowed to receive cell focus. When using keyboard, the property AutoAdvance performs part of the process by finding what should be the next focused cell. When using mouse clicks or moving by code, focus will not move from the current cell unless the target cell is permitted to receive focus.

The grid calls function SelectCell to see if a cell is focusable: if this function returns true, then the target cell identified with arguments aCol and aRow is focusable (the current implementation of TCustomGrid simply returns true). TCustomDrawGrid and hence TDrawGrid and TStringGrid override this method to check first if cell is any wider than 0; normally you don't want a 0 width cell selected so a cell with these characteristics is skipped automatically in the process of finding a suitable cell. The other thing the overridden method SelectCell does is to call the user configurable event OnSelectCell: this event receives the cell coordinates as arguments and always returns a default result value of true.

Once a cell is known to be focusable and we are sure a movement will take place, first the method BeforeMoveSelection is called; this in turns triggers the OnBeforeSelection event. This method's arguments are the coordinates for the new focused cell. At this point any visible editor is hidden too. The "before" word means that selection is not yet changed and current focused coordinates can be accessed with grid.Col and grid.Row properties.

After that, the internal focused cell coordinates are changed and then MoveSelection method is called; this method's purpose is to trigger the OnSelection event if set (this is a notification that the focused cell has, by this time, already changed and cell coordinates are now available through grid.row and grid.col properties).

Note that is not good to use OnSelectCell event to detect cell focus changes, as this event will be triggered several times even for the same cell in the process of finding a suitable cell. Is better to use OnBeforeSelection or OnSelection events for this purpose.

Differences with Delphi

  • SelectCell and OnSelectCell behaviour is probably different - can't really comment on the differences. In Lazarus they are used in functionality like AutoAdvance which as far as I know doesn't exist in Delphi.

When built-in properties are not enough: derived grids

Derived grids usually have to override the following methods:
DrawAllRows: Draws all visible rows.
DrawRow: Draws all cells in a row.
DrawRow draws all cells in the row by first checking if cell is within clipping region, and only draws the cell if it is.
DrawCell:
DrawCellGrid:
DrawCellText:
DrawFocusRect:
(write me).

Save and Retrieve Grid Content

The SaveToFile and LoadFromFile methods allows a grid to save and retrieve it's layout and data to/from a XML format file. TStringGrid, as inherited from TCustomStringGrid, has also the ability to "export" and "import" its content to/from a Comma Separated Values format file, best known as CSV files. This is described in the SaveToCSVFile and LoadFromCSVFile methods reference (TODO: make links).


The kind of information that can be saved and then retrieved when using SaveToFile and LoadFromFile is determined by the SaveOptions property (of type TSaveOptions) which is a set of options described as follows:

soDesign:     Save & load ColCount,RowCount,FixedCols,FixedRows,
              ColWidths, RowHeights and Options (TCustomGrid)
soPosition:   Save & load Scroll position, Row, Col and Selection (TCustomGrid)
soAttributes: Save & load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)
soContent:    Save & load Text (TCustomStringGrid)
soAll:        set of all options (soAll:=[soDesign,soPosition,soAttributes,soContent];)

Not all options apply to all kind of grids, for example, soContent do not apply to TCustomGrid derived grids like TDrawGrid as this kind of grid does not have the concept of "content". TStringGrid is a special kind of grid that "knows" how to handle strings and can use the soContent option if specified.

The soAttributes option is not used in Lazarus standard grids, is there for derived grids.

When using LoadFromFile the grid also uses the SaveOptions property in order to know what kind of information needs to retrieve from the file, so is perfectly possible to specify SaveOptions:=[soDesign,soContent] on saving and only SaveOptions:=[soContent] on loading.

For a TStringGrid the default SaveOptions value is [soContent], for other kind of grids, SaveOptions is the empty set.

Note: One common issue when saving & retrieving grid data occurs when the user specify the SaveOptions property before SaveToFile but not before LoadFromFile. When using LoadFromFile some time after SaveToFile have been used, the SaveOptions property is properly set, but if LoadFromFile is executed on next program run, the SaveOptions property might not have been properly setup, for this reason is recommended to always specify SaveOptions property just before LoadFromFile, or doing it globally at program start like in the following example:


Example:

  1. First, go to menu "File -> New -> Application";
  2. Put an empty TStringGrid on the form;
  3. Put a TButton and TOpenDialog on the form;
  4. Add the event OnCreate for the form;
  5. Add the event OnClick for the button.
unit Unit1; 

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,
  Buttons, StdCtrls, XMLCfg;

type

  { TForm1 }
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
    procedure Form1Create(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end; 

var
  Form1: TForm1; 

implementation

{ TForm1 }

procedure TForm1.Form1Create(Sender: TObject);
begin
 //sets the SaveOptions at creation time of the form 
 stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
 //Ask if thew Execute method of the OpenDialog was launched 
 //when this occurs, the user selects an XML file to Load
 //wich name was stored in the FileName prop.

 if opendialog1.Execute then
 Begin
   //Clear the grid 
   StringGrid1.Clear;
   //Load the XML
   StringGrid1.LoadFromFile(OpenDialog1.FileName);
   //Refresh the Grid
   StringGrid1.Refresh;
 End;
end;

initialization
  {$I unit1.lrs}

end.

The sample xml file: (Copy the text below into a txt file. Don't forget put the xml header :-))

<?xml version="1.0"?>
<CONFIG>
  <grid version="3">
    <saveoptions create="True" position="True" content="True"/>
    <design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20">
      <options>
        <goFixedVertLine value="True"/>
        <goFixedHorzLine value="True"/>
        <goVertLine value="True"/>
        <goHorzLine value="True"/>
        <goRangeSelect value="True"/>
        <goDrawFocusSelected value="False"/>
        <goRowSizing value="False"/>
        <goColSizing value="False"/>
        <goRowMoving value="False"/>
        <goColMoving value="False"/>
        <goEditing value="False"/>
        <goTabs value="False"/>
        <goRowSelect value="False"/>
        <goAlwaysShowEditor value="False"/>
        <goThumbTracking value="False"/>
        <goColSpanning value="False"/>
        <goRelaxedRowSelect value="False"/>
        <goDblClickAutoSize value="False"/>
        <goSmoothScroll value="True"/>
      </options>
    </design>
    <position topleftcol="1" topleftrow="1" col="1" row="1">
      <selection left="1" top="1" right="1" bottom="1"/>
    </position>
    <content>
      <cells cellcount="10">
        <cell1 column="0" row="0" text="Title Col1"/>
        <cell2 column="0" row="1" text="value(1.1)"/>
        <cell3 column="0" row="2" text="value(2.1)"/>
        <cell4 column="0" row="3" text="value(3.1)"/>
        <cell5 column="0" row="4" text="value(4.1)"/>
        <cell6 column="1" row="0" text="Title Col2"/>
        <cell7 column="1" row="1" text="value(1.2)"/>
        <cell8 column="1" row="2" text="value(2.2)"/>
        <cell9 column="1" row="3" text="value(3.2)"/>
        <cell10 column="1" row="4" text="value(4.2)"/>
      </cells>
    </content>
  </grid>
</CONFIG>

--Raditz 21:06, 11 Jan 2006 (CET) from ARGENTINA

Grid Cell Editors

The grid uses cell editors to change the content of cells.

For a specialized grid like TStringGrid, the editor is the usual single line text editor control, but sometimes it's desirable to have other means to enter information. For example:

  1. show the open file dialog to find the location of a file so the user doesn't have to type the full path manually
  2. if the text in the cell represents a date, popup a calendar so we can choose a specific date easily.

Sometimes the information the user should enter in a cell is restricted to a limited list of words; in this case typing the information directly might introduce errors and validation routines might need to be implemented. We can avoid this by using a cell editor that presents the user with a list containing only the legal values.

This is also the case for generic grids like TDrawGrid where the user needs some kind of structure to hold the data that will be shown in the grid. In this situation, the information that is entered in the cell editor updates the internal structure to reflect the changes in the grid.

Builtin cell editors

The grids.pas unit already includes some of the most used cell editors ready for use in grids. It is also possible to create new cell editors (custom cell editors) if the built-in editors are not appropiate for a specific task.

The built-in cell editors are Button, Edit, and Picklist.

Using cell editors

Users can specify what editor will be used for a cell using one of two methods.

  1. Using a custom column and selecting the ButtonStyle property of the column. In this method the user can select the style of the editor that will be shown. Available values are: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn.
  2. Using OnSelectEditor grid event. Here the user specifies in the Editor parameter which editor to use for a cell identified for column aCol and row ARow in a TCustomDrawGrid derived grid or TColumn in TCustomDBGrid. For this purpose there is a useful public function of grids, EditorByStyle(), that takes as parameter one of the following values: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn. This method takes precedence over the first one using custom columns. A custom cell editor can be specified here. This event is also the place to setup the editor with values specific to the cell, row or column.

Setting the ButtonStyle property only works if a column is created with the StringGrid1.Columns.Add; statement. Using expression like StringGrid1.ColCount:=X; will cause an exception. Setting the ButtonStyle property can be done with a similar code:

if ColCB< StringGrid1.Columns.Count
   then StringGrid1.Columns.Items[ColCB].ButtonStyle:=cbsCheckboxColumn;

Description of editor styles

The following is a description of the editor styles. They are enumerated values of type TColumnButtonStyle and so they are prefixed by 'cbs'. This type was used to remain compatible with Delphi's DBGrid.

  • cbsAuto
This is the default editor style for TCustomGrid derived grids. The actual editor class that will be used to edit the cell content depends on several factors. For TCustomGrids it uses a TStringCellEditor class derived from TCustomMaskEdit. This editor is specialized to edit single line strings. It is then used in TStringGrid and TDrawGrid by default. When using Custom Columns, if the programmer filled the Column's PickList property, this behaves as if cbsPickList editor style was set. For a TCustomDBGrid that has a field of type boolean, it behaves as if cbsCheckBoxColumn editor style was specified. This is the recommended value for Custom Cell Editors. TODO: related OnEditingDone.
  • cbsEllipsis
This editor style is the most generic one. When used, a button appears in the editing cell and programmers could use the OnEditButtonClick grid event to detect when the user has pressed the button and take any action programmed for such a cell. For example a programmer could use this editor style to pop up a calendar dialog to allow the user easily to select a specific date. Other possibilities could be to show a file open dialog to find files, a calculator so user can enter the numeric result of calcs, etc.
OnEditButtonClick is just a notification, to find out in which cell a button has been clicked by taking a look at the grid.Row and grid.Col properties.
A DBGrid has specific properties to retrieve the active column or field and because this event occurs in the active record, it could update the information in the active field.
This editor style is implemented using TButtonCellEditor, a direct descendant of TButton.
  • cbsNone
This editor style instructs the grid not to use any editor for a specific cell or column; it behaves then, as if the grid were readonly for such a cell or column.
  • cbsPickList
Used to present the user with a list of values that can be entered. This editor style is implemented using TPickListCellEditor, a component derived from TCustomComboBox. The list of values that are shown is filled in one of two ways depending on the method used to select the editor style.
  1. When using custom columns, programmers can enter a list of values using the column's PickList property. [FOR BEGINNERS: TODO: exact procedure to edit the list]
  2. In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList). See here for an example
The value in a TStringGrid grid will automatically reflect the value selected. If necessary the programmer could detect the moment the value is selected by writing an event handler for the grid's OnPickListSelect event, so additional steps can be taken (for example, to process the new value). TODO: related OnEditingDone.
  • cbsCheckboxColumn
It can be useful when the data content associated with the column is restricted to a pair of values, for example, yes-no, true-false, on-off, 1-0, etc. Instead of forcing the user to type the values for this kind of data in a StringCellEditor or to choose one from a list, cbsCheckboxColumn is used to modify the data of a column by using a checkbox representation that the user can toggle by using a mouse click or pressing the SPACE key (if the column, containing the checkbox is selected and if the StringGrid is editable).
Getting or setting the value of the checkbox in a cell is done the following way:
StringGrid1.Cell[x,y]:='Z';
where Z shall be replaced by 0 for Unchecked, 1 for Checked and an empty string for Grayed. Note that any value (string) different from 0 and 1 will be displayed as a grayed checkbox.
If a columns' ButtonStyle property is set to cbsAuto and DBGrid detects that the field associated with the column is a boolean field, then the grid uses this editor style automatically. This automatic selection can be disabled or enabled using DBGrid's OptionsExtra property; setting dgeCheckboxColumn element to false disables this feature.
The values that are used to recognize the checked or unchecked states are set in a column's properties ValueChecked and ValueUnchecked.
At any moment, the field value can be in one to three states: Unchecked, Checked or Grayed. Internally these states are identified by the following values of type TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked and gcbpGrayed.
This editor style doesn't use real TCheckbox components to handle user interaction: the visual representation is given by three built-in bitmap images that corresponds to the possible states of checkbox. The used bitmaps can be customized by writing a handler for DBGrid event OnUserCheckboxBitmap; the handler of this event gets the state of the checkbox in the parameter CheckedState of type TDBGridCheckboxState and a bitmap parameter that the programmer could use to specify custom bitmaps.
  • cbsButtonColumn
This editor style is used to show a button on every cell on column. Like in the case of cbsCheckboxColumn this editor do not use real buttons, the appearance is defined by current widgetset theme.
The user knows what particular button was pressed by handling the grid's OnEditButtonClick and checking grid's col and row. Note that in this particular case, grid's col and row do not identify the currently selected cell, but the cell of the clicked button. Once the OnEditButtonClick event has been handled, and if the user has not modified the grid's col or row in this handler, the grid automatically resets the col and row to reflect the currently selected cell. While handling the OnEditButtonClick the current grid selection is available in grid.Selection which is a TRect property, left and right represent Column indexes, Top and Bottom are row indexes.
The button's caption is the corresponding cell string.

Editing grids

The effect of editing is different depending on what kind of grid is used, for example, TStringGrid stores the edited text internally and TDBGrid affects records in a dataset. TDrawGrid doesn't know what to do with the edited text, if the programmer do not take control on it, it's simply discarded.
For TDrawGrid (although this should work for all grid classes) in order to access the edited text, the programmer can use the event OnSetEditText which is triggered every time the user modify something in the editor, the aCol and aRow parameters of this event identify the cell being edited, the parameter value holds the text, this is the recommended method. Another way of access the edited text is taking it directly from the editor once the editing process has ended by using the event OnEditingDone. This could be accomplished by accessing the internal editor used for editing, in this case, the default "String Cell Editor". For this, there are two methods: The first is by using the event OnSelectEditor where the parameter Editor is the instance which will be used to edit a cell, you have to store this instance in a variable, for example TheEditor (of type TStringCellEditor), for later use in OnEditingDone. The second alternative is using the grid's method EditorByStyle, this method accepts an "editor style" as parameter and it returns the instace of the editor corresponding to that style. In our case knowing that the style cbsAuto returns the default cell editor, in the OnEditingDone event handler we can use directly TheEditor := Grid.EditorByStyle(cbsAuto). You can then get the text with TheEditor.Text. If you use this method note that "with great power, comes great responsibility".

Options and properties that affect editing

The editing behavior is controlled by a number of properties and options, virtually any adjustment or options that affects editing requires an editable grid or such adjustment might be ignored.
At start the editable state of the grids is different, for TDrawGrid and TStringGrid editing is disabled, for TDbGrid is enabled by default.
The Options property has several items that deal with editing, they are described below:

  • goEditing, dgEditing (in DbGrid). This option changes the editable state of the grid, can be changed at runtime because it is checked when editing is about to be started.
  • goAlwaysShowEditor. Normally the editor is hidden and it becomes visible only when it's needed. With this option, the editor will be visible all the time, if grid is not editable, this option is ignored and editor is always hidden.

cell1 column="0" row="0" text="Title Col1"/> selection left="1" top="1" right="1" bottom="1"/>

Howto and Examples

Focusing a cell

Focusing a cell in TStringGrid is easy. Note that counting starts from zero not 1. So to focus row 10, column 9, do:

StringGrid1.row := 9;
StringGrid1.col := 8;

Example: How to set a custom cell editor

See lazarus/examples/gridexamples/gridcelleditor/gridcelleditor.lpi (from laz 1.2)

Example: How to set a memo editor for dbgrids

You can, of course, use another control instead of a TMemo. Adjust to taste. Adapted from [1] (simplified to use the SelectedFieldRect property, see [2])

  • Place a memo control (or whatever control you want) on your form, set whatever properties you want and set visible to false. This will be used when editing a grid cell. We'll use GridCellMemo in this example.
  • In the OnSelectEditor event put the following code - adapt if you don't want to edit logical column 3, physical column 4:
  if (Column.DesignIndex = 3) then
  begin
      GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
      GridCellMemo.Text:=Column.Field.AsString;
      Editor := GridCellMemo;
  end;
  • Suppose your datasource is called Datasource1 and DBGrid is called ResultsGrid. Then, in the OnEditingDone event for the GridCellMemo put the following:

(The original forum post used the OnChange event, which would fire each time the content is changed. Using OnEditingDone only fires after the user is finished with his edits.)

  if not(Datasource1.State in [dsEdit, dsInsert]) then
    Datasource1.Edit;
    
  Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;

Example: How to add a button editor

// Conditionally show button editor in column index 1 or 2 if 
// cell in column index 1 is empty 
procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer; 
  var Editor: TWinControl);
begin
  if (aCol = 1) or (aCol = 2) then
    if StringGrid1.Cells[1,aRow] = '' then
    begin
      Editor := StringGrid1.EditorByStyle(cbsEllipsis);
    end;
  end;

// Triggering Action ...
procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
begin
  if StringGrid1.Col = 1 then Showmessage('column 1 editor clicked');
  if StringGrid1.Col = 2 then Showmessage('column 2 editor clicked');
end;


Example: Avoid display Text Field as (memo) for DBGrids

When you use SQL sentences like "Select * from A" maybe you get in a cell of grid something like (Memo) instead of your text. There so many ways to fix this, but maybe the easiest is change your "Select * from A" by "Select Cast(Column as TEXT) as Column from A". yeah i know it is not pascal code but is a common problem when you use a DBGrid.

 var
    sql : UTF8String;
    Queryu : TSQLQuery;
.....
    sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
    Queryu.SQL.Text:=sql;


Example: Working with Picklist, How to make it read only and How to fill it at run time.

Using grid's event OnSelectEditor one can customize how PickList editor (see cbsPickList button style) behaves. In next example the picklist editor from column 1 is modified so on odd rows the user can enter values by typing, on even rows the values are limited to the ones contained in its list. Also, this example show how to fill the list with different values depending on the row being processed.

procedure TForm1.gridSelectEditor(Sender: TObject; aCol, aRow: Integer;
  var Editor: TWinControl);
begin
  if aCol=1 then begin
    if (Editor is TCustomComboBox) then
      with Editor as TCustomComboBox do begin
        if (aRow mod 2=0) then
          Style := csDropDown
        else
          Style := csDropDownList;
        case aRow of
          1:
            Items.CommaText := 'ONE,TWO,THREE,FOUR';
          2:
            Items.CommaText := 'A,B,C,D,E';
          3:
            Items.CommaText := 'MX,ES,NL,UK';
          4:
            Items.CommaText := 'RED,GREEN,BLUE,YELLOW';
        end;
      end;
  end;
end;

Aligning text in StringGrids

This code shows how to use different text alignments in columns 2 and 3.

procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
  aState: TGridDrawState);
var
  MyTextStyle: TTextStyle;
begin
  if (aCol=2) or (aCol=3) then
  begin
    MyTextStyle := StringGrid1.Canvas.TextStyle;
    if aCol=2 then
      MyTextStyle.Alignment := taRightJustify 
    else 
    if aCol=3 then
      MyTextStyle.Alignment := taCenter;
    StringGrid1.Canvas.TextStyle := MyTextStyle;
  end;
end;

Multilines in Grids, DBGrid

This sample shows how to make multilined text in cell [3,2]. It works the same for DBGrid where OnPrepareCanvas have parameters for dealing with TColumns and from there with TFields.

procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
  aState: TGridDrawState);
var
  MyTextStyle: TTextStyle;
begin
  if (aRow=2) or (aCol=3) then
  begin
    MyTextStyle := StringGrid1.Canvas.TextStyle;
    MyTextStyle.SingleLine := false;
    StringGrid1.Canvas.TextStyle := MyTextStyle;
  end;
end;

Validating Entered Values

Lazarus version 0.9.29 introduces the StringGrid OnValidateEntry event of type TValidateEntryEvent which has the following declaration:

TValidateEntryEvent =
  procedure(sender: TObject; aCol, aRow: Integer;
            const OldValue: string; var NewValue: string) of object;
aCol,aRow are the cell coordinates of cell being validated.
OldValue is the value that was in cells[aCol,aRow] before editing started.
NewValue is the value that will be finally inserted in cells[aCol,aRow].

Because of the way StringGrid works by setting the cell value while user is editing (see grid's OnSetEditText event and SetEditText method), when the OnValidateEntry event triggers, the cell already contains the entered value (valid or not); using the event arguments OldValue and NewValue the cell value can be validated and changed if needed.

Usually validation occurs when the user has moved to another cell. If validation then fails, it is desirable to keep the cell editor visible/focused so the entered value can be corrected by user. To let the grid know that validation has failed, an exception needs to be raised. The grid will handle the exception to Application.HandleException and any movement is cancelled.

For example, suppose that cell[1,1] should hold only values 'A' or 'B', validation could be made with:

procedure TForm1.GridValidateEntry(sender: TObject; aCol,
  aRow: Integer; const OldValue: string; var NewValue: String);
begin
  if (aCol=1) and (aRow=1) then begin
    if grid.Cells[aCol,aRow]<>'A') and grid.Cells[aCol,aRow]<>'B') then begin
      // set a new valid value so user can just press RETURN to continue for example.
      NewValue := 'A';
      // another option is reverting to previous cell value (which is assumed to be valid)
      // NewValue := OldValue;
      raise Exception.Create('Only A or B are allowed here');
    end else begin
      // if no exception is raised, it is assumed the value is valid, yet if necessary
      // the final value can still be changed by filling NewValue with a different value
       
      // computer knows better :)
      if grid.Cells[1,1]='A' then 
        NewValue := 'B' 
      else 
        NewValue := 'A';
    end;
  end;
end;

Sorting Columns or Rows

Property ColumnClickSorts allows grid to be sorted automatically when user clicks a column header. Clicking the same column many times switches the sort order. Default column sort images are shown to indicate which column was clicked.

In code you can use SortColRow() method. Its first parameter is a boolean value which indicates true if a column is to be sorted or false for a row, the next parameter is the column or row index, the next parameters are optional and select subrange of rows (for column sorting) or columns (for row sorting) to be sorted. If the last parameters are not specified, the whole column or row is sorted. Sorting uses QuickSort algorithm, it could be changed if a descendant grid overrides the sort() method and calls doCompareCells for cell compare.

By default it sorts cell content as strings either in ascending or descending order which is selectable with property SortOrder, by default it uses ascending order.

// sort column 3 in ascending order
grid.SortColRow(true, 3);

// sort column 3 in descending order, skip fixed rows a top
grid.SortOrder := soDescending; // or soAscending
grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);

For custom sorting of numbers, dates, states, etc. StringGrid has the OnCompareCells event which users can handle for example this way:

procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol, BRow: Integer; var Result: integer);
begin
  // Result will be either <0, =0, or >0 for normal order.
  result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
  // For inverse order, just negate the result (eg. based on grid's SortOrder).
  if StringGrid1.SortOrder = soDescending then
    result := -result;
end;

You can use OnCompareCells also when automatic column sorting is enabled through ColumnClickSorts property.

Sorting columns or rows in DBGrid with sort arrows in column header

Here is an example that will sort a DBgrid using the OnTitleClick event and a TSQLQuery and indexes. This should also work for any compatible data set such as TbufDataset. The function uses the column.tag property to store the sort state for each column you click on, so when you go back to one you have already sorted it will pick the correct sort arrow to display.

Prerequisites:

  • You will need an imagelist to store the up/down sort arrows. Assign your imagelist to the dbgrid's TitleImageList property.
  • Ensure the TSQLQuery.MaxIndexesCount is large enough to hold the new indexes you will be creating. For this example I had it set to 100.
  • You will also need a private var to store the last column used to sort, for this example I have called it FLastColumn.

For this example the TSQLQuery used is called OpenQuery.

Light bulb  Примечание: As of March 21st 2013, TSQLQuery has no way to clear indexes, but as a work around you can set unidirectional to true, then set it to false and this will clear the indexes.

In order to reuse the TSQLQuery component with another SQL statement, the indexes must be cleared after sorting or an exception will be raised when you open the TSQLQuery with a different SQL statement.

FLastColumn: TColumn; //store last grid column we sorted on

procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
const
  ImageArrowUp=0; //should match image in imagelist
  ImageArrowDown=1; //should match image in imagelist
var
  ASC_IndexName, DESC_IndexName:string;
  procedure UpdateIndexes;
  begin
    // Ensure index defs are up to date
    OpenQuery.IndexDefs.Updated:=false; {<<<--This line is critical. IndexDefs.Update will not
    update if already true, which will happen on the first column sorted.}
    Openquery.IndexDefs.Update;
  end;
begin
  ASC_IndexName:='ASC_'+Column.FieldName;
  DESC_IndexName:='DESC_'+Column.FieldName;
  // indexes can't sort binary types such as ftMemo, ftBLOB
  if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
    exit;
  // check if an ascending index already exists for this column.
  // if not, create one
  if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
  begin
    OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
    UpdateIndexes; //ensure index defs are up to date
  end;
  // Check if a descending index already exists for this column
  // if not, create one
  if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
  begin
    OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
    UpdateIndexes; //ensure index defs are up to date
  end;

  // Use the column tag to toggle ASC/DESC
  column.tag := not column.tag;
  if boolean(column.tag) then
  begin
    Column.Title.ImageIndex:=ImageArrowUp;
    Openquery.IndexName:=ASC_IndexName;
  end 
  else
  begin
    Column.Title.ImageIndex:=ImageArrowDown;
    OpenQuery.IndexName:=DESC_IndexName;
  end;
  // Remove the sort arrow from the previous column we sorted
  if (FLastColumn <> nil) and (FlastColumn <> Column) then
    FLastColumn.Title.ImageIndex:=-1;
  FLastColumn:=column;
end;

Selecting Records in a DBGrid using checkboxes

The objective is to be able to select arbitrary records in a dbgrid using checkboxes, the grid has the ability to show checkboxes automatically when it detects there are boolean fields, for other field types the user can manually choose the cbsCheckboxColumn ButtonStyle for the column. For this kind of columns the user just click the checkbox and the field content is modified accordingly.

But what happen if there is no such available field in our dataset? or we don't want the grid enter edit state when checking the checkbox?. Adding a fieldless column with ButtonStyle=cbsCheckboxColumn will show all checkboxes grayed and disabled because there is no field linked to this column and so nothing to modify. As we want to handle the checkbox state ourselves we need to store the state somewhere for each record. For this we can use the class TBookmarklist (defined in dbgrids.pas unit) where property CurrentRowSelected can tell if the current record is selected or not. By using dbgrid events OnCellClick and OnUserCheckboxState we can track the checkbox state.

Note this technique needs Lazarus r31148 or later which implements event OnUserCheckboxState.

...
uses ..., dbgrids, stdctrls, ...

type

  { TForm1 }

  TForm1 = class(TForm)
  ...
    procedure DBGrid1CellClick(Column: TColumn);
    procedure DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  ...
  private
    RecList: TBookmarklist;
  ...
  end;

procedure TForm1.DBGrid1CellClick(Column: TColumn);
begin
  if Column.Index=1 then
    RecList.CurrentRowSelected := not RecList.CurrentRowSelected;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  RecList := TBookmarkList.Create(DbGrid1);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  RecList.Free;
end;

procedure TForm1.DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
begin
  if RecList.CurrentRowSelected then
    AState := cbChecked
  else
    AState := cbUnchecked;
end;


Highlighting the selected cell column and row

In Lazarus revision 40276 an option (gdRowHighlight) has been added, it works similar to goRowSelect but it uses a lighter color for selection and the focus rect is only selected cell and not in the whole row. This works fine for rows, but how about columns?.

This section presents a way to highlight columns, rows or both (an example that highlights only column and row headers can be found in lazarus/examples/gridexamples/spreadsheet, this howto is really an extension of that example). This uses two grid events: OnBeforeSelection and OnPrepareCanvas, drawing is not necessary.

The event OnBeforeSelection is triggered when the selection is about the change, in this event we can know what cell is currently selected and what cell will be selected next. We use this information to invalidate the whole row or column of both the old and the new cells. When the next paint cycle starts, the grid will be instructed to paint the cells that belong to the invalidated areas. One of the first steps for painting is calling the OnPrepareCanvas event (if it exists) to setup default canvas properties. We use this event to set up the highlighted row or column:

procedure TForm1.gridBeforeSelection(Sender: TObject; aCol, aRow: Integer);
begin
  // we can decide here if we want highlight columns, rows or both
  // in this example we highlight both
  if Grid.Col<>aCol then
  begin
    // a change on current column is detected
    grid.InvalidateCol(aCol);      // invalidate the new selected cell column
    grid.InvalidateCol(grid.Col);  // invalidate the current (it will be the 'old') selected column
  end;
  if Grid.Row<>aRow then
  begin
    grid.InvalidateRow(aRow);      // invalidate the new selected cell row
    grid.InvalidateRow(grid.Row);  // invalidate the current (it will be the 'old') selected row 
  end;
end; 

procedure TForm1.gridPrepareCanvas(sender: TObject; aCol, aRow: Integer;
  aState: TGridDrawState);
begin
  if gdFixed in aState then
  begin
    if (aCol=grid.Col) or (aRow=grid.Row) then
      grid.Canvas.Brush.Color := clInactiveCaption; // this would highlight also column or row headers
  end else
  if gdFocused in aState then begin
    // we leave alone the current selected/focused cell
  end else
  if (aCol=Grid.Col) or (aRow=grid.Row) then
    grid.Canvas.Brush.Color := clSkyBlue; // highlight rows and columns with clSkyBlue color
end;