Difference between revisions of "Grids Reference Page/ru"

From Free Pascal wiki
Jump to navigationJump to search
 
(159 intermediate revisions by 4 users not shown)
Line 2: Line 2:
  
  
----
+
==Цель==
<span style="color:#FF0000">ENG: '''AT THE MOMENT THIS PAGE IS UNDER TRANSLATION.''' <br /> RUS: '''В НАСТОЯЩИЙ МОМЕНТ СТРАНИЦА НАХОДИТСЯ В ПРОЦЕССЕ ПЕРЕВОДА.'''</span>
 
----
 
 
 
 
 
----
 
-- [[User:Zoltanleo|Zoltanleo]] 23:47, 26 September 2018 (CEST): Ввиду сложности дословного перевода текста с английского на русский слова, требующиеся по смыслу, но отсутствующие в английской версии, указаны в квадратных скобках.
 
----
 
 
 
 
 
== Назначение==
 
  
 
Этот текст попытается показать пользователю некоторые аспекты использования компонентов сеток (Grid) в Lazarus. Он также предназначен для использования в качестве руководства для пользователей, которые никогда не использовали сетки (опытные пользователи тут обычно получают только информацию по новой функциональности).  
 
Этот текст попытается показать пользователю некоторые аспекты использования компонентов сеток (Grid) в Lazarus. Он также предназначен для использования в качестве руководства для пользователей, которые никогда не использовали сетки (опытные пользователи тут обычно получают только информацию по новой функциональности).  
Line 32: Line 22:
  
 
== Дерево наследования ==
 
== Дерево наследования ==
<pre>
+
 
                       [TCustomControl]           
+
                       [[TCustomControl]]           
 
                               |                     
 
                               |                     
 
                               |                     
 
                               |                     
Line 43: Line 33:
 
                 |                          |       
 
                 |                          |       
 
       +--------+--------+                |       
 
       +--------+--------+                |       
       |                |              TDbGrid    
+
       |                |              [[TDBGrid]]    
   TDrawGrid      TCustomStringGrid                 
+
   [[TDrawGrid]]     TCustomStringGrid                 
 
                         |                         
 
                         |                         
 
                         |                         
 
                         |                         
                     TStringGrid                  
+
                     [[TStringGrid]]
</pre>
 
  
 
== Начальный пример ==
 
== Начальный пример ==
Line 58: Line 47:
 
#Поместите сетку в форму
 
#Поместите сетку в форму
 
#*В палитре компонентов выберите вкладку "additional"  
 
#*В палитре компонентов выберите вкладку "additional"  
#*Нажмите значок TStringGrid []
+
#*Нажмите значок TStringGrid [[image:tstringgrid.png]]
 
#*Нажмите на форму рядом с левым верхним углом. Появится новая пустая сетка.
 
#*Нажмите на форму рядом с левым верхним углом. Появится новая пустая сетка.
 
#Поместите кнопку в форму
 
#Поместите кнопку в форму
 
#*с помощью палитры компонентов выберите вкладку "Standard"
 
#*с помощью палитры компонентов выберите вкладку "Standard"
#*Нажмите значок TButton []
+
#*Нажмите значок TButton [[Image:tbutton.png]]
 
#*Нажмите на пустую область формы. Появится новая кнопка.
 
#*Нажмите на пустую область формы. Появится новая кнопка.
 
#Дважды нажмите кнопку из шага 3 и запишите следующий код в обработчике щелчка кнопки:   
 
#Дважды нажмите кнопку из шага 3 и запишите следующий код в обработчике щелчка кнопки:   
#*<syntaxhighlight>Stringgrid1.Cells[1,1] := 'Привет, Мир!';</syntaxhighlight>
+
#*<syntaxhighlight lang=pascal>StringGrid1.Cells[1,1] := 'Привет, Мир!';</syntaxhighlight>
#Запустите программу, щелкнув значок воспроизведения []
+
#Запустите программу, щелкнув значок воспроизведения [[Image:menu_run.png]]
#*по нажатию кнопки button1, текст приветствия будет отображаться в столбце 1 ячейки 1 (обратите внимание, что левый столбец и верхняя строка равны 0)
+
#*по нажатию кнопки Button1, текст приветствия будет отображаться в столбце 1 ячейки 1 (обратите внимание, что левый столбец и верхняя строка равны 0)
  
 
== Различия между сетками Lazarus и Delphi ==
 
== Различия между сетками Lazarus и Delphi ==
Line 134: Line 123:
  
 
В runtime столбцы могут быть изменены с помощью кода:
 
В runtime столбцы могут быть изменены с помощью кода:
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
   var
 
   var
 
     c: TGridColumn;
 
     c: TGridColumn;
Line 156: Line 145:
  
 
В настоящее время нет планов, чтобы сетки использовали только настраиваемые столбцы.
 
В настоящее время нет планов, чтобы сетки использовали только настраиваемые столбцы.
 +
 +
==== свойство AutoFillColumns ====
 +
Часто сетки шире, чем горизонтальное пространство, необходимое для столбцов - это оставляет неприятную пустую область справа от последнего столбца. Сетки LCL предоставляют механизм для расширения ширины указанных столбцов, так что пустое пространство заполняется автоматически.
 +
 +
Для этой цели необходимо либо добавить '''Columns''', как описано [[Grids_Reference_Page/ru#.D1.81.D0.B2.D0.BE.D0.B9.D1.81.D1.82.D0.B2.D0.BE_Columns| выше]], либо установив свойство <code>ColCount</code>, и свойство сетки <code>AutoFillColumns</code> должно быть установлено в <code>true</code>. Каждый столбец имеет свойство <code>SizePriority</code>. Если оно имеет значение <code>0</code>, ширина столбца берется из свойства <code>Width</code> столбца. Но когда оно имеет ненулевое значение, ширина столбца корректируется до среднего доступного размера, оставшегося для всех столбцов с ненулевым <code>SizePriority</code>.
  
 
=== TCustomDBGrid ===
 
=== TCustomDBGrid ===
Line 171: Line 165:
 
==== процедура InplaceEditor ====
 
==== процедура InplaceEditor ====
 
См. пример из bug 23103 - ''и вставьте объяснение того, что он делает/почему это необходимо. Проверить входные значения? Изменить то, что показано?''
 
См. пример из bug 23103 - ''и вставьте объяснение того, что он делает/почему это необходимо. Проверить входные значения? Изменить то, что показано?''
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: char);
 
procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: char);
 
var
 
var
Line 201: Line 195:
 
Эта процедура устанавливает ширину столбца  [в соответствии с] размером самого широкого текст, который был ею найден во всех строках столбца '''aCol'''. Совет: см. параметр '''goDblClickAutoSize''', чтобы позволить автоматически изменять размер столбцов при двойном нажатии на границу столбца.
 
Эта процедура устанавливает ширину столбца  [в соответствии с] размером самого широкого текст, который был ею найден во всех строках столбца '''aCol'''. Совет: см. параметр '''goDblClickAutoSize''', чтобы позволить автоматически изменять размер столбцов при двойном нажатии на границу столбца.
  
==== procedure AutoSizeColumns; ====
+
==== процедура AutoSizeColumns; ====
Automatically resizes all columns by adjusting them to fit in the longest text in each column. This is a quick method of applying AutoSizeColumn() for every column in the grid.
+
Автоматически изменяет размеры всех столбцов так, чтобы они растягивались под самый длинный текст для каждого столбца. Это быстрый способ применения AutoSizeColumn() для каждого столбца в сетке.
==== procedure Clean; overload; ====
+
 
Cleans all cells in the grid, fixed or not.
+
==== процедура Clean; overload; ====
==== procedure Clean(CleanOptions: TGridZoneSet); overload; ====
+
Очищает все ячейки в сетке, [будь то] фиксированные или нет.
Cleans all cells in the grid subject to the given CleanOptions. See [[doc:lcl/grids/tgridzoneset.html|TGridZoneSet]] for more information. Some examples:
+
 
*Clean all cells: grid.Clean([]); (the same as grid.clean)
+
==== процедура Clean(CleanOptions: TGridZoneSet); overload; ====
*Clean all non fixed cells: grid.Clean([gzNormal]);
+
Очищает все ячейки в сетке в соответствии с данными CleanOptions. Дополнительную информацию см. в [[doc:lcl/grids/tgridzoneset.html|TGridZoneSet]]. Некоторые примеры:
*Clean all cells but don't touch grid column headers: Grid.Clean([gzNormal, gzFixedRows]);
+
*Очистить все ячейки: <tt>grid.Clean([]);</tt> (аналогично <tt>grid.clean</tt>)
 +
*Очистить все нефиксированные ячейки: <tt>grid.Clean([gzNormal]);</tt>
 +
*Очистить все ячейки, но не касается заголовков столбцов сетки: <tt>Grid.Clean([gzNormal, gzFixedRows]);</tt>
 +
 
 +
==== процедура Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TGridZoneSet); overload; ====
 +
делает то же самое, что и <tt>Clean(CleanOptions: TGridZoneSet)</tt>, но ограничивается данными <tt>StartCol, StartRow, EndCol и EndRow</tt>. Примеры:
 +
*Очистить индекс столбца с 4 по 6, но не касаться заголовков столбцов сетки, много вариантов:
 +
<syntaxhighlight lang=pascal>
 +
Grid.Clean (4,Grid.FixedRows, 6,Grid.RowCount-1,[]); //или Grid.Clean(4,0,6,Grid.RowCount-1, [gzNormal]);
 +
</syntaxhighlight>
  
==== procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TGridZoneSet); overload; ====
+
и т.п.
does the same as Clean(CleanOptions:TGridZoneSet) but restricted to the given StartCol,StartRow,EndCol and EndRow. Examples:
 
*Clean column index 4 to 6 but don't touch grid column headers: many variations, Grid.Clean(4,Grid.FixedRows,6,Grid.RowCount-1,[]); Grid.Clean(4,0,6,Grid,RowCount-1, [gzNormal]); etc.
 
==== procedure Clean(aRect: TRect; CleanOptions: TGridZoneSet); overload; ====
 
The same as Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions), just taking a TRect instead of individual cell coordinates. Useful to clean the selection: grid.Clean(Grid.Selection,[]);
 
==== procedure SaveToCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ====
 
Save grid content to a comma separated values format (CSV) file (added in Lazarus r32179).
 
  
The AFilename argument specifies a file name where the content will be saved. If the file exists, the content will be overwritten. If it doesn't exist, the file will be created.
+
==== процедура Clean(aRect: TRect; CleanOptions: TGridZoneSet); overload; ====
 +
То же самое, что и <tt>Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions)</tt>, просто берется [прямоугольник] TRect вместо отдельных координат ячейки. Для очистки выделения полезно [сделать]:
 +
<syntaxhighlight lang=pascal>
 +
grid.Clean(Grid.Selection,[]);
 +
</syntaxhighlight>
  
ADelimiter (an optional argument) is used to supply a custom separator if required. By default a CSV format is produced (that is, ADelimiter:=',';) for a TAB separated file ADelimiter should be #9.
+
==== процедура SaveToCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ====
 +
Сохраняет содержимое сетки в файл формата данных с разделителями-запятыми (CSV) (добавлен в Lazarus с r32179).
  
The WithHeader parameter is used to decide if a "row header" should be included or not. The row header is a list of field names at the beginning of the output file; its content comes from the last fixed row in the grid.
+
Аргумент '''AFilename''' указывает имя файла, в котором содержимое будет сохранено. Если файл существует, содержимое будет перезаписано. Если он не существует, файл будет создан.
There is an exception to this rule: if the grid has custom columns, the row header content comes from the custom column titles and not from fixed row cell content.  
 
  
If WithHeader is true and the grid does not include a fixed row or custom columns, the row header content will be taken from the first row in the grid.
+
'''ADelimiter''' (необязательный аргумент) используется для предоставления настраиваемого разделителя, если требуется. По умолчанию создается формат CSV (то есть ADelimiter: = ',') для TAB-разделяемого файла ADelimiter должен быть #9.
  
Normal CSV data output will start at the first non-fixed row in the grid.
+
Параметр '''WithHeader''' используется для определения необходимости включения или отключения "row header"[(заголовка строки)]. Заголовок строки - это список имен полей в начале выходного файла; его содержимое извлекается из последней фиксированной строки в сетке.
 +
Существует исключение из этого правила: если сетка имеет пользовательские столбцы, содержимое заголовка строки извлекается из заголовков пользовательских столбцов, а не из содержимого ячейки фиксированной строки.
  
==== procedure LoadFromCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ====
+
Если WithHeader имеет значение true, а сетка не содержит фиксированную строку или настраиваемые столбцы, содержимое заголовка строки будет взято из первой строки в сетке.
Loads grid content from a comma separated values format (CSV) file (added in Lazarus r32179).
 
  
Columns will be added or deleted to or from the grid as needed according to the number of fields included in each line of the CSV file. Loading a CSV file will not modify the number of fixed rows that already existed in the grid.
+
Вывод CSV-данных обычно начинается с первой нефиксированной строки в сетке.
  
The ''Afilename'' argument specifies the name of the source file with the CSV content.
+
==== процедура LoadFromCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ====
 +
Загружает содержимое сетки из файла формата данных с разделителями-запятыми (CSV) (добавлен в Lazarus с r32179).
  
''ADelimiter'' optional parameter may be used to specify a different separator or delimiter. An example: for a tab-separated file, ''ADelimiter'' should be #9. Another popular file format is semicolon-delimited file, where ''ADelimiter'' should be ;
+
Столбцы будут добавлены в сетку или удалены из нее по мере необходимости, в соответствии с количеством полей, включенных в каждую строку файла CSV. Загрузка CSV-файла не будет изменять количество фиксированных строк, которые уже существовали в сетке.
  
The ''WithHeader'' parameter is used to decide if the first row in the CSV file should be considered as the "header row" or not. If the grid has fixed rows and ''WithHeader'' is true, the column captions for the last fixed row will be taken from the header row. Note however that if the grid has custom columns, the header row will be used as source for the column titles and custom column titles are always shown in the grid's first fixed row or hidden if there are no fixed rows in the grid.
+
Аргумент '''Afilename''' указывает имя исходного файла с содержимым CSV.
  
If the ''LoadFromCSVFile'' procedure has difficulty loading your CSV file (e.g. quotes or spaces being incorrectly interpreted), you could manually load the grid using e.g. [[CsvDocument]]... and of course a patch for ''LoadFromCSVFile'' is always welcome.
+
Дополнительный параметр '''ADelimiter''' может использоваться для указания другого разделителя. Например: для файла с TAB-разделителями ''ADelimiter'' должен быть #9. Другим популярным файловым форматом является файл с разделителем-запятой.
  
==== property Cols[index: Integer]: TStrings read GetCols write SetCols; ====
+
Параметр '''WithHeader''' используется для определения того, следует ли считать первую строку в файле CSV "строкой заголовка" или нет. Если сетка имеет фиксированные строки, а ''WithHeader'' - [значение] true, заголовки столбцов для последней фиксированной строки будут взяты из строки заголовка. Однако, обратите внимание, что если сетка имеет пользовательские столбцы, строка заголовка будет использоваться как источник заголовков столбцов, а заголовки столбцов всегда [будут] отображаются в первой фиксированной строке сетки или [будут] скрыты, если в сетке не [окажется] фиксированных строк.
Get/set a list of strings from/to the given grid's column index starting from row index 0 to RowCount-1.  
 
===== Examples =====
 
* Set Example: Set the content of the third column in the grid from a ListBox:
 
<syntaxhighlight>Grid.Cols[2] := ListBox1.Items;</syntaxhighlight>
 
  
* Get Example: Set the content of a Listbox from the grid's column index 4:
+
Если в процедуре ''LoadFromCSVFile'' возникла проблема с загрузкой файла CSV (например, кавычки или пробелы начинают неправильно интерпретироваться), вы можете загрузить сетку вручную, например, [[CsvDocument/ru|CsvDocument]] ... и, конечно, патч для ''LoadFromCSVFile'' всегда приветствуется.
<syntaxhighlight>procedure TForm1.FillListBox1;
+
 
 +
==== свойство Cols[index: Integer]: TStrings read GetCols write SetCols; ====
 +
Получает/устанавливает список строк из/в индекса столбца данной сетки, начиная с индекса строки от 0 до RowCount-1.
 +
===== Примеры =====
 +
* Пример определения [содержимого строки]:  
 +
<syntaxhighlight lang=pascal>
 +
//задать содержимое третьего столбца в сетке из списка ListBox
 +
Grid.Cols[2] := ListBox1.Items;
 +
</syntaxhighlight>
 +
 
 +
* Пример получения [содержимого строки]:  
 +
<syntaxhighlight lang=pascal>
 +
//задать содержимое Listbox, [получив значение] из столбца сетки с индексом 4:
 +
procedure TForm1.FillListBox1;
 
var  
 
var  
 
   StrTempList: TStringList;
 
   StrTempList: TStringList;
Line 261: Line 272:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
===== Notes. =====
+
===== На заметку =====
This property works differently in Lazarus and in Delphi when getting the data from the grid.  
+
Это свойство работает по-разному в Lazarus и Delphi при получении данных из сетки.
In Lazarus a temporary TStringList object is created for retrieving the column content. It is the responsibility of the user to free this object after use.  
+
В Lazarus создается временный объект TStringList для извлечения содержимого столбца. Пользователь должен освобождать этот объект после использования.
  
This means also that changes in the returned list will not affect the grids content or layout.  
+
Это также означает, что изменения в возвращенном списке не будут влиять на содержимое или макет сетки.
  
See the Get Example.
+
См. пример получения [содержимого строки] выше.
  
==== property Rows[index: Integer]: TStrings read GetRows write SetRows; ====
+
==== свойство Rows[index: Integer]: TStrings read GetRows write SetRows; ====
Get/set a list of strings from/to the given grid's row index starting from column index 0 to column ColCount-1.  
+
Получает/определяет список строк из/в индекса строки данной сетки, начиная с индекса столбца 0 до столбца ColCount-1.
===== Notes. =====
+
===== На заметку =====
This property works differently in Lazarus and in Delphi when getting the data from the grid.  
+
Это свойство работает по-разному в Lazarus и Delphi при получении данных из сетки.
In Lazarus a temporary TStringList object is created for retrieving the row content. It is the responsibility of the user to free this object after use.  
+
В Lazarus создается временный объект TStringList для извлечения содержимого строки. Пользователь должен освобождать этот объект после использования.  
  
This means also that changes in the returned list will not affect the grid's content or layout.  
+
Это также означает, что изменения в возвращенном списке не будут влиять на содержимое или макет сетки.
  
===== Examples =====
+
===== Примеры =====
* Set Example: Set the content of the third row in the grid from a ListBox:
+
* Пример определения [содержимого строки]:  
<syntaxhighlight>Grid.Rows[2] := ListBox1.Items;</syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 +
//задать содержимое третьего столбца в сетке из списка ListBox
 +
Grid.Rows[2] := ListBox1.Items;
 +
</syntaxhighlight>
  
* Get Example: Set the content of a Listbox from the grid's row index 4:
+
* Пример получения [содержимого строки]:
<syntaxhighlight>procedure TForm1.FillListBox1;
+
<syntaxhighlight lang=pascal>
 +
//задать содержимое Listbox, [получив значение] из столбца сетки с индексом 4:
 +
procedure TForm1.FillListBox1;
 
var  
 
var  
 
   StrTempList: TStringList;
 
   StrTempList: TStringList;
Line 293: Line 309:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
* An Example that doesn't work, and its Fix: Retrieved string list is read only
+
* Пример, который не работает, и его исправление: список извлеченных строк  - только для чтения
  
<syntaxhighlight>// this will not work and will cause memory leak
+
<syntaxhighlight lang=pascal>
// because returned StringList is not being freed
+
// это не сработает и вызовет утечку памяти
 +
// потому что возвращаемый StringList не освобождается
 
Grid.Rows[1].CommaText := '1,2,3,4,5';
 
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';  
 
Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g';  
 
   
 
   
// fixing the first case
+
//исправление первого случая
 
Lst:=TStringList.Create;
 
Lst:=TStringList.Create;
 
Lst.CommaText := '1,2,3,4,5';
 
Lst.CommaText := '1,2,3,4,5';
Line 306: Line 323:
 
Lst.Free;</syntaxhighlight>
 
Lst.Free;</syntaxhighlight>
  
==== property UseXORFeatures; ====
+
==== свойство UseXORFeatures; ====
Boolean property, default value: False;
+
Boolean свойство, значение по умолчанию: False;
  
This property controls how the dotted focus rectangle appears in the grid. When True, the rectangle is painted using the XOR raster operation. This allow us to see the focus rectangle no matter what the cells' background color is. When False, the user can control the color of the dotted focus rectangle using the [[FocusColor property]]
+
Это свойство [отвечает за то], как прямоугольник фокуса отображается в сетке. Когда [его значение равно] True, прямоугольник отрисовывается [вокруг ячейки] с использованием растровой операции XOR. Это позволяет нам видеть прямоугольник фокуса, вне зависимости от того, какой цвет фона [имеют] ячейки. Когда [его значение равно] False, пользователь может управлять цветом точек фокуса прямоугольника с помощью свойства [[FocusColor property]]
  
It also controls the look of the column/row resizing. When True, a line shows visually the size that the the column or row will have if the user ends the operation. When False, the column or row resizing takes effect just as the user drags the mouse.
+
Он также контролирует внешний вид изменения размера столбца/строки. Когда [его значение равно] True, линия визуально показывает размер, который [только еще] будет иметь столбец или строка, если пользователь завершит операцию. Когда [его значение равно] False, изменение размера столбца или строки вступает в силу [сразу же], как [только] пользователь [начнет] перетаскивать мышь.
  
 
===TValueListEditor===
 
===TValueListEditor===
TValueListEditor is a control derived from TCustomStringGrid for editing Key-Value pairs.
+
TValueListEditor - это элемент управления, полученный из TCustomStringGrid для редактирования пар ключ-значение.
 +
 
 +
====свойство DisplayOptions====
 +
Управляет различными аспектами внешнего вида TValueListEditor.
 +
 
 +
====свойство TitleCaptions====
 +
Устанавливает значения подписи заголовков (если [свойство] '''doColumnTitles''' [включено в набор свойств] '''DisplayOptions''').
 +
Если <tt>DisplayOptions</tt> не [содержит] значения <tt>doColumnTitles</tt>, то используются заголовки [с надписями] по умолчанию.
 +
 
 +
====свойство Strings====
 +
Предоставляет доступ к списку строк, которые содержат пары Key-Value.<br>
 +
Пары «ключ-значение» должны быть в форме:<br>
 +
<tt>KeyName=Value</tt>
  
====Property DisplayOptions====
+
====свойство ItemProps====
Controls various aspects of the TValueListEditor's appearance.
+
Вы можете использовать это свойство для управления тем, как элементы в столбцах [со значением] "Value" можно редактировать. <br>
 +
Это контролируется установкой свойств '''EditStyle''' и '''ReadOnly''' [свойства] '''ItemProp'''.
  
====Property TitleCaptions====
+
====свойство KeyOptions====
Sets the values of the title captions (if doColumnTitles is in DisplayOptions).
+
KeyOptions - это набор TKeyOptions, определяющий, может ли пользователь изменять содержимое столбца "Key".
If DisplayOptions lacks the value doColumnTitles then default captions are used.
+
*KeyEdit: пользователь может редактировать имя ключа
 +
*KeyAdd: пользователь может добавить ключи (нажав Insert в сетке). KeyAdd требует KeyEdit.
 +
*KeyDelete: пользователь может удалить пары Key-Value (нажав Ctrl+Delete).
 +
*KeyUnique: если установлено, то ключи должны иметь уникальные имена. Попытка ввести дубликат ключа вызовет исключение.
  
====Property Strings====
+
====свойство DropDownRows====
Provides access to the list of strings that hold the Key-Value pairs.<br>
+
Если редактор ячейки является списком выбора (ValueEdit1.ItemProps['key1'].EditStyle=esPickList), это свойство устанавливает DropDownCount[(высоту выпадающего)] отображаемого списка. По умолчанию используется значение 8.
Key-Value pairs must be in the form:<br>
 
'KeyName=Value'
 
  
====Property ItemProps====
+
====функция DeleteRow====
You can use this property to control how the items in the "Value" columns can be edited.<br>
+
Удаляет пару Key-Value индексированной строки, полностью удаляя строку.
This is controlled by setting the ItemProp's EditStyle and ReadOnly properties.
 
  
====Property KeyOptions====
+
====функция InsertRow====
KeyOptions is a set of TKeyOptions controlling whether the user can modify the contents of the "Key" column.
+
Вставляет строку в сетку и устанавливает пару Key-Value. Возвращает индекс вновь вставленной строки.
*KeyEdit: the user can edit the name of the Key
 
*KeyAdd: the user can add keys (by pressing Insert in the grid). KeyAdd requires KeyEdit.
 
*KeyDelete: the user can delete Key-Value pairs (by pressing Ctrl+Delete).
 
*KeyUnique: if set, then Keys must have unique names. Attempting to enter a dulpicate Key will raise an exception.
 
  
====Property DropDownRows====
+
====функция IsEmptyRow====
If the cell editor is a picklist (ValueEdit1.ItemProps['key1'].EditStyle=esPickList) this property sets the DropDownCount of the displayed list. The default is 8.
+
Возвращает [значение] true, если ячейки индексированных строк пусты (Keys[aRrow]=''; Valves[aRrow]='').
  
====Function DeleteRow====
+
====функция FindRow====
Deletes the Key-Value pair of the indexed row removing the row entirely.
+
Возвращает строку с указанным именем ключа.
  
====Function InsertRow====
+
====функция RestoreCurrentRow====
Inserts a row in the grid and sets the Key-Value pair. Returns the index of the newly inserted row.
+
Отменяет редактирование в текущей строке (если редактор все еще в фокусе). [Событие] случается, когда пользователь нажимает клавишу Escape.
  
====Function IsEmptyRow====
+
====Измененное поведение некоторых свойств, унаследованных из TCustomStringGrid====
Returns true if the indexed row's cells are empty (Keys[aRow]=''; Values[aRow]='').
 
  
====Function FindRow====
+
=====Параметры Options=====
Retutns the row that has the specified key name.
+
В соответствии  с особенностями TValueListEditor его свойство Options имеет определенные ограничения
 +
*goColMoving не доступно в Options (вы не можете установить его).
 +
*goAutoAddRows может быть установлен только в том случае, если KeyAdd отмечен в KeyOptions. Установка KeyAdd автоматически установит goAutoAddRows.
 +
*goAutoAddRowsSkipContentCheck недоступен (на данный момент это приводит к сбою в TValueListeditor: требуется исправление).
  
====Function RestoreCurrentRow====
+
=====свойство FixedRows=====
Undoes the editing in the current row (if the editor is still focused). Happens when the user presses the Escape key.
+
Может [иметь значение] только 1 (показать названия столбцов) или 0 (не показывать заголовки столбцов)
  
====Altered behaviour of some properties derived from TCustomStringGrid====
+
=====свойство ColCount=====
 +
Всегда [будет значение] 2.
  
=====Property Options=====
+
====Общие замечания по использованию TValueListEditor====
Due to the nature of TValueListEditor its Options property has certain restrictions
+
При манипулировании содержимым ValueListEditor (сетка) рекомендуется манипулировать лежащим в основе свойством Strings.<br>
*goColMoving is not allowed in Options (you cannot set it).
+
Если вы хотите вставлять или удалять строки, то либо делайте это путем прямого доступа к свойству Strings, либо используйте общедоступные методы из <tt>TValueListEditor</tt>: <tt>DeleteRow()</tt>, <tt>InsertRow()</tt>, <tt>MoveRow()</tt> и <tt>ExchangeRow()</tt>.<br>
*goAutoAddRows can only be set if KeyAdd is in KeyOptions. Setting KeyAdd will automatically set goAutoAddRows.
+
Попытка использовать методы предка для управления строками или столбцами (например, Columns.Add) может привести к сбою.
*goAutoAddRowsSkipContentCheck is not allowed (for the time being, it causes a crash in TValueListeditor: needs fixing).
 
  
=====Property FixedRows=====
+
====Saving and loading the contenst of a TValueListEditor====
Can only be 1 (show column titles) or 0 (don't show column titles)
 
  
=====Property ColCount=====
+
=====Using the Strings property=====
Is always 2.
+
If you just want to save and load the content of the TValueListEditor (e.g. the Key/Value pairs, not the layout) you can use Strings.SaveToFile and StringsLoadFromFile.<br>
 +
In case of LoadFromFile the <tt>RowCount</tt> property will be autmatically adjusted.<br>
 +
No sanity checks are performed upon LoadFromFile (so lines that do not represent a Key/Value pair will end up as Keys without Value).
  
====General comments on the use of TValueListEditor====
+
=====Using TValueList.SaveToFile and TValueList.LoadFromFile=====
When manipulating the contents of the ValueListEditor (the grid), it is recommended to manipulate the underlying Strings property.<br>
+
When using SaveToFile and LoadFromFile you get the additional benefits of also being able to save and load the layout, like with other grids.<br>
If you want to insert or delete rows then either do this by accessing the Strings property directly, or use the public methods from TValueListEditor: DeleteRow(), InsertRow(), MoveRow() and ExchangeRow().<br>
+
SaveToFiel will also save information about wether or not the TValueListEditor uses ColumnTitles, and if so, it saves them.
Trying to use ancestor's methods to manipulate rows or columns (e.g. Columns.Add) might result in a crash.
+
<br>
 +
<br>
 +
In contrast to it's ancestors TValueList.LoadFromFile performs sanity checks on the file it tries to read. In particular RowCount must be specified and there cannot be a cell with RowIndex&nbsp;>&nbsp;RowCount or ColumnIndex other than 0 or 1. <br>
 +
If the file does not seem to be a valid TValueListEditor grid file, and exception is raised.<br>
 +
<br>
 +
TValueList.SaveToFile and TValueList.LoadFromFile do not work correctly in Lazarus versions <= 2.1 r62044.
 +
=====Using SaveToCSVFile and LoadFromCSV file=====
 +
These methods should <b>not</b> be used at the moment, since for a TValueListEditor they are flawed.
  
== Working with grids ==
+
== Работа с сетками ==
=== Customizing grids ===
+
=== Настройка сеток ===
Grid are components derived from the [http://lazarus-ccr.sourceforge.net/docs/lcl/controls/tcustomcontrol.html TCustomControl] class, and don't have a native widget associated with them which means that grids are not restricted by the look of current interface theme. This can be both an advantage and a disadvantage: usually programmers want to create a uniform-look application. The good news is that Lazarus grids are flexible enough to get something from both worlds; programmers can easily make grids look similar to other native controls, or they can customize the grid to the finest detail so they can obtain almost the same look in any platform or widget interface (that is, with the exception of scrollbars, because their look is still determined by the current theme).
+
Сетка - это компоненты, полученные из класса [http://lazarus-ccr.sourceforge.net/docs/lcl/controls/tcustomcontrol.html TCustomControl] и не имеющие родственного виджета, связанного с ними, что означает, что сетки не ограничены по виду текущей темы интерфейса. Это может быть как преимуществом, так и недостатком: обычно программисты хотят создавать приложение с одинаково выглядящим интерфейсом. Хорошей новостью является то, что сетки Lazarus достаточно гибки, чтобы получить что-то из обоих миров; программисты могут легко сделать сетки похожими на другие собственные элементы управления, или они могут настроить сетку в мельчайших деталях, чтобы они могли получать почти одинаковый вид в любом интерфейсе платформы или виджета (то есть, за исключением полос прокрутки, поскольку их внешний вид все еще определяется текущей темой).
  
=== Properties and Events for customizing grids ===
+
=== Свойства и События для настройки сеток ===
Some properties can affect the way the grid looks by acting when the cell is about to be painted in PrepareCanvas/OnPrepareCanvas by changing default canvas properties like brush color or font. Following is a list of such properties:
+
Некоторые свойства могут влиять на то, как выглядит сетка, действуя, когда ячейка собирается быть отрисованной в PrepareCanvas/OnPrepareCanvas, изменяя свойства холста по умолчанию, такие как цвет кисти или шрифт. Ниже приведен список таких свойств:
*'''AlternateColor.''' With this the user can change the background color appears on alternated rows. This is to allow easy reading off of grid rows data.
+
*'''AlternateColor.''' Этим [свойством] пользователь может изменить цвет фона, появляющийся в чередующихся строках. Это позволяет легко считывать данные строк сетки.
*'''Color.''' This sets the primary color used to draw non fixed cells background.
+
*'''Color.''' Это [свойство] устанавливает основной цвет, используемый для отрисовки фона нефиксированных ячеек.
*'''FixedColor.''' This is the color used to draw fixed cells background.
+
*'''FixedColor.''' Это цвет, используемый для создания фона фиксированных ячеек.
*'''Flat.''' This eliminates the 3d look of fixed cells.<br>
+
*'''Flat.''' Это [свойство] устраняет трехмерный вид фиксированных ячеек [(т.е. делает их плоскими)].<br>
*'''TitleFont.''' Font used to draw the text in fixed cells.<br>
+
*'''TitleFont.''' Шрифт, используемый для отрисовки текста в фиксированных ячейках.<br>
*'''TitleStyle.''' This property changes the 3D look of fixed cells, there are 3 settings:
+
*'''TitleStyle.''' Это свойство изменяет трехмерный вид неподвижных ячеек, имеет 3 значения [стилей]:
**''tsLazarus.'' This is the default look
+
**''tsLazarus.'' Это стандартный вид.
**''tsNative.'' This tries to set a look that is conforms with the current widgetset theme.
+
**''tsNative.'' Это [свойство] пытается установить внешний вид, соответствующий текущей теме виджетов.
**''tsStandard.'' This style is a more contrasted look, like Delphi grids.
+
**''tsStandard.'' Этот стиль представляет собой более контрастный вид, как у сетки Delphi.
*'''AltColorStartNormal.''' Boolean. If true: alternate color is always in the second row after fixed rows, the first row after fixed rows will be always color. If false: default color is set to the first row as if there were no fixed rows.
+
*'''AltColorStartNormal.''' Boolean[-свойство]. Если [имеет значение] true: чередующийся цвет всегда находится во второй строке после фиксированных строк, первая строка после фиксированных строк будет всегда цветной. Если [имеет значение] false: цвет по умолчанию установлен в первую строку, как если бы не было фиксированных строк.
*'''BorderColor.''' This sets the grid's border color used when Flat:=True and BorderStyle:=bsSingle;
+
*'''BorderColor.''' Это [свойство] устанавливает цвет границы сетки, используемую, когда Flat:=True и BorderStyle:=bsSingle;
*'''EditorBorderStyle.''' If set to bsNone under windows the cell editors will not have the border, like in delphi, set to bsSingle by default because the border can be theme specific in some widgetsets and to allow a uniform look.<br>
+
*'''EditorBorderStyle.''' Если установлено значение bsNone под [ОС] windows, редакторы ячейки не будут иметь границы, как в delphi по умолчанию для bsSingle, потому что граница может быть специфичной для темы в некоторых виджетах и обеспечивать единообразный внешний вид.<br>
*'''FocusColor.''' The color used to draw the current focused cell if UseXORFeatures is not set, by default this is clRed.
+
*'''FocusColor.''' Цвет, используемый для отрисовки текущего фокуса ячейки, если [свойство] UseXORFeatures не установлено, по умолчанию это clRed.
*'''FocusRectVisible.''' Turns on/off the drawing of focused cell.
+
*'''FocusRectVisible.''' Включает/Выключает отрисовку фокуса ячейки.
*'''GridLineColor.''' Color of grid lines in non fixed area.
+
*'''GridLineColor.''' Цвет линий сетки в нефиксированной области.
*'''GridLineStyle.''' Pen style used to draw lines in non fixed area, possible choices are: ''psSolid'', ''psDash'', ''psDot'', ''psDashDot'', ''psDashDotDot'', ''psinsideFrame'', ''psPattern'',''psClear''. default is ''psSolid''.
+
*'''GridLineStyle.''' Стиль Pen используется для отрисовки линий в нефиксированной области, возможны следующие варианты: ''psSolid'', ''psDash'', ''psDot'', ''psDashDot'', ''psDashDotDot'', ''psinsideFrame'', ''psPattern'',''psClear''. По умолчанию - ''psSolid''.
*'''SelectedColor.''' Color used to draw cell background on selected cells.
+
*'''SelectedColor.''' Цвет, используемый для отрисовки фона ячейки на выбранных ячейках.
*'''UseXORFeatures.''' If set, focus rect is drawn using XOR mode so it should make visible the focus rect in combination with any cell color ackground. It also affects the moving columns look.
+
*'''UseXORFeatures.''' Если установлено, прямоугольник фокуса рисуется с использованием режима XOR, поэтому он должен сделать видимым прямоугольник фокуса в сочетании с любым цветом ячейки. Это также влияет на просмотр движущихся столбцов.
*'''DefaultDrawing.''' Boolean. Normally the grids prepare the grid canvas using some properties according to the kind of cell that is being painted. If the user writes an OnDrawCell event handler, a set DefaultDrawing also paints the cell background. If the user draws the cell himself, it is better to turn off this property so painting is not duplicated. In a StringGrid, a set DefaultDrawing draws the text in each cell.
+
*'''DefaultDrawing.''' Boolean[-свойство]. Обычно сетки готовят холст сетки с использованием некоторых свойств в соответствии с видом окрашиваемой ячейки. Если пользователь пишет обработчик событий OnDrawCell, набор DefaultDrawing также рисует фон ячейки. Если пользователь сам рисует ячейку, лучше отключить это свойство, чтобы отрисовка не дублировалась. В StringGrid набор DefaultDrawing рисует текст в каждой ячейке.
*'''AutoAdvance.''' where the cell cursor will go when pressing enter, or after editing.
+
*'''AutoAdvance.''' [свойство определяет,] куда установится курсор ячейки при нажатии [клавиши] enter или после редактирования.
*'''TabAdvance.''' where the cell cursor will go when pressing Tab or Shift-Tab.
+
*'''TabAdvance.''' [свойство определяет,] куда переместится курсор ячейки при нажатии [клавиши] Tab или Shift-Tab.
*'''ExtendedColSizing.''' If true user can resize columns not just at the headers but along the columns height.
+
*'''ExtendedColSizing.''' Если [значение равно] true, пользователь может изменять размер столбцов не только в заголовках, но и по высоте столбцов.
Other properties that also affect the grids look.
+
 
 +
 
 +
Другие свойства, которые также влияют на то, как выглядят сетки.
  
 
'''Options'''.
 
'''Options'''.
:Options property is a set with some elements to enable diverse functionality but some are related directly with grid's look. This options can be set at designtime or runtime.
+
:Свойство Options - это набор некоторых элементов для обеспечения разнообразной функциональности, но некоторые из них напрямую связаны с внешним видом сетки. Эти параметры можно установить в режиме разработки(designtime) или времени выполнения(runtime).
*'''goFixedVertLine, goFixedHorzLine''' it draws a vertical or horizontal line respectively delimiting cells or columns in fixed area, active by default.
+
*'''goFixedVertLine, goFixedHorzLine''' [свойство] рисует вертикальную или горизонтальную линию, соответственно разграничивая ячейки или столбцы в фиксированной области, по умолчанию [значение равно] active.
*'''goVertLine, goHorzLine''' the same as previous, but for normal browseable area. A grid can be made to simulate a listbox by unsetting both of this elements.
+
*'''goVertLine, goHorzLine''' [свойство аналогично] предыдущему, но для нормальной просматриваемой области. Сетка может быть создана для имитации списка, [если] отключить оба этих элемента.
*'''goDrawFocusSelected''' if this element is enabled a selection background is painted in focused cell in addition to focused dotted rectangle (note this doesn't work yet when goRowSelect option is set, in such case row is always painted as if goDrawFocusSelected is set)
+
*'''goDrawFocusSelected''' если этот элемент включен, фон выделения отрисовывается в сфокусированной ячейке в дополнение к пунктирному прямоугольнику фокуса (обратите внимание, что это не работает, когда установлена опция goRowSelect, в этом случае строка всегда отрисовывается так, как если бы была выбрана опция goDrawFocusSelected)
*'''goRowSelect''' select the full row instead of individual cells
+
*'''goRowSelect''' выбирает [для выделения] полную строку вместо отдельных ячеек
*'''goFixedRowNumbering''' if set, grid will do numbering of rows in first fixed column
+
*'''goFixedRowNumbering''' если [свойство] установлено, сетка будет нумеровать строки в первом фиксированном столбце
*'''goHeaderHotTracking''' if set, the grid will try to show a different look when the mouse cursor is overing any fixed cell. In order for this to work, desired cell zone needs to be enabled with property HeaderHotZones. Try combining this option with property TitleStyle:=tsNative to get themed hot tracking look.
+
*'''goHeaderHotTracking''' если [свойство] установлено, сетка попытается показать другой вид [ячейки, когда] курсор мыши будет находиться над любой фиксированной ячейкой. Для того, чтобы это сработало, желаемая зона ячейки должна быть включена с помощью свойства HeaderHotZones. Попробуйте комбинировать этот параметр с свойством TitleStyle:=tsNative, чтобы получить подсвечивание [соответственно] теме [виджета].
*'''goHeaderPushedLook''' if set, this element enables a pushed look when clicking any fixed cell. The zone of "pushable" cells is enabled using HeaderPusedZones property.
+
*'''goHeaderPushedLook''' если этот параметр установлен, этот элемент включает [эффект] "нажатия" при клике [мышью] любой фиксированной ячейки. Зона "нажатия" ячеек становится доступной при использовании свойства HeaderPusedZones.
  
(write more)
+
(напишите еще)
  
=== Description of grid's drawing process ===
+
=== Описание процесса отрисовки сетки ===
Like other custom controls, the grid is drawn using the paint method. In general terms the grid is drawn by painting all rows, and each row by painting its individual cells.  
+
Как и другие настраиваемые элементы управления, сетка отрисовывается с использованием метода рисования. В общих чертах сетка отрисовывается путем рисования всех строк и каждой строки путем рисования ее отдельных ячеек.
  
The process is as follow:
+
Процесс заключается в следующем:
*First the visible cells area is determined: each row is tested to see if it intersects the canvas clipping region; if it's ok, then the visible area is painted by drawing columns of each row.  
+
* Сначала определяется область видимых ячеек: каждая строка проверяется, пересекает ли она область отсечения холста; если все в порядке, то видимая область окрашивается путем рисования столбцов каждой строки.
*The column and row values are used to identify the cell that is about to be painted and again each column is tested for intersection with the clippling region; if everything is ok, some additional properties like the cell's rectangular extent and visual state are passed as arguments to the DrawCell method.
+
* Значения столбцов и строк используются для идентификации ячейки, которая должна быть окрашена, и снова каждый столбец проверяется на пересечение с областью отсечения; если все в порядке, некоторые дополнительные свойства, такие как прямоугольная протяженность ячейки и визуальное состояние, передаются в качестве аргументов методу DrawCell.
*As the drawing process is running, the visual state of each cell is adjusted according to grid options and position within grid. The visual state is retained in a varible of type TGridDrawState which is a set with following elements:
+
* Когда процесс отрисовки выполняется, визуальное состояние каждой ячейки настраивается в соответствии с параметрами сетки и расположением в сетке. Визуальное состояние сохраняется в виде типа TGridDrawState, который представляет собой набор со следующими элементами:
**''gdSelected'' The cell will have a selected look.
+
**''gdSelected'' У ячейки будет выделенный вид.
**''gdFocused'' The cell will have a focused look.
+
**''gdFocused'' Ячейка будет иметь [рамку] фокуса.
**''gdFixed'' Cell have to be painted with fixed cell look.
+
**''gdFixed'' Ячейка будет нарисована [как] фиксированная.
**''gdHot'' the mouse is over this cell, so paint it with hot tracking look
+
**''gdHot'' [когда] мышь находится над этой ячейкой, то она отрисовывается с подсвечиванием.
**''gdPushed'' the cell is being clicked, paint it with pushed look
+
**''gdPushed'' [когда по] ячейке [кликают мышью], она отрисовывается как нажимаемая [(кликабельная)]
*'''DrawCell.''' The DrawCell method is virtual and may be overriden in descendant grids to do custom drawing. The information passed to DrawCell helps to identify the particular cell is being painted, the physical area ocuppied in screen and its visible status. See DrawCell reference for details. For each cell the following occurs:
+
*'''DrawCell.''' Метод DrawCell является виртуальным и может быть переопределен в унаследованных сетках для выполнения пользовательского рисования. Информация, переданная DrawCell, помогает идентифицировать конкретную ячейку, которая окрашивается, физическую область, занятую экраном, и ее видимый статус. Подробнее см. ссылку DrawCell. Для каждой ячейки происходит следующее:
*'''PrepareCanvas.''' In this method, if the DefaultDrawing property is set, the grid canvas is setup with default properties for brush and font based on current visual state. For several design and runtime properties, the text alignment is set to match programmer selection in custom columns if they exists. If DefaultDrawing is false, brush color is set to clWindow and Font color to clWindowText, the text alignment is set with grids defaultTextStyle property value.
+
*'''PrepareCanvas.''' В этом методе, если установлено свойство DefaultDrawing, холст сетки настраивается со свойствами по умолчанию для кисти и шрифта на основе текущего визуального состояния. Для нескольких свойств дизайн-тайма и времени выполнения выравнивание текста устанавливается в соответствии с выбором программиста в пользовательских столбцах, если они существуют. Если DefaultDrawing [имеет значение] false, цвет кисти устанавливается в clWindow и цвет шрифта в clWindowText, выравнивание текста задается значением defaultTextStyle сетки.
*'''OnPrepareCanvas.''' If the programmer wrote an event handler for OnPrepareCanvas event, it is called at this point. This event can be used for doing simple customization like changing cell's background color, font's properties like color, fontface and style, Text layout like different combinations of left, center, top, bottom, right alignment, etc. Any change made to the canvas in this event would be lost, because the next cell drawing will reset canvas again to a default state. So it's safe doing changes only for particular cell or cells and forget about it for the rest. Using this event sometimes helps to avoid using the OnDrawCell grid event, where users would be forced to duplicate the grid's drawing code.    Todo: samples of what can be made and what to leave for OnDrawCell?...
+
*'''OnPrepareCanvas.''' Если программист написал обработчик событий для события OnPrepareCanvas, он вызывается в этот момент. Это событие можно использовать для простой настройки, например: изменения цвета фона ячейки, свойств шрифта, таких как цвет, шрифт и стиль, расположение текста, как различные комбинации левого, центрального, верхнего, нижнего, правого выравнивания и т.д. Любые изменения, сделанные на холсте [ранее] в этом событии будут потеряны, потому что следующая перерисовка ячейки снова сбросит содержимое холста до состояния по умолчанию. Поэтому безопасно делать изменения только для конкретной ячейки или клеток и забыть об этом для остальных. Использование этого события иногда помогает избежать использования события сетки OnDrawCell, где пользователи будут вынуждены дублировать код отрисовки сетки.<br />''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.
+
*'''OnDrawCell.''' Если не указан обработчик для события OnDrawCell, следующим сетка вызывает метод DefaultDrawCell, который просто рисует фон ячейки, используя текущий цвет и стиль кисти холста. Если обработчик OnDrawCell существует, сетка сначала рисует фон ячейки, но только если установлено свойство DefaultDrawing, [которое, в свою очередь,] вызывает событие OnDrawCell для создания пользовательской отрисовки клетки. Обычно программисты хотят делать пользовательскую отрисовку  только для определенных ячеек и стандартную отрисовку для других; в этом случае они могут ограничить пользовательскую операцию определенной ячейкой или ячейками, просмотрев аргументы ACol, ARow и AState, а для других ячеек просто вызывают метод DefaultDrawCell и позволяют сетке позаботиться об этом.
*'''Text.''' At this point (only for TStringGrid) if DefaultDrawing property is true, the cell's text content is painted.
+
*'''Text.''' В этот момент (только для TStringGrid), если свойство DefaultDrawing [имеет значение] true, рисуется текстовое содержимое ячейки.
*'''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).
+
*'''Grid lines''' Последний шаг для каждой ячейки состоит в том, чтобы нарисовать линии сетки: если параметры сетки goVertLine, goHorzLine, goFixedVertLine и goFixedHorzLine указаны, ячейка сетки рисуется в этой точке. Сетки только со строками или только со столбцами могут быть получены путем изменения этих параметров. Если программист предпочел бы иметь "тематический" вид, то это делается и в этот момент (см. свойство 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.
+
*'''FocusRect.''' Когда все столбцы текущей строки были отрисованы, наступает время рисовать прямоугольник фокуса для текущей выбранной ячейки или для всей строки, если установлена опция goRowSelect.
====Differences with Delphi====
+
====Различия с Delphi====
*In Lazarus TCustomGrid.DrawCell method is not abstract and its default implementation does basic cell background filling.
+
*В Lazarus метод TCustomGrid.DrawCell не является абстрактным, и его реализация по умолчанию выполняет базовое заполнение фона ячейки.
*In Delphi, the cell's text is drawn before entering the OnDrawCell event (see [http://www.freepascal.org/mantis/view.php?id=9619 bug report #9619]).
+
*В Delphi текст ячейки рисуется перед входом в событие OnDrawCell (см. [http://www.freepascal.org/mantis/view.php?id=9619 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.
+
Расположение текущей (сфокусированной) ячейки (или строки) сетки может быть изменено с помощью клавиатуры, мыши или кода. Чтобы успешно сменить фокус ячейки на другую позицию, мы должны проверить целевую позицию, чтобы увидеть, разрешено ли ей получать фокус ячейки. При использовании клавиатуры свойство AutoAdvance выполняет часть процесса, обнаруживая, что должно быть следующей сфокусированной ячейкой. При использовании кликов мыши или перемещения [посредством] кода фокус не будет перемещаться из текущей ячейки, если целевой ячейке не разрешено получать фокус.
  
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.
+
Сетка вызывает функцию SelectCell, чтобы увидеть, является ли ячейка фокусируемой: если эта функция возвращает true, то целевая ячейка, идентифицированная с аргументами aCol и aRow, является фокусируемой (текущая реализация TCustomGrid просто возвращает true). TCustomDrawGrid и, следовательно, TDrawGrid и TStringGrid переопределяют этот метод, чтобы сначала проверить, [является ли] ячейка шире, чем 0; обычно вы не хотите, чтобы ячейка с нулевой шириной 0 выбиралась, [другими словами, вы хотите,] чтобы ячейка с этими характеристиками пропускалась автоматически в процессе поиска подходящей ячейки. Другая вещь, которую переопределяет метод SelectCell, - это вызов настраиваемое пользователем событие OnSelectCell: это событие принимает координаты ячейки как аргументы и всегда возвращает значение по умолчанию 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.  
+
Как только [становится] известно, что ячейка [является] фокусируемой, и мы уверены, что движение будет иметь место, сначала вызывается метод BeforeMoveSelection; это по очереди вызывает событие OnBeforeSelection. Аргументами этого метода являются координаты для новой сфокусированной ячейки. В этот момент [также будет] скрыт любой видимый редактор. Слово "перед" означает, что выбор еще не изменен, и к текущим сфокусированным координатам можно получить доступ по свойствам grid.Col и grid.Row.
  
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).
+
После этого меняются координаты внутренней сфокусированной ячейки, а затем вызывается метод MoveSelection; цель этого метода заключается в том, чтобы инициировать событие OnSelection, если оно установлено (это уведомление, что  сфокусированная ячейка к этому времени уже изменилась и координаты ячейки теперь доступны через свойства grid.row и grid.col).
  
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.
+
Обратите внимание, что неэффективно использовать событие OnSelectCell для обнаружения изменений фокуса ячейки, так как это событие будет срабатывать несколько раз даже для одной и той же ячейки в процессе поиска подходящей ячейки. Для этого лучше использовать события OnBeforeSelection или OnSelection.
  
====Differences with Delphi====
+
====Различия с 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.
+
*Поведение SelectCell и OnSelectCell, вероятно, различны - в действительности не могу судить о различиях. В Lazarus они используются в таких функциях, как AutoAdvance, которая, насколько я знаю, не существует в Delphi.
  
=== When built-in properties are not enough: derived grids ===
+
=== Когда встроенных свойств недостаточно: наследники сетки ===
Derived grids usually have to override the following methods:<br>
+
Наследники сетки обычно должны переопределять следующие методы:<br />
DrawAllRows: Draws all visible rows.<br>
+
DrawAllRows: Отрисовывать все видимые строки<br />
DrawRow: Draws all cells in a row.<br>
+
DrawRow: Отрисовывать все ячейки в строке.<br />
DrawRow draws all cells in the row by first checking if cell is within clipping region, and only draws the cell if it is.<br>
+
DrawRow отрисовывает все ячейки в строке, предварительно проверив, находится ли ячейка в области отсечения, и только [потом] рисует ячейку, если она есть.<br />
DrawCell:<br>
+
DrawCell:<br />
DrawCellGrid:<br>
+
DrawCellGrid:<br />
DrawCellText:<br>
+
DrawCellText:<br />
DrawFocusRect:<br>
+
DrawFocusRect:<br />
(write me).<br>
 
  
=== Save and Retrieve Grid Content ===
+
(напишите мне).<br />
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).
 
  
 +
=== Что происходит в методе <tt>TCustomGrid.Paint</tt>? ===
 +
В следующем списке показан внутренний порядок вызовов методов для отрисовки <tt>TCustomGrid</tt> (или потомков). Каждый элемент в списках представляет вызовы методов во время операции отрисовки. Это должно помочь найти правильный точку для изменения поведения, когда дело доходит до [написания] наследников <tt>TCustomGrid</tt>.
  
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:
+
*'''<tt>DrawEdges</tt>''': Рисует внешнюю границу сетки.
 +
*'''<tt>DrawAllRows</tt>''' (virtual): Рисует все строки в видимой части сетки. Это единственный метод, вызываемый в процессе отрисовки, который объявлен ''<tt>virtual</tt>''.
 +
**'''<tt>DrawRow</tt>''' (virtual): Вызывается для каждой строки внутри текущего окна просмотра.
 +
***'''<tt>DrawCell</tt>''' (virtual): Сначала вызывается для каждой "нормальной" (т.е. не фиксированной) ячейки в строке.
 +
****'''<tt>PrepareCanvas</tt>''' (virtual): Устанавливает стили рисования холста в соответствии с визуальными свойствами текущей ячейки.
 +
****'''<tt>DrawFillRect</tt>''': Рисует фон ячейки со стилями, установленными в <tt>PrepareCanvas</tt>.
 +
****'''<tt>DrawCellGrid</tt>''' (virtual): Рисует линии границы ячейки.
 +
***'''<tt>DrawFocusRect</tt>''' (virtual): В <tt>TCustomGrid</tt> этот метод ничего не делает. В наследниках сетки этот метод используется для рисования прямоугольника фокуса внутри активной ячейки.
 +
***'''<tt>DrawCell</tt>''' (virtual): (см. выше) Вызывается для каждой фиксированной ячейки в видимом окне просмотра строки.
 +
*'''<tt>DrawColRowMoving</tt>''': активен только при перемещении столбца или строки. Этот метод рисует линию, которая указывает новую позицию строки/столбца.
 +
*'''<tt>DrawBorder</tt>''': Если необходимо (<tt>Flat=TRUE</tt> и <tt>BorderStyle=bsSingle</tt>), это отрисовывает внутреннюю линию границы.
 +
==== Дополнительные методы для рисования <tt>TCustomGrid</tt> ====
 +
Эти методы объявлены и (частично) реализованы в <tt>TCustomGrid</tt>, но они не вызываются непосредственно отсюда. Они используются классами потомков для отрисовки содержимого ячейки.
  
soDesign:     Save & load ColCount,RowCount,FixedCols,FixedRows,
+
*'''<tt>DrawCellText</tt>''' (virtual): Записывает/отрисовывает текст, который передается в качестве параметра в ячейку. Текст отформатирован с использованием стилей, которые активны в <tt>Canvas.TextStyle</tt> (см. также <tt>PrepareCanvas</tt>).
              ColWidths, RowHeights and Options (TCustomGrid)
+
*'''<tt>DrawThemedCell</tt>''' (virtual): Используется только для фиксированных ячеек и если <tt>TitleStyle=tsNative</tt>. [Метод] отрисовывает фон ячейки, используя <tt>ThemeServices</tt>.
soPosition:   Save & load Scroll position, Row, Col and Selection (TCustomGrid)
+
*'''<tt>DrawColumnText</tt>''' (virtual): Используется только для ячеек заголовков столбцов.
soAttributes: Save & load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)
+
**'''<tt>DrawColumnTitleImage</tt>''': Если сетке сопоставлен <tt>TitleImageList</tt> и <tt>Columns.Title[x].Title.ImageIndex</tt> содержит допустимое значение, [указанное в <tt>ImageIndex</tt>] изображение отрисовывается внутри ячейки.
soContent:   Save & load Text (TCustomStringGrid)
+
**'''<tt>DrawCellText</tt>''' : (см. выше)
 +
*'''<tt>DrawTextInCell</tt>''' (virtual): Используется для "нормальных" (т.е. не фиксированных) ячеек. В <tt>TCustomGrid</tt> этот метод ничего не делает. В наследуемых сетках этот метод используется для отрисовки содержимого ячеек. В <tt>TStringGrid</tt> метод вызывает <tt>DrawCellText</tt> (см. выше).
  
soAll:       set of all options (soAll:=[soDesign,soPosition,soAttributes,soContent];)
+
==== Методы рисования, введенные <tt>TCustomDrawGrid</tt> ====
 +
Как вы можете видеть, базовый класс <tt>TCustomGrid</tt> не выводит никакого содержимого в ячейки. Это делается в '''<tt>TCustomDrawGrid</tt>'''.
 +
'''<tt>TCustomDrawGrid</tt>''' переопределяет метод <tt>DrawCell</tt> следующим образом:
 +
*'''<tt>DrawCell</tt>''' (override):  
 +
**'''<tt>PrepareCanvas</tt>''': вызывает унаследованный метод из '''<tt>TCustomGrid</tt>'''.
 +
**'''<tt>DefaultDrawCell</tt>''' (virtual): Этот метод вызывается только в том случае, если обработчик события для <tt>OnDrawCell</tt> НЕ назначен.
 +
***'''<tt>DrawFillRect / DrawThemedCell</tt>''' (von <tt>TCustomGrid</tt>): Рисует фон ячейки. Если <tt>TitleStyle=tsNative</tt>, фон фиксированных ячеек рисуется с помощью <tt>DrawThemedCell</tt>.
 +
***'''<tt>DrawCellAutonumbering</tt>''' (virtual): Если [свойство] <tt>goFixedAutonumbering</tt> [включено] в [набор] <tt>Options</tt>, номера строк отображаются в первом фиксированном столбце [с помощью метода] <tt>TCustomGrid.DrawCellText</tt>.
 +
***'''<tt>DrawColumnText</tt>''' (из <tt>TCustomGrid</tt>): Вызывается только для ячеек заголовков столбцов (см. «Дополнительные методы отрисовки в <tt>TCustomGrid</tt>»).
 +
***'''<tt>DrawTextInCell</tt>''' (из <tt>TCustomGrid</tt>): Вызывается только для "нормальных" (т.е. не фиксированных) ячеек (см. «Дополнительные методы отрисовки в <tt>TCustomGrid</tt>»).
 +
**'''<tt>DrawCellGrid</tt>''' (virtual): Рисует границы ячейки.
  
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.
+
=== Сохранение и получение содержимого сетки ===
 +
Методы '''SaveToFile''' и '''LoadFromFile''' позволяют сетке сохранять и извлекать ее макет и данные в/из файла формата XML. TStringGrid, как унаследованный от TCustomStringGrid, также имеет возможность "экспортировать" и "импортировать" свой контент в файл формата Comma Separated Values, наиболее известный как CSV-файлы. Это описано в ссылке методов [[Grids_Reference_Page/ru#.D0.BF.D1.80.D0.BE.D1.86.D0.B5.D0.B4.D1.83.D1.80.D0.B0_SaveToCSVFile.28AFileName:_string.3B_ADelimiter:Char.3D.27.2C.27.3B_WithHeader:boolean.3Dtrue.29.3B|'''SaveToCSVFile''']] и [[Grids_Reference_Page/ru#.D0.BF.D1.80.D0.BE.D1.86.D0.B5.D0.B4.D1.83.D1.80.D0.B0_LoadFromCSVFile.28AFileName:_string.3B_ADelimiter:Char.3D.27.2C.27.3B_WithHeader:boolean.3Dtrue.29.3B|'''LoadFromCSVFile''']].
  
The soAttributes option is not used in Lazarus standard grids, is there for derived grids.
+
Тип информации, которая может быть сохранена, а затем извлечена при использовании '''SaveToFile''' и '''LoadFromFile''', определяется свойством SaveOptions (тип TSaveOptions), которое представляет собой набор параметров, описанных ниже:
 +
soDesign:    Сохраняет и загружает ColCount,RowCount,FixedCols,FixedRows,
 +
              ColWidths, RowHeights и Options (TCustomGrid)
 +
soPosition:  Сохраняет и загружает Scroll position, Row, Col и Selection (TCustomGrid)
 +
soAttributes:  Сохраняет и загружает Colors, Text Alignment и Layout, и т.д. (TCustomDrawGrid)
 +
soContent:    Сохраняет и загружает текст (TCustomStringGrid)
 +
soAll:        набор всех параметров (soAll:=[soDesign,soPosition,soAttributes,soContent];)
 +
Не все параметры применимы ко всем типам сеток, например, soContent не применяется к наследникам сеток TCustomGrid, таким как TDrawGrid, поскольку такая сетка не имеет понятия "контент". TStringGrid - это специальный вид сетки, который "знает", как обрабатывать строки, и может использовать параметр soContent, если он указан.
  
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.  
+
Опция soAttributes не используется в стандартных сетках Lazarus, [она предназначена] для наследников сеток.
  
For a TStringGrid the default SaveOptions value is [soContent], for other kind of grids, SaveOptions is the empty set.
+
При использовании '''LoadFromFile''' сетка также использует свойство SaveOptions, чтобы знать, какая информация должна извлекаться из файла, поэтому вполне возможно указать SaveOptions:=[soDesign,soContent] при сохранении и только SaveOptions:=[soContent] при загрузке.
  
'''Note:'''
+
Для TStringGrid значение SaveOptions по умолчанию - [soContent], для других видов сеток SaveOptions - это пустой набор.
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:
 
  
 +
'''На заметку:'''
 +
Одна из распространенных проблем при сохранении и извлечении данных сетки возникает [тогда], когда пользователь определяет свойство SaveOptions перед [методом] '''SaveToFile''', но не раньше [вызова метода] '''LoadFromFile'''. При использовании '''LoadFromFile''' через некоторое время после '''SaveToFile''', свойство SaveOptions устанавливается должным образом, но если '''LoadFromFile''' выполняется при следующем запуске программы, свойство SaveOptions может настроиться неправильно, по этой причине рекомендуется всегда указывать свойство SaveOptions непосредственно перед [вызовом метода] '''LoadFromFile''' или делать это глобально при запуске программы, как в следующем примере:
 
----
 
----
 +
'''Пример:'''
 +
# Сначала перейдите в меню "File -> New -> Application";
 +
# Бросьте пустой TStringGrid в форму;
 +
# Бросьте TButton и TOpenDialog на форму;
 +
# Добавьте событие OnCreate для формы;
 +
# Добавьте событие OnClick для кнопки.
 +
  
'''Example:'''
+
<syntaxhighlight lang=pascal>unit Unit1;  
# First, go to menu "File -> New -> Application";
 
# Put an empty TStringGrid on the form;
 
# Put a TButton and TOpenDialog on the form;
 
# Add the event OnCreate for the form;
 
# Add the event OnClick for the button.
 
<syntaxhighlight>unit Unit1;  
 
  
 
{$mode objfpc}{$H+}
 
{$mode objfpc}{$H+}
Line 533: Line 604:
 
procedure TForm1.Form1Create(Sender: TObject);
 
procedure TForm1.Form1Create(Sender: TObject);
 
begin
 
begin
  //sets the SaveOptions at creation time of the form
+
  //устанавливаем SaveOptions во время создания формы
 
  stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
 
  stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
 
end;
 
end;
Line 540: Line 611:
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
 
begin
 
begin
  //Ask if thew Execute method of the OpenDialog was launched
+
  //Спрашиваем, был ли запущен метод Execute [компонента] OpenDialog  
  //when this occurs, the user selects an XML file to Load
+
  //когда это происходит, пользователь выбирает файл XML для загрузки
  //wich name was stored in the FileName prop.
+
  //имя, которое было сохранено в объявлении FileName.
  
 
  if opendialog1.Execute then
 
  if opendialog1.Execute then
  Begin
+
  begin
   //Clear the grid
+
   //Очищаем сетку
 
   StringGrid1.Clear;
 
   StringGrid1.Clear;
   //Load the XML
+
   //Загружаем XML
 
   StringGrid1.LoadFromFile(OpenDialog1.FileName);
 
   StringGrid1.LoadFromFile(OpenDialog1.FileName);
   //Refresh the Grid
+
   //Обновляем сетку
 
   StringGrid1.Refresh;
 
   StringGrid1.Refresh;
  End;
+
  end;
 
end;
 
end;
  
Line 561: Line 632:
  
 
----
 
----
'''The sample xml file:'''
+
'''Образец xml-файла:'''
(Copy the text below into a txt file. Don't forget put the xml header :-))
+
(Скопируйте текст ниже в текстовый файл. Не забудьте поставить заголовок xml :-))
 
<syntaxhighlight lang="xml"><?xml version="1.0"?>
 
<syntaxhighlight lang="xml"><?xml version="1.0"?>
 
<CONFIG>
 
<CONFIG>
Line 608: Line 679:
 
     </content>
 
     </content>
 
   </grid>
 
   </grid>
</CONFIG></syntaxhighlight>
+
</CONFIG>
----
+
</syntaxhighlight>
--[[User:Raditz|Raditz]] 21:06, 11 Jan 2006 (CET) from '''ARGENTINA'''
 
  
=== Grid Cell Editors ===
+
=== Редактор ячеек сетки ===
 +
В сетке используются редакторы ячеек для изменения содержимого ячеек.
  
The grid uses cell editors to change the content of cells.  
+
Для специализированной сетки, такой как TStringGrid, редактор является обычным однострочным текстовым контролом, но иногда желательно иметь другие средства для ввода информации. Например:
  
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:
+
#показать диалоговое окно открытия файла, чтобы найти местоположение файла, чтобы пользователь не вводил полный путь вручную
 +
#если текст в ячейке представляет дату, [вызвать] всплывающий календарь, чтобы мы могли легко выбрать конкретную дату.
  
#show the open file dialog to find the location of a file so the user doesn't have to type the full path manually
+
Иногда информация, которую пользователь должен ввести в ячейку, ограничена определенным списком слов; в этом случае ввод информации напрямую может привести к ошибкам и, возможно, потребуется выполнить процедуру проверки. Мы можем избежать этого, используя редактор ячейки, который представляет пользователю список, содержащий только легальные значения.
#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.  
+
Это также относится к общим сеткам типа TDrawGrid, где пользователю нужна какая-то структура для хранения данных, которые будут отображаться в сетке. В этой ситуации информация, введенная в редакторе ячейки, обновляет внутреннюю структуру, чтобы отображать изменения в сетке.
  
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.
+
==== Встроенный редактор ячейки ====
 +
Модуль grids.pas уже включает некоторые из наиболее используемых редакторов ячеек, готовых для использования в сетках. Также возможно создавать новые редакторы ячеек (настраиваемые редакторы ячеек), если встроенные редакторы не подходят для конкретной задачи.
  
==== Builtin cell editors ====
+
Встроенные редакторы ячеек - это Button, Edit и Picklist.
  
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 a custom column and selecting the ButtonStyle property of the column''' (''использование настраиваемого столбца и выбор свойства ButtonStyle для столбца''). В этом методе пользователь может выбрать стиль редактора, который будет показан. Доступными значениями являются: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn.
 +
#'''Using OnSelectEditor grid event''' (''Использование события сетки OnSelectEditor''). Здесь пользователь указывает в параметре Editor, какой редактор использовать для ячейки, обозначаемой столбцом aCol и строкой ARow наследуемой сетки TCustomDrawGrid или TColumn в TCustomDBGrid. Для этой цели существует полезная public-функция сетки EditorByStyle(), которая принимает в качестве параметра одно из следующих значений: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn. Этот метод имеет приоритет над первым при использовании пользовательских столбцов. Редактор пользовательских ячеек можно указать здесь (см. главу 5 [http://www.freepascal.org/~michael/articles/grids/grids.pdf этого документа] (автор Michaël Van Canneyt), в которой объясняется, как правильно реализовывать пользовательский редактор). Это событие также позволяет настраивать редактор со значениями, специфичными для ячейки, строки или столбца (например, пользовательское всплывающее меню для редактора ячейки).
  
==== Using cell editors ====
+
Установка свойства ''ButtonStyle'' работает только в том случае, если столбец создан с помощью инструкции <tt>StringGrid1.Columns.Add</tt>. Использование выражения типа <tt>StringGrid1.ColCount:=X;</tt> вызовет исключение.
 +
Установка свойства ButtonStyle может быть выполнена с помощью подобного кода:
  
Users can specify what editor will be used for a cell using one of two methods.
+
<syntaxhighlight lang=pascal>
 
+
if ColCB< StringGrid1.Columns.Count
#'''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.
+
   then StringGrid1.Columns.Items[ColCB].ButtonStyle:=cbsCheckboxColumn;
#'''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.
+
</syntaxhighlight>
 
 
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:
 
<syntaxhighlight>if ColCB< StringGrid1.Columns.Count
 
   then StringGrid1.Columns.Items[ColCB].ButtonStyle:=cbsCheckboxColumn;</syntaxhighlight>
 
 
 
==== 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.
+
==== Описание стилей редактора ====
 +
Ниже приводится описание стилей редактора. Они являются значениями перечисляемого типа TColumnButtonStyle и поэтому имеют префикс 'cbs'. Этот тип использовался, чтобы оставаться совместимым с DBGrid Delphi.
  
 
*'''cbsAuto'''
 
*'''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.
+
:Это стиль редактора по умолчанию для сеток-наследников TCustomGrid. Фактический класс редактора, который будет использоваться для редактирования содержимого ячейки, зависит от нескольких факторов. Для TCustomGrids он использует класс TStringCellEditor, полученный из TCustomMaskEdit. Этот редактор специализирован для редактирования строк одной строки. Затем он используется по умолчанию в TStringGrid и TDrawGrid. При использовании пользовательских столбцов, если программист заполнил свойство PickList столбца, [редактор] ведет себя так, как если бы был установлен стиль редактора cbsPickList. Для TCustomDBGrid, который имеет поле типа boolean, он ведет себя так, как если бы был указан стиль редактора cbsCheckBoxColumn. Это рекомендуемое значение для Custom Cell Editors. TODO: сослаться на OnEditingDone.
 
*'''cbsEllipsis'''
 
*'''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, чтобы определить, когда пользователь нажал кнопку, и выполнить любое действие, запрограммированное для такой ячейки. Например, программист может использовать этот стиль редактора, чтобы открыть диалог календаря, чтобы пользователь мог легко выбрать конкретную дату. Другие возможности могут заключаться в том, чтобы показать диалог открытия файла для поиска файлов, калькулятор, чтобы пользователь мог ввести числовой результат вычислений и т.д.
  
: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.
+
:OnEditButtonClick - это просто уведомление, чтобы узнать, в какой ячейке нажата кнопка, взглянув на свойства grid.Row и grid.Col.
  
: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.
+
:DBGrid имеет определенные свойства для извлечения активного столбца или поля, и, поскольку это событие происходит в активной записи, оно может обновлять информацию в активном поле.
  
:This editor style is implemented using TButtonCellEditor, a direct descendant of TButton.
+
:Этот стиль редактора реализован с использованием TButtonCellEditor, прямого потомка TButton.
 
*'''cbsNone'''
 
*'''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'''
 
*'''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.
+
:Используется для представления пользователю списка значений, которые могут быть введены. Этот стиль редактора реализован с использованием TPickListCellEditor, компонента, полученного из TCustomComboBox. Список отображаемых значений заполняется одним из двух способов в зависимости от метода, используемого для выбора стиля редактора.
:#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]
+
:#При использовании настраиваемых столбцов программисты могут вводить список значений, используя свойство PickList столбца. [ДЛЯ НОВИЧКОВ: TODO: точная процедура для редактирования списка]
:#In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList). [[Grids Reference Page#Example: Working with Picklist, How to make it read only and How to fill it at run time.|See here for an example]]
+
:#В OnSelectEditor программисты получают экземпляр TPickListCellEditor, используя функцию EditorByStyle(cbsPickList). [[Grids_Reference_Page/ru#.D0.9F.D1.80.D0.B8.D0.BC.D0.B5.D1.80:_.D0.A0.D0.B0.D0.B1.D0.BE.D1.82.D0.B0_.D1.81_Picklist.2C_.D0.9A.D0.B0.D0.BA_.D1.81.D0.B4.D0.B5.D0.BB.D0.B0.D1.82.D1.8C_.D0.B5.D0.B3.D0.BE_.D1.82.D0.BE.D0.BB.D1.8C.D0.BA.D0.BE_.D0.B4.D0.BB.D1.8F_.D1.87.D1.82.D0.B5.D0.BD.D0.B8.D1.8F_.D0.B8_.D0.9A.D0.B0.D0.BA_.D0.B7.D0.B0.D0.BF.D0.BE.D0.BB.D0.BD.D0.B8.D1.82.D1.8C_.D0.B5.D0.B3.D0.BE_.D0.B2_run_time.|См. пример здесь]]
: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.
+
:Значение в сетке TStringGrid будет автоматически отображать выбранное значение. При необходимости программист может определить момент, когда значение выбрано, путем написания обработчика для события OnPickListSelect сетки, поэтому могут быть предприняты дополнительные шаги (например, для обработки нового значения). TODO: связать с OnEditingDone.
 
*'''cbsCheckboxColumn'''
 
*'''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).
+
:Это может быть полезно, когда содержимое данных, связанное с столбцом, ограничено парой значений, например да-нет, true-false, вкл-выкл, 1-0 и т.д. Вместо того, чтобы принуждать пользователя вводить значения для данных такого типа в StringCellEditor или выбирать один из списка, cbsCheckboxColumn используется для изменения данных столбца с помощью представления флажка, которое пользователь может переключать, используя щелчок мыши или нажимая клавишу SPACE (если столбец, содержащий этот флажок, а также StringGrid доступны для редактирования).
: Getting or setting the value of the checkbox in a cell is done the following way:
+
:Получение или установка значения флажка в ячейке осуществляется следующим образом:
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
StringGrid1.Cell[x,y]:='Z';
 
StringGrid1.Cell[x,y]:='Z';
 
</syntaxhighlight>
 
</syntaxhighlight>
: 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.
+
:где Z заменяется на '''0''' для ''Unchecked'', '''1''' для ''Checked'' и пустую строку для ''Grayed''. Обратите внимание, что любое значение (строка), отличное от ''0'' и ''1'', будет отображаться как серый сheckbox.
 +
 
 +
:Если для свойства столбцов ButtonStyle установлено значение cbsAuto, и DBGrid обнаруживает, что поле, связанное с столбцом, является логическим полем, то сетка автоматически использует этот стиль редактора. Этот автоматический выбор может быть отключен или активирован с использованием свойства OptionGxtra DBGrid; установка элемента dgeCheckboxColumn в false отключает эту функцию.
 +
 
 +
:Значения, используемые для распознавания ''checked'' или ''unchecked'' состояний, задаются в свойствах столбца ValueChecked и ValueUnchecked [соответственно].
  
:The values that are used to recognize the checked or unchecked states are set in a column's properties ValueChecked and ValueUnchecked.
+
:В любой момент значение поля может находиться в одном-трех состояниях: Unchecked, Checked или Grayed. Внутренне эти состояния идентифицируются следующими значениями типа TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked и gcbpGrayed.
  
: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.
+
:Этот стиль редактора не использует реальные компоненты TCheckbox для обработки взаимодействия с пользователем: визуальное представление задается тремя встроенными растровыми изображениями, которые соответствуют возможным состояниям флажка. Использованные растровые изображения можно настроить, написав обработчик для события OnUserCheckboxBitmap [компонента] DBGrid; обработчик этого события получает состояние флажка в параметре CheckedState типа TDBGridCheckboxState и параметр растрового изображения, который программист может использовать для указания пользовательских растровых изображений.
  
: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'''
 
*'''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.
+
:Этот стиль редактора используется для отображения кнопки в каждой ячейке в столбце. Как и в случае cbsCheckboxColumn, этот редактор не использует реальные кнопки, внешний вид определяется текущей темой widgetset.
 +
 
 +
:Пользователь знает, какая конкретная кнопка была нажата благодаря обработчику OnEditButtonClick сетки и проверке столбца и строки. Обратите внимание, что в этом конкретном случае столбец и строка сетки идентифицируют не текущую выбранную ячейку, а ячейку нажатой кнопки. Как только событие OnEditButtonClick будет обработано, и если пользователь не изменил столбец или строку в этом обработчике, сетка автоматически сбрасывает столбец и строку, чтобы отобразить выбранную ячейку. Пока обработчик OnEditButtonClick текущего выделения сетки доступен в grid.Selection, который является свойством TRect, левое и правое [значения] представляют [собой] индексы столбцов, а верхний и нижний - индексы строк.
 +
 
 +
:Заголовок кнопки - соответствующая строка ячейки.
 +
 
 +
===Редактирование сеток===
 +
Эффект редактирования отличается в зависимости от того, какая сетка используется, например, TStringGrid сохраняет отредактированный текст внутри, а TDBGrid влияет на записи в наборе данных. TDrawGrid "не знает", что делать с отредактированным текстом: если программист не контролирует его, [отредактированный текст] просто отбрасывается.<br />Для TDrawGrid (хотя это должно работать для всех классов сетки), чтобы получить доступ к отредактированному тексту, программист может использовать событие '''OnSetEditText''', которое запускается каждый раз, когда пользователь что-то изменяет в редакторе, параметры ''aCol'' и ''Row'' этого события определяют редактируемую ячейку, параметр ''value'' содержит [редактируемый] текст -  это рекомендуемый метод. Другой способ доступа к отредактированному тексту - прием его непосредственно из редактора после завершения процесса редактирования с использованием события '''OnEditingDone'''. Это можно сделать, обратившись к внутреннему редактору, используемому для редактирования, в данном случае, по умолчанию "String Cell Editor" [("Редактору строк ячеек")]. Для этого существует два метода: первый - с использованием события '''OnSelectEditor''', где параметр ''Editor'' - это экземпляр, который будет использоваться для редактирования ячейки; вы должны сохранить этот экземпляр в переменной, например TheEditor (типа ''TStringCellEditor''), для последующего использования в '''OnEditingDone'''. Второй вариант - использование метода '''EditorByStyle''' сетки, этот метод принимает "стиль редактора" в качестве параметра и возвращает экземпляр редактора, соответствующего этому стилю. В нашем случае, зная, что стиль cbsAuto возвращает редактор ячейки по умолчанию, в обработчике события '''OnEditingDone''' можно использовать непосредственно TheEditor:=Grid.EditorByStyle(cbsAuto). Затем вы можете получить текст с помощью ''TheEditor.Text''. Если вы [будете] использовать этот метод, обратите внимание на то, что "великая сила несет большую ответственность".
 +
 
 +
====Параметры и свойства, которые влияют на редактирование====
 +
Поведение редактирования контролируется рядом свойств и параметров, фактически любая настройка или параметры, которые влияют на редактирование, требуют редактирования сетки или такая настройка может быть проигнорирована.<br />
 +
При запуске редактируемое состояние сетки различно [в разных видах сетках], поскольку редактирование TDrawGrid и TStringGrid отключено, для TDbGrid включено по умолчанию.<br />
 +
Свойство '''Options''' имеет несколько элементов, которые касаются редактирования, они описаны ниже:<br />
 +
*'''goEditing, dgEditing (в DbGrid)'''. Эта опция изменяет редактируемое состояние сетки, ее можно изменить во время выполнения, поскольку она проверяет, когда начнется редактирование.
 +
*'''goAlwaysShowEditor'''. Обычно редактор скрыт и становится видимым только тогда, когда это необходимо. С помощью этой опции редактор будет отображаться все время, если сетка не редактируется, этот параметр игнорируется, и редактор всегда скрыт.
 +
 
 +
===Хинты (подсказки) ячейки===
 +
В дополнение к стандартным хинтам большинства элементов управления потомки TCustomGrid могут отображать специальную подсказку для ячейки, над которой курсирует мышь. Эта функция управляется параметрами сетки '''goCellHints''' и '''goTruncCellHints''' (или '''dgCellHints''' и '''gdTruncCellHints''' для DBGrid соответственно):
  
: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.
+
* '''goCellHints''' оценивает событие '''OnGetCellHint''' для извлечения отображаемого текста для ячейки, указанной переданными параметрами. В случае TDrawGrid и TStringGrid задаются индексы столбцов и строк, тогда как в случае TDBGrid текст ячейки может быть извлечен из поля, назначенного указанному объекту столбца.
 +
* '''goTruncCellHints''' отображает подсказку только в том случае, если текст не помещается [полностью] в ячейку и [поэтому] усечен; текст подсказки - это не обрезанный ячейкой текст. В Lazarus 1.9+ этот текст передается событию '''OnGetCellHint''' для дальнейшего уточнения (например, отображает весь текст memo в подсказке). В случае обрезания ячейкой текстов рекомендуется также активировать опцию '''goCellEllipsis''' (или '''dgCellEllipsis''' для DBGrid), которая добавляет «...» к обрезанному ячейкой тексту.
  
:The button's caption is the corresponding cell string.
+
Если сетка имеет значение в своем стандартном свойстве '''Hint''', тогда свойство '''HintPriority''' можно использовать для управления тем, как все эти тексты подсказок могут быть объединены в одно всплывающее окно:
 +
* '''chpAll''' помещает стандартную подсказку в первую строку, подсказку, определяемую событием OnGetCellHint во вторую строку, и подсказку для усеченных ячеек в третью строку
 +
* '''chpAllNoDefault''' отображает подсказки только ячейки, заданные событием OnGetCellHint, и усеченными ячейками.
 +
* '''chpTruncOnly''' отображает подсказки только для усеченных ячеек; это настройки по умолчанию.
  
===Editing grids===
+
В дополнение, стандартное свойство '''ShowHint''' сетки должно быть установлено в true, чтобы отображать подсказки на ячейки.
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.<br>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.<br>
 
At start the editable state of the grids is different, for TDrawGrid and TStringGrid editing is disabled, for TDbGrid is enabled by default.<br>
 
The '''Options''' property has several items that deal with editing, they are described below:<br>
 
*'''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:
+
Фокусировка ячейки в TStringGrid очень проста. Обратите внимание, что подсчет начинается с нуля, а не 1. Таким образом, чтобы перевести фокус на строку 10, столбец 9, выполните:
  
<syntaxhighlight>StringGrid1.row := 9;
+
<syntaxhighlight lang=pascal>StringGrid1.row := 9;
 
StringGrid1.col := 8;</syntaxhighlight>
 
StringGrid1.col := 8;</syntaxhighlight>
  
===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===
+
см. lazarus/examples/gridexamples/gridcelleditor/gridcelleditor.lpi (начиная с laz 1.2)
You can, of course, use another control instead of a TMemo. Adjust to taste.
 
Adapted from [http://forum.lazarus.freepascal.org/index.php?topic=3640.0] (simplified to use the SelectedFieldRect property, see [http://forum.lazarus.freepascal.org/index.php/topic,8713.msg43288.html#msg43288])
 
  
* 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.
+
===Пример: Как установить memo-редактор для dbgrids===
 +
Разумеется, вы можете использовать другой контрол вместо TMemo. Откорректируйте [на свой] вкус.
 +
Адаптирован из [http://forum.lazarus.freepascal.org/index.php?topic=3640.0] (упрощенное использование свойства SelectedFieldRect, см. [http://forum.lazarus.freepascal.org/index.php/topic,8713.msg43288.html#msg43288])
  
* In the OnSelectEditor event put the following code - adapt if you don't want to edit logical column 3, physical column 4:
+
* Поместите элемент управления memo (или любой другой контрол, который вы хотите) на свою форму, установите любые свойства, которые вы хотите, и установите значение visible:= false. Этот [контрол] будет использоваться при редактировании ячейки сетки. В этом примере мы будем использовать GridCellMemo.
<syntaxhighlight>
+
* В событии OnSelectEditor поместите следующий код - адаптируйте, если вы не хотите редактировать логический столбец 3, физический столбец 4:
  if (Column.DesignIndex = 3) then
+
<syntaxhighlight lang=pascal>
 +
if (Column.DesignIndex = 3) then
 
   begin
 
   begin
 
       GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
 
       GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
Line 725: Line 808:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
* Suppose your datasource is called Datasource1 and DBGrid is called ResultsGrid. Then, in the OnEditingDone event for the GridCellMemo put the following:
+
* Предположим, что ваш источник данных называется Datasource1, а DBGrid называется ResultsGrid. Тогда в событии OnEditingDone для GridCellMemo введите следующее:
(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.)
+
(В исходном сообщении форума используется событие OnChange, которое будет срабатывать при каждом изменении содержимого. Использование OnEditingDone запускается только после того, как пользователь закончил свои правки.)
<syntaxhighlight>
+
 
   if not(Datasource1.State in [dsEdit, dsInsert]) then
+
<syntaxhighlight lang=pascal>
    Datasource1.Edit;
+
   if not(Datasource1.State in [dsEdit, dsInsert]) then Datasource1.Edit;
 
      
 
      
 
   Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;
 
   Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===Example: How to add a button editor===
+
'''Осторожно''' Использование обычного элемента управления в качестве настраиваемого редактора имеет множество недостатков: при изменении размеров столбцов или скроллинге сетки он не будет изменять размер или положение, вам придется позаботиться о том, чтобы изменить данные в сетке (как объяснялось выше), он не работает должным образом с DbGrids и препятствует нормальной навигации по сетке с помощью клавиатуры.
 +
Сетка взаимодействует с редактором, используя некоторые специальные сообщения сетки, поэтому лучше [выделить этот контрол в] подкласс для упорядочивания управления этими сообщениями, как описано в пятой главе [http://www.freepascal.org/~michael/articles/grids/grids.pdf этого документа].
 +
 
 +
'''Осторожно''' Если у вас более одной сетки в одной и той же форме, используйте разные настраиваемые редакторы для каждой из них: если вы [будете] использовать один и тот же элемент управления, начнут происходить плохие вещи с того момента, когда обе сетки попытаются использовать его одновременно.
  
<syntaxhighlight>// Conditionally show button editor in column index 1 or 2 if
+
===Пример: как добавить кнопку редактора===
// cell in column index 1 is empty
+
<syntaxhighlight lang=pascal>
 +
// Условно показываем кнопку редактора в столбцах индекс 1 или 2, если
 +
// ячейка в столбце index 1 пуста
 
procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer;  
 
procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer;  
 
   var Editor: TWinControl);
 
   var Editor: TWinControl);
Line 748: Line 836:
 
   end;
 
   end;
  
// Triggering Action ...
+
// Запуск действия ...
 
procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
 
procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
 
begin
 
begin
   if StringGrid1.Col = 1 then Showmessage('column 1 editor clicked');
+
   if StringGrid1.Col = 1 then Showmessage('щелкнут редактор столбца 1');
   if StringGrid1.Col = 2 then Showmessage('column 2 editor clicked');
+
   if StringGrid1.Col = 2 then Showmessage('щелкнут редактор столбца 2');
end;</syntaxhighlight>
+
end;
 +
</syntaxhighlight>
  
 +
===Пример: Запрет отображения текстовых полей как "(Memo)" для DBGrids===
 +
Когда вы используете SQL-выражения, такие как <tt>"Select * from A"</tt>, вы можете увидеть "(Memo)" вместо своего содержимого в ячейке сетки. Есть много способов исправить это, но, может быть, самым простым является изменение вашего выражения <tt>"Select * from A"</tt> на <tt>"Select Cast(Column as TEXT) as Column from A"</tt>. Это обычная проблема при использовании компонента DBGrid.
  
===Example: Avoid display Text Field as  (memo) for DBGrids===
+
<syntaxhighlight lang=pascal>
 
 
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.
 
 
 
<syntaxhighlight>
 
 
  var
 
  var
 
     sql : UTF8String;
 
     sql : UTF8String;
     Queryu : TSQLQuery;
+
     Query : TSQLQuery;
 
.....
 
.....
 
     sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
 
     sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
     Queryu.SQL.Text:=sql;
+
     Query.SQL.Text:=sql;
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
+
===Пример: Работа с Picklist, Как сделать его только для чтения и Как заполнить его в runtime.===
===Example: Working with Picklist, How to make it read only and How to fill it at run time.===
+
Используя событие сетки OnSelectEditor, вы можете настроить, как поведет себя редактор PickList (см. стиль кнопки cbsPickList). В следующем примере редактор списка выбора из столбца 1 изменяется на нечетных строках таким образом, что пользователь может вводить значения, набирая [их с клавиатуры], а на четных строках [вводимые] значения ограничиваются теми, которые содержатся в его списке. Кроме того, в этом примере показано, как заполнить список разными значениями в зависимости от обрабатываемой строки.
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.
+
<syntaxhighlight lang=pascal>
<syntaxhighlight>
 
 
procedure TForm1.gridSelectEditor(Sender: TObject; aCol, aRow: Integer;
 
procedure TForm1.gridSelectEditor(Sender: TObject; aCol, aRow: Integer;
 
   var Editor: TWinControl);
 
   var Editor: TWinControl);
Line 798: Line 884:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===Aligning text in StringGrids===
+
===Выравнивание текста в StringGrids===
This code shows how to use different text alignments in columns 2 and 3.
+
Этот код показывает, как использовать разное выравнивание текста в столбцах 2 и 3.
<syntaxhighlight>procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
+
<syntaxhighlight lang=pascal>procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
 
   aState: TGridDrawState);
 
   aState: TGridDrawState);
 
var
 
var
Line 817: Line 903:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
===Multilines in Grids, DBGrid===
+
===Многострочность в 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.
+
В этом примере показано, как сделать многострочный текст в ячейке [3,2]. Он работает так же для DBGrid, где [событие] OnPrepareCanvas имеет параметры для работы с столбцами, а оттуда и с TFields.
<syntaxhighlight>procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
+
<syntaxhighlight lang=pascal>procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
 
   aState: TGridDrawState);
 
   aState: TGridDrawState);
 
var
 
var
Line 832: Line 918:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
=== Validating Entered Values ===
+
=== Проверка введенных значений ===
Lazarus version 0.9.29 introduces the StringGrid OnValidateEntry event of type TValidateEntryEvent which has the following declaration:
+
Lazarus версии 0.9.29 для StringGrid ввел событие OnValidateEntry типа TValidateEntryEvent, которое имеет следующее объявление:
  
<syntaxhighlight>TValidateEntryEvent =
+
<syntaxhighlight lang=pascal>TValidateEntryEvent =
 
   procedure(sender: TObject; aCol, aRow: Integer;
 
   procedure(sender: TObject; aCol, aRow: Integer;
 
             const OldValue: string; var NewValue: string) of object;</syntaxhighlight>
 
             const OldValue: string; var NewValue: string) of object;</syntaxhighlight>
  
  aCol,aRow are the cell coordinates of cell being validated.
+
где
  OldValue is the value that was in cells[aCol,aRow] before editing started.
+
  aCol,aRow являются координатами проверяемой ячейки.
  NewValue is the value that will be finally inserted in cells[aCol,aRow].
+
  OldValue это значение, которое было в ячейках [aCol, aRow] до начала редактирования.
 +
  NewValue это значение, которое будет окончательно вставлено в ячейки [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.
+
Из-за того, что StringGrid работает, устанавливая значение ячейки во время редактирования пользователем (см. событие OnSetEditText и метод SetEditText сетки), то когда событие OnValidateEntry запускается, ячейка уже содержит введенное значение (корректное или нет); используя аргументы события OldValue и NewValue, значение ячейки может быть проверено и изменено, если необходимо.
  
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.  
+
Обычно проверка выполняется, когда пользователь перемещается [с редактируемой ячейки] в другую. Если проверка закончилась неудачей, желательно сохранить редактор ячейки видимым/сфокусированным, чтобы введенное значение могло быть исправлено пользователем. Чтобы сетка "узнала", что проверка не прошла, необходимо создать исключение. Сетка будет обрабатывать исключение из Application.HandleException, и любое перемещение отменится.
  
For example, suppose that cell[1,1] should hold only values 'A' or 'B', validation could be made with:
+
Например, предположим, что ячейка [1,1] должна содержать только значения 'A' или 'B', [тогда] валидация может быть выполнена с помощью:
  
<syntaxhighlight>procedure TForm1.GridValidateEntry(sender: TObject; aCol,
+
<syntaxhighlight lang=pascal>procedure TForm1.GridValidateEntry(sender: TObject; aCol,
 
   aRow: Integer; const OldValue: string; var NewValue: String);
 
   aRow: Integer; const OldValue: string; var NewValue: String);
 
begin
 
begin
 
   if (aCol=1) and (aRow=1) then begin
 
   if (aCol=1) and (aRow=1) then begin
 
     if grid.Cells[aCol,aRow]<>'A') and grid.Cells[aCol,aRow]<>'B') 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.
+
       // устанавливаем новое допустимое значение, чтобы пользователь мог просто нажать RETURN для продолжения, например
 
       NewValue := 'A';
 
       NewValue := 'A';
       // another option is reverting to previous cell value (which is assumed to be valid)
+
       // другой вариант - вернуться предыдущему значению ячейки (которое считается допустимым)
 
       // NewValue := OldValue;
 
       // NewValue := OldValue;
       raise Exception.Create('Only A or B are allowed here');
+
      // Используйте EAbort, а не другой тип исключения, чтобы избежать [возникновения] ложных
 +
      // диалоговых окон ошибок и [диалоговых окон] обратной отладки
 +
       raise EAbort.Create('Разрешены только A или B');
 
     end else begin
 
     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
+
       // окончательное значение все равно можно изменить, заполнив NewValue другим значением
 
        
 
        
       // computer knows better :)
+
       // компьютер знает лучше :)
 
       if grid.Cells[1,1]='A' then  
 
       if grid.Cells[1,1]='A' then  
 
         NewValue := 'B'  
 
         NewValue := 'B'  
Line 872: Line 961:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
=== 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.
+
Свойство ColumnClickSorts позволяет сортировать сетку автоматически, когда пользователь щелкает по заголовку столбца.
 +
Многократный щелчок по тому же столбцу переключает порядок сортировки.
 +
По умолчанию отображаются значки сортировки, показывающие, какой столбец был нажат.
 +
 
 +
В коде вы можете использовать метод SortColRow().
 +
 
 +
Первый параметр IsColumn - это логическое значение, которое указывает:
 +
* True  если будет отсортирован столбец
 +
* False если будет отсортирована строка
 +
 
 +
Второй параметр, index, является целым значением:
 +
* Index  указывает колонку или строку для сортировки.
 +
 
 +
Последние два параметра являются необязательными, [они] определяют поддиапазон строк или столбцов для сортировки.
 +
* FromIndex    (От столбца или строки)
 +
* ToIndex      (До столбца или строки)
 +
Если последние два параметра не указаны,  сортируется весь столбец или строка.
  
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.
+
Сортировка использует алгоритм QuickSort, его можно изменить, если сетка-наследник переопределяет метод sort() и вызывает doCompareCells для сравнения с ячейкой.
  
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.
+
По умолчанию [метод SortColRow()] сортирует содержимое ячейки как строки в порядке возрастания или убывания, которое можно выбрать с помощью свойства SortOrder.
<syntaxhighlight>// sort column 3 in ascending order
+
По умолчанию он использует порядок [сортировки] по возрастанию.
 +
 
 +
<syntaxhighlight lang=pascal>
 +
// Сортировка столбца 3 в порядке возрастания
 
grid.SortColRow(true, 3);
 
grid.SortColRow(true, 3);
  
// sort column 3 in descending order, skip fixed rows a top
+
// Сортировка столбца 3 в порядке убывания, пропустить фиксированные строки сверху
grid.SortOrder := soDescending; // or soAscending
+
grid.SortOrder := soDescending; // или soAscending
grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);</syntaxhighlight>
+
grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);
 +
</syntaxhighlight>
 +
 
 +
Для пользовательской сортировки чисел, дат, состояний и т.д.:
 +
у StringGrid есть событие OnCompareCells, с которым пользователи могут обращаться, например, следующим образом:
  
For custom sorting of numbers, dates, states, etc. StringGrid has the OnCompareCells event which users can handle for example this way:
+
<syntaxhighlight lang=pascal>
<syntaxhighlight>procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol, BRow: Integer; var Result: integer);
+
procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol, BRow: Integer; var Result: integer);
 
begin
 
begin
   // Result will be either <0, =0, or >0 for normal order.
+
   // Результат будет либо <0, = 0, либо >0 для нормального порядка.
 
   result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
 
   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).
+
   // Для обратного порядка просто [присвойте] негативный результат (напр., на основе SortOrder сетки).
 
   if StringGrid1.SortOrder = soDescending then
 
   if StringGrid1.SortOrder = soDescending then
 
     result := -result;
 
     result := -result;
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
You can use OnCompareCells also when automatic column sorting is enabled through ColumnClickSorts property.
+
Вы также можете использовать OnCompareCells, когда автоматическая сортировка столбцов включена через свойство ColumnClickSorts.
  
=== Sorting columns or rows in DBGrid with sort arrows in column header===
+
=== Переопределить автоматическую сортировку (ColumnClickSorts установлен в False). Исключить сортировку некоторых столбцов. Показать стрелки сортировки в заголовке столбца ===
Here is an example that will sort a '''DBgrid''' using the '''OnTitleClick''' event
+
(ColumnClickSorts, установленный в False, НЕ будет показывать стрелки сортировки в заголовке столбца, поэтому мы должны установить стрелки.)
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:'''
+
<syntaxhighlight lang=pascal>
* You will need an imagelist to store the up/down sort arrows. Assign your imagelist to the dbgrid's '''TitleImageList''' property.
+
procedure TForm1.StringGrid1HeaderClick(Sender: TObject; IsColumn: Boolean;
* Ensure the '''[[doc:fcl/sqldb/tsqlquery.maxindexescount.html|TSQLQuery.MaxIndexesCount]]''' is large enough to hold the new indexes you will be creating. For this example I had it set to 100.
+
Index: Integer);
* You will also need a private var to store the last column used to sort, for this example I have called it '''FLastColumn'''.
+
begin
 +
if isColumn then with StringGrid1 do
 +
  begin
 +
    col:=Index;                                                  // Задаем столбец для сортировки
 +
    if Tag  = 0 then Tag :=-1                                    // Предположим, что данные в столбце возрастают
 +
                else Tag := Tag  * -1;                          // Используем StringGrid1.Tag для хранения порядка сортировки 
 +
                                                                // Первый щелчок заголовка сортирует по убыванию
 +
 
 +
    // Активируем CompareCells (активируем сортировку)                                                                           
 +
    case Index of
 +
      0: col:=-1;                                                // Не сортируем столбец 0 и не показываем стрелку сортировки
 +
      1: SortColRow(True, Index);                                // Сортировка будет происходить в CompareCells, как только будет выполнен оператор
 +
      2: SortColRow(True, Index);
 +
      3: SortColRow(True, Index);
 +
      4: SortColRow(True, Index);
 +
      5: col:=-1;                                                // Не сортируем столбец 5 и не показываем стрелку сортировки
 +
    end;
 +
  end;
 +
end;
  
For this example the '''TSQLQuery''' used is called OpenQuery.
+
// Compare возвращает одно из следующих значений:
 +
// if A<B  return: -1            ( Восходящий )
 +
// if A=B  return:  0
 +
// if A>B  return:  1            ( Нисходящий )
  
{{Note|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.}}
+
// Как сортировать: Text, Integer, Float, DateTime:
  
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.
+
procedure TForm1.StringGrid1CompareCells(Sender: TObject; ACol, ARow, BCol,
 +
BRow: Integer; var Result: integer);
  
<syntaxhighlight>
+
  function CompareTextAsText: Integer;                        // Text
FLastColumn: TColumn; //store last grid column we sorted on
+
  begin
 +
    CompareText(Cells[ACol,ARow], Cells[BCol,BRow]);
 +
  end;
 +
 
 +
  function CompareTextAsInteger: Integer;
 +
  var i1,i2:Integer;
 +
  begin
 +
    if TryStrToInt(StringGrid1.Cells[ACol,ARow],i1) and      // Integer
 +
      TryStrToInt(StringGrid1.Cells[BCol,BRow],i2) then
 +
    Result := CompareValue(i1, i2)
 +
    else Exit(0);
 +
  end; 
 +
 
 +
  function CompareTextAsFloat: Integer;                        // Float
 +
  var f1,f2:Extended;
 +
  begin
 +
    if TryStrToFloat(StringGrid1.Cells[ACol,ARow],f1) and
 +
      TryStrToFloat(StringGrid1.Cells[BCol,BRow],f2) then
 +
    Result := CompareValue(f1, f2)
 +
    else Exit(0);
 +
  end;
 +
 
 +
  function CompareTextAsDateTime: Integer;                  // DateTime
 +
  var
 +
  D1, D2 : TDateTime;
 +
  begin
 +
    if Trim(StringGrid1.Cells[ACol,ARow]) <> Trim(StringGrid1.Cells[BCol,BRow]) then
 +
    begin
 +
      if trim(StringGrid1.Cells[ACol,ARow]) = '' then Exit(1);
 +
      if Trim(StringGrid1.Cells[BCol,BRow]) = '' then Exit(-1);
 +
    end
 +
    else Exit(0);
 +
 
 +
    if TryStrToDate(StringGrid1.Cells[ACol,ARow], D1, 'yy/mm/dd') and
 +
      TryStrToDate(StringGrid1.Cells[BCol,BRow], D2, 'yy/mm/dd') then
 +
    Result := CompareDateTime(D1, D2)
 +
    else Exit(0);
 +
end;
 +
 
 +
begin                                        // Не делайте здесь ничего другого, сортировка может быть выполнена некорректно
 +
  With    StringGrid1 do
 +
  begin
 +
    case ACol of
 +
      0 : ;                                                                    // Не сортируйте столбец 0
 +
      1 :  Result := CompareTextAsText;                                        // Text
 +
      2 :  Result := CompareTextAsInteger;                                      // Integer
 +
      3 :  Result := CompareTextAsFloat;                                        // Float
 +
      4 :  Result := CompareTextAsDateTime;                                    // DateTime
 +
    end; {Case}
 +
  end;
 +
  if  StringGrid1.Tag  = -1 then  Result := -Result;                          // Переключаем порядок сортировки
 +
end; 
 +
 
 +
// DrawCell сделает трюк, предыдущие изображения стрелки будут удалены:
 +
procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
 +
  aRect: TRect; aState: TGridDrawState);
 +
 
 +
  procedure DrawArrow(ImgIndex:Integer);                  // Нужны стрелки вверх/вниз в ImageList
 +
  begin
 +
    with StringGrid1 do
 +
      TitleImageList.Draw(Canvas,                        // TitleImageList из StringGrid
 +
      ARect.Right - TitleImageList.Width,
 +
      (ARect.Bottom - ARect.Top - TitleImageList.Height) div 2 + 2,  // Положение стрелки, может быть отрегулировано
 +
      ImgIndex );
 +
  end;
 +
begin
 +
  if Col > 0 then                                    // В этом демо нет стрелок в col 0 
 +
    if (ARow = 0) and                                // Предположим, строка 0 в качестве заголовка строк
 +
      Assigned(TitleImageList) then                // Нужны стрелки вверх/вниз в ImageList
 +
        case Columns.Items[aCol].Tag of            // Использовать тег столбца как индекс Восходящей/Нисходящей стрелки
 +
          -1: DrawArrow(1);                        // Заменяем Index указателем стрелки вниз
 +
            0: DrawArrow(-1);                        // Удаляем предыдущее изображение
 +
            1: DrawArrow(2);                        // Заменяем Index указателем стрелки вверх
 +
        end;
 +
end;
 +
</syntaxhighlight>
 +
 
 +
===Сортировка столбцов или строк в DBGrid со стрелками сортировки в заголовке столбца===
 +
Вот пример, который будет сортировать '''DBgrid''', используя событие '''OnTitleClick''' и '''TSQLQuery''' и индексы. Это также должно работать для любого совместимого набора данных, такого как '''TbufDataset'''.
 +
Функция использует свойство '''column.tag''' для хранения состояния сортировки для каждого столбца, на который вы нажимаете, поэтому, когда вы вернетесь к тому [столбцу], который вы уже отсортировали, [заголовок столбца] отобразит правильную стрелку сортировки для отображения.
 +
 
 +
'''Предпосылки:'''
 +
* Для хранения [изображений] стрелок вверх/вниз вам понадобится imagelist. Присвойте свой imagelist свойству '''TitleImageList''' dbgrid'а.
 +
* Убедитесь, что '''[[doc:fcl/sqldb/tsqlquery.maxindexescount.html|TSQLQuery.MaxIndexesCount]]''' достаточно велик, чтобы хранить новые индексы, которые вы будете создавать. Для этого примера я установил значение 100.
 +
* Вам также понадобится переменная-поле [(в разделе интерфейса private)] для хранения последнего столбца, который используется для сортировки, для этого примера я назвал его  '''FLastColumn'''.
 +
 
 +
В этом примере используемый '''TSQLQuery''' называется OpenQuery.
 +
 
 +
{{Note|По состоянию на 21 марта 2013 года '''TSQLQuery''' не имеет возможности очищать индексы, но в качестве обходного пути вы можете установить [значение его свойства] '''unidirectional''' в true, а затем в false: это очистит индексы.}}
 +
 
 +
Чтобы повторно использовать компонент '''TSQLQuery''' с другим оператором SQL, индексы должны быть очищены после сортировки, [иначе] при открытии '''TSQLQuery''' с помощью другого оператора SQL будет вызвано исключение.
 +
 
 +
<syntaxhighlight lang=pascal>
 +
FLastColumn: TColumn; //сохраняем последний столбец сетки, который мы отсортировали
  
 
procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
 
procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
 
const
 
const
   ImageArrowUp=0; //should match image in imagelist
+
   ImageArrowUp=0; //должен соответствовать изображению в imagelist
   ImageArrowDown=1; //should match image in imagelist
+
   ImageArrowDown=1; //должен соответствовать изображению в imagelist
 
var
 
var
 
   ASC_IndexName, DESC_IndexName:string;
 
   ASC_IndexName, DESC_IndexName:string;
 
   procedure UpdateIndexes;
 
   procedure UpdateIndexes;
 
   begin
 
   begin
     // Ensure index defs are up to date
+
     // Обеспечиваем обновление индексов
     OpenQuery.IndexDefs.Updated:=false; {<<<--This line is critical. IndexDefs.Update will not
+
     OpenQuery.IndexDefs.Updated:=false; {<<<--Эта строка - решающая. IndexDefs.Update не будет
     update if already true, which will happen on the first column sorted.}
+
     обновляться, если уже true, что произойдет в первом отсортированном столбце.}
 
     Openquery.IndexDefs.Update;
 
     Openquery.IndexDefs.Update;
 
   end;
 
   end;
Line 932: Line 1,157:
 
   ASC_IndexName:='ASC_'+Column.FieldName;
 
   ASC_IndexName:='ASC_'+Column.FieldName;
 
   DESC_IndexName:='DESC_'+Column.FieldName;
 
   DESC_IndexName:='DESC_'+Column.FieldName;
   // indexes can't sort binary types such as ftMemo, ftBLOB
+
   // индексы не могут сортировать двоичные типы, такие как ftMemo, ftBLOB
 
   if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
 
   if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
 
     exit;
 
     exit;
   // check if an ascending index already exists for this column.
+
   // проверяем, существует ли восходящий индекс для этого столбца
   // if not, create one
+
   // если нет, создадим [его]
 
   if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
 
   if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
 
   begin
 
   begin
 
     OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
 
     OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
     UpdateIndexes; //ensure index defs are up to date
+
     UpdateIndexes; //обеспечиваем обновление индексов
 
   end;
 
   end;
   // Check if a descending index already exists for this column
+
   // проверяем, существует ли нисходящий индекс для этого столбца
   // if not, create one
+
   // если нет, создадим [его]
 
   if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
 
   if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
 
   begin
 
   begin
 
     OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
 
     OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
     UpdateIndexes; //ensure index defs are up to date
+
     UpdateIndexes; //обеспечиваем обновление индексов
 
   end;
 
   end;
  
   // Use the column tag to toggle ASC/DESC
+
   // Используем тег столбца для переключения ASC/DESC
 
   column.tag := not column.tag;
 
   column.tag := not column.tag;
 
   if boolean(column.tag) then
 
   if boolean(column.tag) then
Line 962: Line 1,187:
 
     OpenQuery.IndexName:=DESC_IndexName;
 
     OpenQuery.IndexName:=DESC_IndexName;
 
   end;
 
   end;
   // Remove the sort arrow from the previous column we sorted
+
   // Удаляем стрелку сортировки из предыдущего столбца, который мы отсортировали
 
   if (FLastColumn <> nil) and (FlastColumn <> Column) then
 
   if (FLastColumn <> nil) and (FlastColumn <> Column) then
 
     FLastColumn.Title.ImageIndex:=-1;
 
     FLastColumn.Title.ImageIndex:=-1;
Line 969: Line 1,194:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Selecting Records in a DBGrid using checkboxes ===
+
=== Выбор записей в DBGrid с использованием checkbox'ов ===
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.
+
Цель состоит в том, чтобы иметь возможность выбирать произвольные записи в dbgrid с помощью checkbox'ов, сетка имеет возможность показывать checkbox'ы автоматически, когда обнаруживает наличие логических полей, а для других типов полей пользователь может вручную [в свойстве] ButtonStyle столбца выбрать [значение] cbsCheckboxColumn. Для таких столбцов пользователь просто помечает checkbox, и содержимое поля изменяется соответствующим образом.
  
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.
+
Но что произойдет, если в нашем наборе данных нет такого доступного поля? или мы не хотим, чтобы сетка входила в состояние редактирования при изменении состояния checkbox'а?. Добавление столбца без полей с помощью ButtonStyle=cbsCheckboxColumn покажет все checkbox'ы с неопределенным значением и отключенными, потому что нет поля, связанного с этим столбцом, и поэтому ничего не нужно изменять. Поскольку мы хотим самостоятельно обрабатывать состояние checkbox'а, нам нужно где-то сохранить состояние для каждой записи. Для этого мы можем использовать класс TBookmarklist (определенный в модуле dbgrids.pas), где свойство CurrentRowSelected может определить, выбрана ли текущая запись или нет. Используя события dbgridOnCellClick и OnUserCheckboxState, мы можем отслеживать состояние checkbox.
  
Note this technique needs Lazarus r31148 or later which implements event OnUserCheckboxState.
+
Обратите внимание, что этой методике нужен Lazarus r31148 или более поздней версии, которая реализует событие OnUserCheckboxState.
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
...
 
...
 
uses ..., dbgrids, stdctrls, ...
 
uses ..., dbgrids, stdctrls, ...
Line 1,021: Line 1,246:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
=== Подсветка выделенного столбца и строки ячейки ===
 +
В версии Lazarus 40276 добавлена опция ('''gdRowHighlight'''), она работает аналогично '''goRowSelect''', но использует более светлый тон для выделения, а прямоугольник фокуса - только выбранная ячейка, а не в целая строка. Это отлично работает для строк, но как насчет столбцов?
  
=== Highlighting the selected cell column and row ===
+
В этом разделе показан способ выделения столбцов, строк или и того, и другого (пример, который выделяет только столбцы и заголовки строк, можно найти в lazarus/examples/gridexamples/spreadsheet, этот способ действительно является расширением этого примера). Он использует два события сетки: '''OnBeforeSelection''' и '''OnPrepareCanvas''', рисунок не нужен.
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.  
+
Событие '''OnBeforeSelection''' запускается, когда начинается смена выделения, в этом случае мы можем узнать, какая ячейка выбрана в данный момент и какая ячейка будет выбрана следующей. Мы используем эту информацию для перерисовки всей строки или столбца как старой, так и новой ячеек. Когда начнется следующий цикл отрисовки, сетке будет предложено перерисовать ячейки, принадлежащие недействительным областям. Одним из первых шагов для перерисовки является вызов события '''OnPrepareCanvas''' (если он существует) для настройки свойств холста по умолчанию. Мы используем это событие для настройки выделенной строки или столбца:
  
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:
+
<syntaxhighlight lang=pascal>
<syntaxhighlight>
 
 
procedure TForm1.gridBeforeSelection(Sender: TObject; aCol, aRow: Integer);
 
procedure TForm1.gridBeforeSelection(Sender: TObject; aCol, aRow: Integer);
 
begin
 
begin
   // we can decide here if we want highlight columns, rows or both
+
   // мы можем здесь принять решение, если хотим выделить столбцы, строки или и те, и другие
   // in this example we highlight both
+
   // в этом примере мы выделяем и те, и другие
 
   if Grid.Col<>aCol then
 
   if Grid.Col<>aCol then
 
   begin
 
   begin
     // a change on current column is detected
+
     // обнаружено изменение текущего столбца
     grid.InvalidateCol(aCol);      // invalidate the new selected cell column
+
     grid.InvalidateCol(aCol);      // перерисовываем новый выбранный столбец ячейки
     grid.InvalidateCol(grid.Col);  // invalidate the current (it will be the 'old') selected column
+
     grid.InvalidateCol(grid.Col);  // перерисовываем текущий (это будет 'старый') выбранный столбец
 
   end;
 
   end;
 
   if Grid.Row<>aRow then
 
   if Grid.Row<>aRow then
 
   begin
 
   begin
     grid.InvalidateRow(aRow);      // invalidate the new selected cell row
+
     grid.InvalidateRow(aRow);      // перерисовываем новую выбранную строку ячейки
     grid.InvalidateRow(grid.Row);  // invalidate the current (it will be the 'old') selected row
+
     grid.InvalidateRow(grid.Row);  // перерисовываем текущую (это будет 'старая') выбранную строку
 
   end;
 
   end;
 
end;  
 
end;  
Line 1,052: Line 1,277:
 
   begin
 
   begin
 
     if (aCol=grid.Col) or (aRow=grid.Row) then
 
     if (aCol=grid.Col) or (aRow=grid.Row) then
       grid.Canvas.Brush.Color := clInactiveCaption; // this would highlight also column or row headers
+
       grid.Canvas.Brush.Color := clInactiveCaption; // это подсветило бы также заголовки столбцов или строк
 
   end else
 
   end else
 
   if gdFocused in aState then begin
 
   if gdFocused in aState then begin
     // we leave alone the current selected/focused cell
+
     // мы оставляем в покое текущую выделенную/сфокусированную ячейку
 
   end else
 
   end else
 
   if (aCol=Grid.Col) or (aRow=grid.Row) then
 
   if (aCol=Grid.Col) or (aRow=grid.Row) then
     grid.Canvas.Brush.Color := clSkyBlue; // highlight rows and columns with clSkyBlue color
+
     grid.Canvas.Brush.Color := clSkyBlue; // подсветка строк и столбцов цветом clSkyBlue
 +
end;
 +
</syntaxhighlight>
 +
 
 +
== DBGrids "Как сделать..." и Примеры==
 +
==== Пример: как установить редактор memo для dbgrids====
 +
Конечно, вы можете использовать другой элемент управления вместо TMemo. Отрегулируйте по вкусу.
 +
Адаптировано из [http://forum.lazarus.freepascal.org/index.php?topic=3640.0] (упрощено использование свойства SelectedFieldRect, см. [http://forum.lazarus.freepascal.org/index.php/topic,8713.msg43288.html#msg43288])
 +
 
 +
* Поместите элемент управления memo (или любой другой элемент управления) на форму, установите любые свойства, которые вы хотите, и установите видимым значение false. Он будет использоваться при редактировании ячейки сетки. Мы будем использовать GridCellMemo в этом примере.
 +
* В событии OnSelectEditor вставьте следующий код - адаптируйте, если вы не хотите редактировать логический столбец 3, физический столбец 4:
 +
<syntaxhighlight lang="pascal">
 +
  if (Column.DesignIndex = 3) then
 +
  begin
 +
      GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
 +
      GridCellMemo.Text:=Column.Field.AsString;
 +
      Editor := GridCellMemo;
 +
  end;
 +
</syntaxhighlight>
 +
 
 +
* Предположим, ваш источник данных называется Datasource1, а DBGrid называется ResultsGrid. Затем в событие OnEditingDone для GridCellMemo поместите следующее:
 +
(В исходном сообщении на форуме использовалось событие OnChange, которое будет запускаться каждый раз при изменении содержимого. Использование OnEditingDone срабатывает только после того, как пользователь завершит редактирование.)
 +
<syntaxhighlight lang="pascal">
 +
  if not(Datasource1.State in [dsEdit, dsInsert]) then Datasource1.Edit;
 +
   
 +
  Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;
 +
</syntaxhighlight>
 +
 
 +
'''Осторожно''' Использование обычного элемента управления в качестве пользовательского редактора имеет много недостатков: он не будет изменять размеры или перемещать себя при изменении размера столбцов или прокрутке сетки, вы должны позаботиться о модификации данных в сетке. (как объяснено выше), он не работает должным образом с DbGrids и мешает нормальной навигации по сетке с помощью клавиатуры. Сетка взаимодействует с редактором с помощью некоторых специальных сообщений сетки, поэтому для управления этими сообщениями лучше выделить подкласс элемента управления, как объясняется в пятой главе [http://www.freepascal.org/~michael/articles/grids/grids.pdf этого документа].
 +
 
 +
'''Осторожно''' Если у вас несколько сеток в одной форме, используйте для каждой свой собственный редактор: если вы используете один и тот же элемент управления, могут произойти плохие вещи, поскольку обе сетки попытаются использовать его одновременно.
 +
 
 +
===Пример: исключение отображения текстовых полей как «(Memo)» для DBGrids===
 +
Когда вы используете операторы SQL, такие как <code>"Select * from A"</code>, вы можете увидеть «(Memo)» вместо текстового содержимого в ячейке сетки. Есть много способов исправить это, но, возможно, самый простой - изменить выражение <code>"Select * from A"</code> на <code>Select Cast(Column as TEXT) as Column from A</code>. Это распространенная проблема при использовании компонента DBGrid.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
var
 +
    sql : UTF8String;
 +
    Query : TSQLQuery;
 +
.....
 +
    sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
 +
    Query.SQL.Text:=sql;
 +
</syntaxhighlight>
 +
 
 +
===Сортировка столбцов или строк в DBGrid со стрелками сортировки в заголовке столбца===
 +
Вот пример, который будет сортировать '''DBgrid''', используя событие '''OnTitleClick''' и  '''TSQLQuery''' и индексы. Это также должно работать для любого совместимого набора данных, такого как '''TbufDataset'''. Функция использует свойство '''column.tag''' для хранения состояния сортировки для каждого столбца, по которому вы щелкнули, поэтому, когда вы вернетесь к тому, который уже отсортирован, она выберет правильную стрелку сортировки для отображения.
 +
 
 +
'''Предварительные условия:'''
 +
* Вам понадобится список изображений для хранения стрелок вверх/вниз. Присвойте свой список изображений свойству dbgrid '''TitleImageList'''.
 +
* Убедитесь, что свойство '''[[doc:fcl/sqldb/tsqlquery.maxindexescount.html|TSQLQuery.MaxIndexesCount]]''' достаточно велико для хранения новых индексов, которые вы будете создавать. Для этого примера у меня было установлено значение 100.
 +
* Вам также понадобится поле-переменная в разделе private класса для хранения последнего столбца, использованного для сортировки, для этого примера я назвал ее '''FLastColumn'''.
 +
 
 +
В этом примере используемый '''TSQLQuery''' назван OpenQuery.
 +
 
 +
{{Note|по состоянию на 21 марта 2013 года '''TSQLQuery''' не имеет возможности очищать индексы, но в качестве обходного пути вы можете установить свойство '''unidirectional''' в значение true, а затем установить его в значение false, и это очистит индексы.}}
 +
 
 +
Чтобы повторно использовать компонент '''TSQLQuery''' с другим оператором SQL, индексы должны быть очищены после сортировки, иначе будет возбуждено исключение при открытии '''TSQLQuery''' с другим оператором SQL.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
FLastColumn: TColumn; //сохраняем последний столбец сетки, по которому мы делали сортировку
 +
 
 +
procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
 +
const
 +
  ImageArrowUp=0; //должно соответствовать изображению в imagelist
 +
  ImageArrowDown=1; //должно соответствовать изображению в imagelist
 +
var
 +
  ASC_IndexName, DESC_IndexName:string;
 +
  procedure UpdateIndexes;
 +
  begin
 +
    // Убедитесь, что индексы обновлены
 +
    OpenQuery.IndexDefs.Updated:=false; {<<<--Эта строчка имеет решающее значение. IndexDefs.Update не будет обновляться, если уже имеет значение true, что произойдет в первом отсортированном столбце.}
 +
    Openquery.IndexDefs.Update;
 +
  end;
 +
begin
 +
  ASC_IndexName:='ASC_'+Column.FieldName;
 +
  DESC_IndexName:='DESC_'+Column.FieldName;
 +
  // индексы не могут сортировать двоичные типы, такие как ftMemo, ftBLOB
 +
  if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
 +
    exit;
 +
  // проверяем, существует ли восходящий индекс для этого столбца.
 +
  // если нет, создаем
 +
  if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
 +
  begin
 +
    OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
 +
    UpdateIndexes; //ensure index defs are up to date
 +
  end;
 +
  // проверяем, существует ли нисходящий индекс для этого столбца.
 +
  // если нет, создаем
 +
  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;
 +
 
 +
  // Используем тег столбца для переключения направления сортировки 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;
 +
  // Удаляем стрелку сортировки из предыдущего столбца, который мы отсортировали
 +
  if (FLastColumn <> nil) and (FlastColumn <> Column) then
 +
    FLastColumn.Title.ImageIndex:=-1;
 +
  FLastColumn:=column;
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
=== Выбор записей в DBGrid с помощью флажков(чекбоксов) ===
 +
Цель состоит в том, чтобы иметь возможность выбирать произвольные записи в dbgrid с помощью флажков, сетка имеет возможность автоматически отображать флажки, когда обнаруживает наличие логических полей, для других типов полей пользователь может вручную выбрать значение cbsCheckboxColumn свойства ButtonStyle для столбца. Для этого типа столбцов пользователь просто устанавливает флажок, и содержимое поля изменяется соответствующим образом.
 +
 +
Но что произойдет, если в нашем наборе данных нет такого доступного поля? Или мы не хотим, чтобы сетка входила в состояние редактирования при установке флажка? При добавлении столбца без полей с помощью <code>ButtonStyle = cbsCheckboxColumn</code> все флажки будут выделены серым цветом и отключены, поскольку поле, связанное с этим столбцом, отсутствует и поэтому изменять нечего. Поскольку мы хотим обрабатывать состояние флажка самостоятельно, нам нужно хранить состояние где-то для каждой записи. Для этого мы можем использовать класс TBookmarklist (определенный в модуле dbgrids.pas), где свойство CurrentRowSelected может сказать, выбрана текущая запись или нет. Используя события dbgrid OnCellClick и OnUserCheckboxState, мы можем отслеживать состояние флажка.
 +
 +
{{Note| эта хитрость требует наличие Lazarus r31148 или новее, который реализует событие OnUserCheckboxState.}}
 +
 +
<syntaxhighlight lang="pascal">
 +
...
 +
uses ..., dbgrids, stdctrls, ...
 +
 +
type
 +
 +
  { TForm1 }
  
[[Category:Russian (unfinished translation)]]
+
  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;
 +
</syntaxhighlight>
 +
 
 +
===Пример: Как экспортировать данные из DBGrid в текстовый файл===
 +
Есть несколько способов создать текстовый файл из содержимого DBGrid. В большинстве случаев люди будут говорить вам, что вместо того, чтобы думать о DBGrid, как о контейнере данных, лучше поискать связанный набор данных.
 +
 
 +
Но что произойдет, когда вам понадобится текстовое представление, которое должно соответствовать внешнему виду сетки: видимость колонок, ширина или порядок?
 +
 
 +
Одним из решений является использование пакета LazReport, в котором есть компонент, специально предназначенный для создания отчета из DbGrid. Затем этот отчет можно экспортировать в несколько других форматов, включая текстовый формат. Здесь представлено простое решение, которое также может быть использовано в качестве основы для других экспортеров текстового формата, например, простого экспортера HTML.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
// aGrid: Сетка для экспорта
 +
// lpp: строк на странице. Не включает текстовый заголовок
 +
// pageBreak: вставляет разрыв страницы, который некоторые принтеры могут использовать для запуска новой страницы
 +
// require: uses LazUTF8
 +
function GridTextExporter(aGrid: TDbGrid; lpp: Integer; pageBreak:boolean): TStringList;
 +
const
 +
  PX_PER_CHAR = 7;
 +
 
 +
var
 +
  Ds: TDataset;
 +
  book: TBookMark;
 +
  recCount: Integer;
 +
  line: string;
 +
  column: TColumn;
 +
 
 +
  function WidthToChar(aWidth: Integer): integer;
 +
  begin
 +
    result := trunc(aWidth/PX_PER_CHAR);
 +
  end;
 +
 
 +
  procedure AddNext(theText: string; alignment: TAlignment);
 +
  var
 +
    width: Integer;
 +
  begin
 +
    if (line<>'') and (line<>#12) then
 +
      line := line + ' ';
 +
 
 +
    width := WidthToChar(Column.Width);
 +
 
 +
    case Alignment of
 +
      taRightJustify: line := line + UTF8PadLeft(theText, width);
 +
      taCenter:      line := line + UTF8PadCenter(theText, width);
 +
      else            line := line + UTF8PadRight(theText, width);
 +
    end;
 +
  end;
 +
 
 +
  procedure CollectHeader;
 +
  begin
 +
    AddNext(Column.Title.Caption, Column.Title.Alignment);
 +
  end;
 +
 
 +
  procedure CollectField;
 +
  var
 +
    field: TField;
 +
  begin
 +
    field := Column.Field;
 +
 
 +
    if (field.DataType=ftMemo) and (dgDisplayMemoText in aGrid.Options) then
 +
      AddNext(field.AsString, Column.Alignment)
 +
    else if Field.DataType<>ftBlob then
 +
      AddNext(field.DisplayText, Column.Alignment)
 +
    else
 +
      AddNext('(blob)', Column.Alignment);
 +
  end;
 +
 
 +
  procedure LoopColumns(printingRecord: boolean);
 +
  var
 +
    c: TCollectionItem;
 +
  begin
 +
    if (not printingRecord) and pageBreak and (result.count>0) then
 +
      line := #12
 +
    else
 +
      line := '';
 +
    for c in aGrid.Columns do begin
 +
      Column := TColumn(c);
 +
      if Column.Visible and (Column.Width>=PX_PER_CHAR) then begin
 +
        if printingRecord then CollectField
 +
        else                  CollectHeader;
 +
      end;
 +
    end;
 +
    result.add(line);
 +
  end;
 +
 
 +
begin
 +
  result := TStringList.create;
 +
  Ds := aGrid.DataSource.DataSet;
 +
  Ds.DisableControls;
 +
  book := Ds.GetBookmark;
 +
  try
 +
    Ds.First;
 +
    recCount := 0;
 +
    while not DS.EOF do begin
 +
      if RecCount mod lpp = 0 then
 +
        LoopColumns(false);
 +
      LoopColumns(true);
 +
      inc(recCount);
 +
      Ds.Next;
 +
    end;
 +
  finally
 +
    Ds.GotoBookmark(book);
 +
    Ds.FreeBookmark(book);
 +
    Ds.EnableControls;
 +
  end;
 +
end;
 +
</syntaxhighlight>
 +
 
 +
Чтобы использовать это просто сделайте:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
var
 +
  L: TStringList;
 +
begin
 +
  L := GridTextExporter(grid, 80, true);
 +
  L.SaveToFile('grid.txt');
 +
  L.Free;
 +
end;
 +
</syntaxhighlight>

Latest revision as of 03:21, 21 January 2022

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


Цель

Этот текст попытается показать пользователю некоторые аспекты использования компонентов сеток (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 tstringgrid.png
    • Нажмите на форму рядом с левым верхним углом. Появится новая пустая сетка.
  3. Поместите кнопку в форму
    • с помощью палитры компонентов выберите вкладку "Standard"
    • Нажмите значок TButton tbutton.png
    • Нажмите на пустую область формы. Появится новая кнопка.
  4. Дважды нажмите кнопку из шага 3 и запишите следующий код в обработчике щелчка кнопки:
    • StringGrid1.Cells[1,1] := 'Привет, Мир!';
      
  5. Запустите программу, щелкнув значок воспроизведения menu run.png
    • по нажатию кнопки 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, новый столбец будет не настраиваемым, а нормальным столбцом.

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

свойство AutoFillColumns

Часто сетки шире, чем горизонтальное пространство, необходимое для столбцов - это оставляет неприятную пустую область справа от последнего столбца. Сетки LCL предоставляют механизм для расширения ширины указанных столбцов, так что пустое пространство заполняется автоматически.

Для этой цели необходимо либо добавить Columns, как описано выше, либо установив свойство ColCount, и свойство сетки AutoFillColumns должно быть установлено в true. Каждый столбец имеет свойство SizePriority. Если оно имеет значение 0, ширина столбца берется из свойства Width столбца. Но когда оно имеет ненулевое значение, ширина столбца корректируется до среднего доступного размера, оставшегося для всех столбцов с ненулевым SizePriority.

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) может привести к сбою.

Saving and loading the contenst of a TValueListEditor

Using the Strings property

If you just want to save and load the content of the TValueListEditor (e.g. the Key/Value pairs, not the layout) you can use Strings.SaveToFile and StringsLoadFromFile.
In case of LoadFromFile the RowCount property will be autmatically adjusted.
No sanity checks are performed upon LoadFromFile (so lines that do not represent a Key/Value pair will end up as Keys without Value).

Using TValueList.SaveToFile and TValueList.LoadFromFile

When using SaveToFile and LoadFromFile you get the additional benefits of also being able to save and load the layout, like with other grids.
SaveToFiel will also save information about wether or not the TValueListEditor uses ColumnTitles, and if so, it saves them.

In contrast to it's ancestors TValueList.LoadFromFile performs sanity checks on the file it tries to read. In particular RowCount must be specified and there cannot be a cell with RowIndex > RowCount or ColumnIndex other than 0 or 1.
If the file does not seem to be a valid TValueListEditor grid file, and exception is raised.

TValueList.SaveToFile and TValueList.LoadFromFile do not work correctly in Lazarus versions <= 2.1 r62044.

Using SaveToCSVFile and LoadFromCSV file

These methods should not be used at the moment, since for a TValueListEditor they are flawed.

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

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

Сетка - это компоненты, полученные из класса 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. Если не указан обработчик для события OnDrawCell, следующим сетка вызывает метод DefaultDrawCell, который просто рисует фон ячейки, используя текущий цвет и стиль кисти холста. Если обработчик OnDrawCell существует, сетка сначала рисует фон ячейки, но только если установлено свойство DefaultDrawing, [которое, в свою очередь,] вызывает событие OnDrawCell для создания пользовательской отрисовки клетки. Обычно программисты хотят делать пользовательскую отрисовку только для определенных ячеек и стандартную отрисовку для других; в этом случае они могут ограничить пользовательскую операцию определенной ячейкой или ячейками, просмотрев аргументы ACol, ARow и AState, а для других ячеек просто вызывают метод DefaultDrawCell и позволяют сетке позаботиться об этом.
  • Text. В этот момент (только для TStringGrid), если свойство DefaultDrawing [имеет значение] true, рисуется текстовое содержимое ячейки.
  • Grid lines Последний шаг для каждой ячейки состоит в том, чтобы нарисовать линии сетки: если параметры сетки goVertLine, goHorzLine, goFixedVertLine и goFixedHorzLine указаны, ячейка сетки рисуется в этой точке. Сетки только со строками или только со столбцами могут быть получены путем изменения этих параметров. Если программист предпочел бы иметь "тематический" вид, то это делается и в этот момент (см. свойство TitleStyle).
  • FocusRect. Когда все столбцы текущей строки были отрисованы, наступает время рисовать прямоугольник фокуса для текущей выбранной ячейки или для всей строки, если установлена опция goRowSelect.

Различия с Delphi

  • В Lazarus метод TCustomGrid.DrawCell не является абстрактным, и его реализация по умолчанию выполняет базовое заполнение фона ячейки.
  • В Delphi текст ячейки рисуется перед входом в событие OnDrawCell (см. bug report #9619).

Выбор ячейки сетки

Расположение текущей (сфокусированной) ячейки (или строки) сетки может быть изменено с помощью клавиатуры, мыши или кода. Чтобы успешно сменить фокус ячейки на другую позицию, мы должны проверить целевую позицию, чтобы увидеть, разрешено ли ей получать фокус ячейки. При использовании клавиатуры свойство AutoAdvance выполняет часть процесса, обнаруживая, что должно быть следующей сфокусированной ячейкой. При использовании кликов мыши или перемещения [посредством] кода фокус не будет перемещаться из текущей ячейки, если целевой ячейке не разрешено получать фокус.

Сетка вызывает функцию SelectCell, чтобы увидеть, является ли ячейка фокусируемой: если эта функция возвращает true, то целевая ячейка, идентифицированная с аргументами aCol и aRow, является фокусируемой (текущая реализация TCustomGrid просто возвращает true). TCustomDrawGrid и, следовательно, TDrawGrid и TStringGrid переопределяют этот метод, чтобы сначала проверить, [является ли] ячейка шире, чем 0; обычно вы не хотите, чтобы ячейка с нулевой шириной 0 выбиралась, [другими словами, вы хотите,] чтобы ячейка с этими характеристиками пропускалась автоматически в процессе поиска подходящей ячейки. Другая вещь, которую переопределяет метод SelectCell, - это вызов настраиваемое пользователем событие OnSelectCell: это событие принимает координаты ячейки как аргументы и всегда возвращает значение по умолчанию true.

Как только [становится] известно, что ячейка [является] фокусируемой, и мы уверены, что движение будет иметь место, сначала вызывается метод BeforeMoveSelection; это по очереди вызывает событие OnBeforeSelection. Аргументами этого метода являются координаты для новой сфокусированной ячейки. В этот момент [также будет] скрыт любой видимый редактор. Слово "перед" означает, что выбор еще не изменен, и к текущим сфокусированным координатам можно получить доступ по свойствам grid.Col и grid.Row.

После этого меняются координаты внутренней сфокусированной ячейки, а затем вызывается метод MoveSelection; цель этого метода заключается в том, чтобы инициировать событие OnSelection, если оно установлено (это уведомление, что сфокусированная ячейка к этому времени уже изменилась и координаты ячейки теперь доступны через свойства grid.row и grid.col).

Обратите внимание, что неэффективно использовать событие OnSelectCell для обнаружения изменений фокуса ячейки, так как это событие будет срабатывать несколько раз даже для одной и той же ячейки в процессе поиска подходящей ячейки. Для этого лучше использовать события OnBeforeSelection или OnSelection.

Различия с Delphi

  • Поведение SelectCell и OnSelectCell, вероятно, различны - в действительности не могу судить о различиях. В Lazarus они используются в таких функциях, как AutoAdvance, которая, насколько я знаю, не существует в Delphi.

Когда встроенных свойств недостаточно: наследники сетки

Наследники сетки обычно должны переопределять следующие методы:
DrawAllRows: Отрисовывать все видимые строки
DrawRow: Отрисовывать все ячейки в строке.
DrawRow отрисовывает все ячейки в строке, предварительно проверив, находится ли ячейка в области отсечения, и только [потом] рисует ячейку, если она есть.
DrawCell:
DrawCellGrid:
DrawCellText:
DrawFocusRect:

(напишите мне).

Что происходит в методе TCustomGrid.Paint?

В следующем списке показан внутренний порядок вызовов методов для отрисовки TCustomGrid (или потомков). Каждый элемент в списках представляет вызовы методов во время операции отрисовки. Это должно помочь найти правильный точку для изменения поведения, когда дело доходит до [написания] наследников TCustomGrid.

  • DrawEdges: Рисует внешнюю границу сетки.
  • DrawAllRows (virtual): Рисует все строки в видимой части сетки. Это единственный метод, вызываемый в процессе отрисовки, который объявлен virtual.
    • DrawRow (virtual): Вызывается для каждой строки внутри текущего окна просмотра.
      • DrawCell (virtual): Сначала вызывается для каждой "нормальной" (т.е. не фиксированной) ячейки в строке.
        • PrepareCanvas (virtual): Устанавливает стили рисования холста в соответствии с визуальными свойствами текущей ячейки.
        • DrawFillRect: Рисует фон ячейки со стилями, установленными в PrepareCanvas.
        • DrawCellGrid (virtual): Рисует линии границы ячейки.
      • DrawFocusRect (virtual): В TCustomGrid этот метод ничего не делает. В наследниках сетки этот метод используется для рисования прямоугольника фокуса внутри активной ячейки.
      • DrawCell (virtual): (см. выше) Вызывается для каждой фиксированной ячейки в видимом окне просмотра строки.
  • DrawColRowMoving: активен только при перемещении столбца или строки. Этот метод рисует линию, которая указывает новую позицию строки/столбца.
  • DrawBorder: Если необходимо (Flat=TRUE и BorderStyle=bsSingle), это отрисовывает внутреннюю линию границы.

Дополнительные методы для рисования TCustomGrid

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

  • DrawCellText (virtual): Записывает/отрисовывает текст, который передается в качестве параметра в ячейку. Текст отформатирован с использованием стилей, которые активны в Canvas.TextStyle (см. также PrepareCanvas).
  • DrawThemedCell (virtual): Используется только для фиксированных ячеек и если TitleStyle=tsNative. [Метод] отрисовывает фон ячейки, используя ThemeServices.
  • DrawColumnText (virtual): Используется только для ячеек заголовков столбцов.
    • DrawColumnTitleImage: Если сетке сопоставлен TitleImageList и Columns.Title[x].Title.ImageIndex содержит допустимое значение, [указанное в ImageIndex] изображение отрисовывается внутри ячейки.
    • DrawCellText : (см. выше)
  • DrawTextInCell (virtual): Используется для "нормальных" (т.е. не фиксированных) ячеек. В TCustomGrid этот метод ничего не делает. В наследуемых сетках этот метод используется для отрисовки содержимого ячеек. В TStringGrid метод вызывает DrawCellText (см. выше).

Методы рисования, введенные TCustomDrawGrid

Как вы можете видеть, базовый класс TCustomGrid не выводит никакого содержимого в ячейки. Это делается в TCustomDrawGrid. TCustomDrawGrid переопределяет метод DrawCell следующим образом:

  • DrawCell (override):
    • PrepareCanvas: вызывает унаследованный метод из TCustomGrid.
    • DefaultDrawCell (virtual): Этот метод вызывается только в том случае, если обработчик события для OnDrawCell НЕ назначен.
      • DrawFillRect / DrawThemedCell (von TCustomGrid): Рисует фон ячейки. Если TitleStyle=tsNative, фон фиксированных ячеек рисуется с помощью DrawThemedCell.
      • DrawCellAutonumbering (virtual): Если [свойство] goFixedAutonumbering [включено] в [набор] Options, номера строк отображаются в первом фиксированном столбце [с помощью метода] TCustomGrid.DrawCellText.
      • DrawColumnText (из TCustomGrid): Вызывается только для ячеек заголовков столбцов (см. «Дополнительные методы отрисовки в TCustomGrid»).
      • DrawTextInCell (из TCustomGrid): Вызывается только для "нормальных" (т.е. не фиксированных) ячеек (см. «Дополнительные методы отрисовки в TCustomGrid»).
    • DrawCellGrid (virtual): Рисует границы ячейки.

Сохранение и получение содержимого сетки

Методы SaveToFile и LoadFromFile позволяют сетке сохранять и извлекать ее макет и данные в/из файла формата XML. TStringGrid, как унаследованный от TCustomStringGrid, также имеет возможность "экспортировать" и "импортировать" свой контент в файл формата Comma Separated Values, наиболее известный как CSV-файлы. Это описано в ссылке методов SaveToCSVFile и LoadFromCSVFile.

Тип информации, которая может быть сохранена, а затем извлечена при использовании SaveToFile и LoadFromFile, определяется свойством SaveOptions (тип TSaveOptions), которое представляет собой набор параметров, описанных ниже:

soDesign:     Сохраняет и загружает ColCount,RowCount,FixedCols,FixedRows,
              ColWidths, RowHeights и Options (TCustomGrid)
soPosition:   Сохраняет и загружает Scroll position, Row, Col и Selection (TCustomGrid)
soAttributes:  Сохраняет и загружает Colors, Text Alignment и Layout, и т.д. (TCustomDrawGrid)
soContent:    Сохраняет и загружает текст (TCustomStringGrid)
soAll:        набор всех параметров (soAll:=[soDesign,soPosition,soAttributes,soContent];)

Не все параметры применимы ко всем типам сеток, например, soContent не применяется к наследникам сеток TCustomGrid, таким как TDrawGrid, поскольку такая сетка не имеет понятия "контент". TStringGrid - это специальный вид сетки, который "знает", как обрабатывать строки, и может использовать параметр soContent, если он указан.

Опция soAttributes не используется в стандартных сетках Lazarus, [она предназначена] для наследников сеток.

При использовании LoadFromFile сетка также использует свойство SaveOptions, чтобы знать, какая информация должна извлекаться из файла, поэтому вполне возможно указать SaveOptions:=[soDesign,soContent] при сохранении и только SaveOptions:=[soContent] при загрузке.

Для TStringGrid значение SaveOptions по умолчанию - [soContent], для других видов сеток SaveOptions - это пустой набор.

На заметку: Одна из распространенных проблем при сохранении и извлечении данных сетки возникает [тогда], когда пользователь определяет свойство SaveOptions перед [методом] SaveToFile, но не раньше [вызова метода] LoadFromFile. При использовании LoadFromFile через некоторое время после SaveToFile, свойство SaveOptions устанавливается должным образом, но если LoadFromFile выполняется при следующем запуске программы, свойство SaveOptions может настроиться неправильно, по этой причине рекомендуется всегда указывать свойство SaveOptions непосредственно перед [вызовом метода] LoadFromFile или делать это глобально при запуске программы, как в следующем примере:


Пример:

  1. Сначала перейдите в меню "File -> New -> Application";
  2. Бросьте пустой TStringGrid в форму;
  3. Бросьте TButton и TOpenDialog на форму;
  4. Добавьте событие OnCreate для формы;
  5. Добавьте событие OnClick для кнопки.


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
 //устанавливаем SaveOptions во время создания формы
 stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
 //Спрашиваем, был ли запущен метод Execute [компонента] OpenDialog 
 //когда это происходит, пользователь выбирает файл XML для загрузки 
 //имя, которое было сохранено в объявлении FileName.

 if opendialog1.Execute then
 begin
   //Очищаем сетку 
   StringGrid1.Clear;
   //Загружаем XML
   StringGrid1.LoadFromFile(OpenDialog1.FileName);
   //Обновляем сетку
   StringGrid1.Refresh;
 end;
end;

initialization
  {$I unit1.lrs}

end.

Образец xml-файла: (Скопируйте текст ниже в текстовый файл. Не забудьте поставить заголовок xml :-))

<?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>

Редактор ячеек сетки

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

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

  1. показать диалоговое окно открытия файла, чтобы найти местоположение файла, чтобы пользователь не вводил полный путь вручную
  2. если текст в ячейке представляет дату, [вызвать] всплывающий календарь, чтобы мы могли легко выбрать конкретную дату.

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

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

Встроенный редактор ячейки

Модуль grids.pas уже включает некоторые из наиболее используемых редакторов ячеек, готовых для использования в сетках. Также возможно создавать новые редакторы ячеек (настраиваемые редакторы ячеек), если встроенные редакторы не подходят для конкретной задачи.

Встроенные редакторы ячеек - это Button, Edit и Picklist.

Использование редакторов ячейки

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

  1. Using a custom column and selecting the ButtonStyle property of the column (использование настраиваемого столбца и выбор свойства ButtonStyle для столбца). В этом методе пользователь может выбрать стиль редактора, который будет показан. Доступными значениями являются: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn.
  2. Using OnSelectEditor grid event (Использование события сетки OnSelectEditor). Здесь пользователь указывает в параметре Editor, какой редактор использовать для ячейки, обозначаемой столбцом aCol и строкой ARow наследуемой сетки TCustomDrawGrid или TColumn в TCustomDBGrid. Для этой цели существует полезная public-функция сетки EditorByStyle(), которая принимает в качестве параметра одно из следующих значений: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn. Этот метод имеет приоритет над первым при использовании пользовательских столбцов. Редактор пользовательских ячеек можно указать здесь (см. главу 5 этого документа (автор Michaël Van Canneyt), в которой объясняется, как правильно реализовывать пользовательский редактор). Это событие также позволяет настраивать редактор со значениями, специфичными для ячейки, строки или столбца (например, пользовательское всплывающее меню для редактора ячейки).

Установка свойства ButtonStyle работает только в том случае, если столбец создан с помощью инструкции StringGrid1.Columns.Add. Использование выражения типа StringGrid1.ColCount:=X; вызовет исключение. Установка свойства ButtonStyle может быть выполнена с помощью подобного кода:

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

Описание стилей редактора

Ниже приводится описание стилей редактора. Они являются значениями перечисляемого типа TColumnButtonStyle и поэтому имеют префикс 'cbs'. Этот тип использовался, чтобы оставаться совместимым с DBGrid Delphi.

  • cbsAuto
Это стиль редактора по умолчанию для сеток-наследников TCustomGrid. Фактический класс редактора, который будет использоваться для редактирования содержимого ячейки, зависит от нескольких факторов. Для TCustomGrids он использует класс TStringCellEditor, полученный из TCustomMaskEdit. Этот редактор специализирован для редактирования строк одной строки. Затем он используется по умолчанию в TStringGrid и TDrawGrid. При использовании пользовательских столбцов, если программист заполнил свойство PickList столбца, [редактор] ведет себя так, как если бы был установлен стиль редактора cbsPickList. Для TCustomDBGrid, который имеет поле типа boolean, он ведет себя так, как если бы был указан стиль редактора cbsCheckBoxColumn. Это рекомендуемое значение для Custom Cell Editors. TODO: сослаться на OnEditingDone.
  • cbsEllipsis
Этот стиль редактора является наиболее общим. При [его] использовании в ячейке редактирования появляется кнопка, и программисты могут использовать событие сетки OnEditButtonClick, чтобы определить, когда пользователь нажал кнопку, и выполнить любое действие, запрограммированное для такой ячейки. Например, программист может использовать этот стиль редактора, чтобы открыть диалог календаря, чтобы пользователь мог легко выбрать конкретную дату. Другие возможности могут заключаться в том, чтобы показать диалог открытия файла для поиска файлов, калькулятор, чтобы пользователь мог ввести числовой результат вычислений и т.д.
OnEditButtonClick - это просто уведомление, чтобы узнать, в какой ячейке нажата кнопка, взглянув на свойства grid.Row и grid.Col.
DBGrid имеет определенные свойства для извлечения активного столбца или поля, и, поскольку это событие происходит в активной записи, оно может обновлять информацию в активном поле.
Этот стиль редактора реализован с использованием TButtonCellEditor, прямого потомка TButton.
  • cbsNone
Этот стиль редактора указывает, что сетка не должна использовать какой-либо редактор для конкретной ячейки или столбца; [это поведение аналогично тому], как если бы такая ячейка или столбец сетки были только для чтения.
  • cbsPickList
Используется для представления пользователю списка значений, которые могут быть введены. Этот стиль редактора реализован с использованием TPickListCellEditor, компонента, полученного из TCustomComboBox. Список отображаемых значений заполняется одним из двух способов в зависимости от метода, используемого для выбора стиля редактора.
  1. При использовании настраиваемых столбцов программисты могут вводить список значений, используя свойство PickList столбца. [ДЛЯ НОВИЧКОВ: TODO: точная процедура для редактирования списка]
  2. В OnSelectEditor программисты получают экземпляр TPickListCellEditor, используя функцию EditorByStyle(cbsPickList). См. пример здесь
Значение в сетке TStringGrid будет автоматически отображать выбранное значение. При необходимости программист может определить момент, когда значение выбрано, путем написания обработчика для события OnPickListSelect сетки, поэтому могут быть предприняты дополнительные шаги (например, для обработки нового значения). TODO: связать с OnEditingDone.
  • cbsCheckboxColumn
Это может быть полезно, когда содержимое данных, связанное с столбцом, ограничено парой значений, например да-нет, true-false, вкл-выкл, 1-0 и т.д. Вместо того, чтобы принуждать пользователя вводить значения для данных такого типа в StringCellEditor или выбирать один из списка, cbsCheckboxColumn используется для изменения данных столбца с помощью представления флажка, которое пользователь может переключать, используя щелчок мыши или нажимая клавишу SPACE (если столбец, содержащий этот флажок, а также StringGrid доступны для редактирования).
Получение или установка значения флажка в ячейке осуществляется следующим образом:
StringGrid1.Cell[x,y]:='Z';
где Z заменяется на 0 для Unchecked, 1 для Checked и пустую строку для Grayed. Обратите внимание, что любое значение (строка), отличное от 0 и 1, будет отображаться как серый сheckbox.
Если для свойства столбцов ButtonStyle установлено значение cbsAuto, и DBGrid обнаруживает, что поле, связанное с столбцом, является логическим полем, то сетка автоматически использует этот стиль редактора. Этот автоматический выбор может быть отключен или активирован с использованием свойства OptionGxtra DBGrid; установка элемента dgeCheckboxColumn в false отключает эту функцию.
Значения, используемые для распознавания checked или unchecked состояний, задаются в свойствах столбца ValueChecked и ValueUnchecked [соответственно].
В любой момент значение поля может находиться в одном-трех состояниях: Unchecked, Checked или Grayed. Внутренне эти состояния идентифицируются следующими значениями типа TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked и gcbpGrayed.
Этот стиль редактора не использует реальные компоненты TCheckbox для обработки взаимодействия с пользователем: визуальное представление задается тремя встроенными растровыми изображениями, которые соответствуют возможным состояниям флажка. Использованные растровые изображения можно настроить, написав обработчик для события OnUserCheckboxBitmap [компонента] DBGrid; обработчик этого события получает состояние флажка в параметре CheckedState типа TDBGridCheckboxState и параметр растрового изображения, который программист может использовать для указания пользовательских растровых изображений.
  • cbsButtonColumn
Этот стиль редактора используется для отображения кнопки в каждой ячейке в столбце. Как и в случае cbsCheckboxColumn, этот редактор не использует реальные кнопки, внешний вид определяется текущей темой widgetset.
Пользователь знает, какая конкретная кнопка была нажата благодаря обработчику OnEditButtonClick сетки и проверке столбца и строки. Обратите внимание, что в этом конкретном случае столбец и строка сетки идентифицируют не текущую выбранную ячейку, а ячейку нажатой кнопки. Как только событие OnEditButtonClick будет обработано, и если пользователь не изменил столбец или строку в этом обработчике, сетка автоматически сбрасывает столбец и строку, чтобы отобразить выбранную ячейку. Пока обработчик OnEditButtonClick текущего выделения сетки доступен в grid.Selection, который является свойством TRect, левое и правое [значения] представляют [собой] индексы столбцов, а верхний и нижний - индексы строк.
Заголовок кнопки - соответствующая строка ячейки.

Редактирование сеток

Эффект редактирования отличается в зависимости от того, какая сетка используется, например, TStringGrid сохраняет отредактированный текст внутри, а TDBGrid влияет на записи в наборе данных. TDrawGrid "не знает", что делать с отредактированным текстом: если программист не контролирует его, [отредактированный текст] просто отбрасывается.
Для TDrawGrid (хотя это должно работать для всех классов сетки), чтобы получить доступ к отредактированному тексту, программист может использовать событие OnSetEditText, которое запускается каждый раз, когда пользователь что-то изменяет в редакторе, параметры aCol и Row этого события определяют редактируемую ячейку, параметр value содержит [редактируемый] текст - это рекомендуемый метод. Другой способ доступа к отредактированному тексту - прием его непосредственно из редактора после завершения процесса редактирования с использованием события OnEditingDone. Это можно сделать, обратившись к внутреннему редактору, используемому для редактирования, в данном случае, по умолчанию "String Cell Editor" [("Редактору строк ячеек")]. Для этого существует два метода: первый - с использованием события OnSelectEditor, где параметр Editor - это экземпляр, который будет использоваться для редактирования ячейки; вы должны сохранить этот экземпляр в переменной, например TheEditor (типа TStringCellEditor), для последующего использования в OnEditingDone. Второй вариант - использование метода EditorByStyle сетки, этот метод принимает "стиль редактора" в качестве параметра и возвращает экземпляр редактора, соответствующего этому стилю. В нашем случае, зная, что стиль cbsAuto возвращает редактор ячейки по умолчанию, в обработчике события OnEditingDone можно использовать непосредственно TheEditor:=Grid.EditorByStyle(cbsAuto). Затем вы можете получить текст с помощью TheEditor.Text. Если вы [будете] использовать этот метод, обратите внимание на то, что "великая сила несет большую ответственность".

Параметры и свойства, которые влияют на редактирование

Поведение редактирования контролируется рядом свойств и параметров, фактически любая настройка или параметры, которые влияют на редактирование, требуют редактирования сетки или такая настройка может быть проигнорирована.
При запуске редактируемое состояние сетки различно [в разных видах сетках], поскольку редактирование TDrawGrid и TStringGrid отключено, для TDbGrid включено по умолчанию.
Свойство Options имеет несколько элементов, которые касаются редактирования, они описаны ниже:

  • goEditing, dgEditing (в DbGrid). Эта опция изменяет редактируемое состояние сетки, ее можно изменить во время выполнения, поскольку она проверяет, когда начнется редактирование.
  • goAlwaysShowEditor. Обычно редактор скрыт и становится видимым только тогда, когда это необходимо. С помощью этой опции редактор будет отображаться все время, если сетка не редактируется, этот параметр игнорируется, и редактор всегда скрыт.

Хинты (подсказки) ячейки

В дополнение к стандартным хинтам большинства элементов управления потомки TCustomGrid могут отображать специальную подсказку для ячейки, над которой курсирует мышь. Эта функция управляется параметрами сетки goCellHints и goTruncCellHints (или dgCellHints и gdTruncCellHints для DBGrid соответственно):

  • goCellHints оценивает событие OnGetCellHint для извлечения отображаемого текста для ячейки, указанной переданными параметрами. В случае TDrawGrid и TStringGrid задаются индексы столбцов и строк, тогда как в случае TDBGrid текст ячейки может быть извлечен из поля, назначенного указанному объекту столбца.
  • goTruncCellHints отображает подсказку только в том случае, если текст не помещается [полностью] в ячейку и [поэтому] усечен; текст подсказки - это не обрезанный ячейкой текст. В Lazarus 1.9+ этот текст передается событию OnGetCellHint для дальнейшего уточнения (например, отображает весь текст memo в подсказке). В случае обрезания ячейкой текстов рекомендуется также активировать опцию goCellEllipsis (или dgCellEllipsis для DBGrid), которая добавляет «...» к обрезанному ячейкой тексту.

Если сетка имеет значение в своем стандартном свойстве Hint, тогда свойство HintPriority можно использовать для управления тем, как все эти тексты подсказок могут быть объединены в одно всплывающее окно:

  • chpAll помещает стандартную подсказку в первую строку, подсказку, определяемую событием OnGetCellHint во вторую строку, и подсказку для усеченных ячеек в третью строку
  • chpAllNoDefault отображает подсказки только ячейки, заданные событием OnGetCellHint, и усеченными ячейками.
  • chpTruncOnly отображает подсказки только для усеченных ячеек; это настройки по умолчанию.

В дополнение, стандартное свойство ShowHint сетки должно быть установлено в true, чтобы отображать подсказки на ячейки.

"Как сделать..." и Примеры

Фокусировка ячейки

Фокусировка ячейки в TStringGrid очень проста. Обратите внимание, что подсчет начинается с нуля, а не 1. Таким образом, чтобы перевести фокус на строку 10, столбец 9, выполните:

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

Пример: Как настроить собственный редактор ячеек.

см. lazarus/examples/gridexamples/gridcelleditor/gridcelleditor.lpi (начиная с laz 1.2)

Пример: Как установить memo-редактор для dbgrids

Разумеется, вы можете использовать другой контрол вместо TMemo. Откорректируйте [на свой] вкус. Адаптирован из [1] (упрощенное использование свойства SelectedFieldRect, см. [2])

  • Поместите элемент управления memo (или любой другой контрол, который вы хотите) на свою форму, установите любые свойства, которые вы хотите, и установите значение visible:= false. Этот [контрол] будет использоваться при редактировании ячейки сетки. В этом примере мы будем использовать GridCellMemo.
  • В событии OnSelectEditor поместите следующий код - адаптируйте, если вы не хотите редактировать логический столбец 3, физический столбец 4:
if (Column.DesignIndex = 3) then
  begin
      GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
      GridCellMemo.Text:=Column.Field.AsString;
      Editor := GridCellMemo;
  end;
  • Предположим, что ваш источник данных называется Datasource1, а DBGrid называется ResultsGrid. Тогда в событии OnEditingDone для GridCellMemo введите следующее:

(В исходном сообщении форума используется событие OnChange, которое будет срабатывать при каждом изменении содержимого. Использование OnEditingDone запускается только после того, как пользователь закончил свои правки.)

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

Осторожно Использование обычного элемента управления в качестве настраиваемого редактора имеет множество недостатков: при изменении размеров столбцов или скроллинге сетки он не будет изменять размер или положение, вам придется позаботиться о том, чтобы изменить данные в сетке (как объяснялось выше), он не работает должным образом с DbGrids и препятствует нормальной навигации по сетке с помощью клавиатуры. Сетка взаимодействует с редактором, используя некоторые специальные сообщения сетки, поэтому лучше [выделить этот контрол в] подкласс для упорядочивания управления этими сообщениями, как описано в пятой главе этого документа.

Осторожно Если у вас более одной сетки в одной и той же форме, используйте разные настраиваемые редакторы для каждой из них: если вы [будете] использовать один и тот же элемент управления, начнут происходить плохие вещи с того момента, когда обе сетки попытаются использовать его одновременно.

Пример: как добавить кнопку редактора

// Условно показываем кнопку редактора в столбцах индекс 1 или 2, если
// ячейка в столбце index 1 пуста
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;

// Запуск действия ...
procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
begin
  if StringGrid1.Col = 1 then Showmessage('щелкнут редактор столбца 1');
  if StringGrid1.Col = 2 then Showmessage('щелкнут редактор столбца 2');
end;

Пример: Запрет отображения текстовых полей как "(Memo)" для DBGrids

Когда вы используете SQL-выражения, такие как "Select * from A", вы можете увидеть "(Memo)" вместо своего содержимого в ячейке сетки. Есть много способов исправить это, но, может быть, самым простым является изменение вашего выражения "Select * from A" на "Select Cast(Column as TEXT) as Column from A". Это обычная проблема при использовании компонента DBGrid.

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

Пример: Работа с Picklist, Как сделать его только для чтения и Как заполнить его в runtime.

Используя событие сетки OnSelectEditor, вы можете настроить, как поведет себя редактор PickList (см. стиль кнопки cbsPickList). В следующем примере редактор списка выбора из столбца 1 изменяется на нечетных строках таким образом, что пользователь может вводить значения, набирая [их с клавиатуры], а на четных строках [вводимые] значения ограничиваются теми, которые содержатся в его списке. Кроме того, в этом примере показано, как заполнить список разными значениями в зависимости от обрабатываемой строки.

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;

Выравнивание текста в StringGrids

Этот код показывает, как использовать разное выравнивание текста в столбцах 2 и 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;

Многострочность в Grids, DBGrid

В этом примере показано, как сделать многострочный текст в ячейке [3,2]. Он работает так же для DBGrid, где [событие] OnPrepareCanvas имеет параметры для работы с столбцами, а оттуда и с 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;

Проверка введенных значений

Lazarus версии 0.9.29 для StringGrid ввел событие OnValidateEntry типа TValidateEntryEvent, которое имеет следующее объявление:

TValidateEntryEvent =
  procedure(sender: TObject; aCol, aRow: Integer;
            const OldValue: string; var NewValue: string) of object;

где

aCol,aRow являются координатами проверяемой ячейки.
OldValue это значение, которое было в ячейках [aCol, aRow] до начала редактирования.
NewValue это значение, которое будет окончательно вставлено в ячейки [aCol, aRow].

Из-за того, что StringGrid работает, устанавливая значение ячейки во время редактирования пользователем (см. событие OnSetEditText и метод SetEditText сетки), то когда событие OnValidateEntry запускается, ячейка уже содержит введенное значение (корректное или нет); используя аргументы события OldValue и NewValue, значение ячейки может быть проверено и изменено, если необходимо.

Обычно проверка выполняется, когда пользователь перемещается [с редактируемой ячейки] в другую. Если проверка закончилась неудачей, желательно сохранить редактор ячейки видимым/сфокусированным, чтобы введенное значение могло быть исправлено пользователем. Чтобы сетка "узнала", что проверка не прошла, необходимо создать исключение. Сетка будет обрабатывать исключение из Application.HandleException, и любое перемещение отменится.

Например, предположим, что ячейка [1,1] должна содержать только значения 'A' или 'B', [тогда] валидация может быть выполнена с помощью:

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
      // устанавливаем новое допустимое значение, чтобы пользователь мог просто нажать RETURN для продолжения, например
      NewValue := 'A';
      // другой вариант - вернуться предыдущему значению ячейки (которое считается допустимым)
      // NewValue := OldValue;
      // Используйте EAbort, а не другой тип исключения, чтобы избежать [возникновения] ложных
      // диалоговых окон ошибок и [диалоговых окон] обратной отладки
      raise EAbort.Create('Разрешены только A или B');
    end else begin
      // если исключение не создано, предполагается, что значение действительно, но при необходимости
      // окончательное значение все равно можно изменить, заполнив NewValue другим значением
       
      // компьютер знает лучше :)
      if grid.Cells[1,1]='A' then 
        NewValue := 'B' 
      else 
        NewValue := 'A';
    end;
  end;
end;

Сортировка столбцов или строк

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

В коде вы можете использовать метод SortColRow().

Первый параметр IsColumn - это логическое значение, которое указывает:

  • True если будет отсортирован столбец
  • False если будет отсортирована строка

Второй параметр, index, является целым значением:

  • Index указывает колонку или строку для сортировки.

Последние два параметра являются необязательными, [они] определяют поддиапазон строк или столбцов для сортировки.

  • FromIndex (От столбца или строки)
  • ToIndex (До столбца или строки)

Если последние два параметра не указаны, сортируется весь столбец или строка.

Сортировка использует алгоритм QuickSort, его можно изменить, если сетка-наследник переопределяет метод sort() и вызывает doCompareCells для сравнения с ячейкой.

По умолчанию [метод SortColRow()] сортирует содержимое ячейки как строки в порядке возрастания или убывания, которое можно выбрать с помощью свойства SortOrder. По умолчанию он использует порядок [сортировки] по возрастанию.

// Сортировка столбца 3 в порядке возрастания
grid.SortColRow(true, 3);

// Сортировка столбца 3 в порядке убывания, пропустить фиксированные строки сверху
grid.SortOrder := soDescending; // или soAscending
grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);

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

procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol, BRow: Integer; var Result: integer);
begin
  // Результат будет либо <0, = 0, либо >0 для нормального порядка.
  result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
  // Для обратного порядка просто [присвойте] негативный результат (напр., на основе SortOrder сетки).
  if StringGrid1.SortOrder = soDescending then
    result := -result;
end;

Вы также можете использовать OnCompareCells, когда автоматическая сортировка столбцов включена через свойство ColumnClickSorts.

Переопределить автоматическую сортировку (ColumnClickSorts установлен в False). Исключить сортировку некоторых столбцов. Показать стрелки сортировки в заголовке столбца

(ColumnClickSorts, установленный в False, НЕ будет показывать стрелки сортировки в заголовке столбца, поэтому мы должны установить стрелки.)

procedure TForm1.StringGrid1HeaderClick(Sender: TObject; IsColumn: Boolean;
 Index: Integer);
begin
 if isColumn then with StringGrid1 do
  begin
    col:=Index;                                                  // Задаем столбец для сортировки
    if Tag  = 0 then Tag :=-1                                    // Предположим, что данные в столбце возрастают
                else Tag := Tag  * -1;                           // Используем StringGrid1.Tag для хранения порядка сортировки  
                                                                 // Первый щелчок заголовка сортирует по убыванию 
   
     // Активируем CompareCells (активируем сортировку)                                                                            
    case Index of
      0: col:=-1;                                                // Не сортируем столбец 0 и не показываем стрелку сортировки
      1: SortColRow(True, Index);                                // Сортировка будет происходить в CompareCells, как только будет выполнен оператор
      2: SortColRow(True, Index);
      3: SortColRow(True, Index);
      4: SortColRow(True, Index);
      5: col:=-1;                                                // Не сортируем столбец 5 и не показываем стрелку сортировки
    end;
  end;
end;

// Compare возвращает одно из следующих значений:
// if A<B  return: -1            ( Восходящий )
// if A=B  return:  0
// if A>B  return:  1            ( Нисходящий )

// Как сортировать: Text, Integer, Float, DateTime:

procedure TForm1.StringGrid1CompareCells(Sender: TObject; ACol, ARow, BCol,
 BRow: Integer; var Result: integer);

  function CompareTextAsText: Integer;                         // Text
  begin
    CompareText(Cells[ACol,ARow], Cells[BCol,BRow]);
  end;

  function CompareTextAsInteger: Integer;
  var i1,i2:Integer;
  begin
    if TryStrToInt(StringGrid1.Cells[ACol,ARow],i1) and       // Integer
       TryStrToInt(StringGrid1.Cells[BCol,BRow],i2) then
    Result := CompareValue(i1, i2)
    else Exit(0);
  end;  

  function CompareTextAsFloat: Integer;                        // Float
  var f1,f2:Extended;
  begin
    if TryStrToFloat(StringGrid1.Cells[ACol,ARow],f1) and
       TryStrToFloat(StringGrid1.Cells[BCol,BRow],f2) then
    Result := CompareValue(f1, f2)
    else Exit(0);
  end;

  function CompareTextAsDateTime: Integer;                  // DateTime
  var
  D1, D2 : TDateTime;
  begin
    if Trim(StringGrid1.Cells[ACol,ARow]) <> Trim(StringGrid1.Cells[BCol,BRow]) then
    begin
      if trim(StringGrid1.Cells[ACol,ARow]) = '' then Exit(1);
      if Trim(StringGrid1.Cells[BCol,BRow]) = '' then Exit(-1);
    end
    else Exit(0);

    if TryStrToDate(StringGrid1.Cells[ACol,ARow], D1, 'yy/mm/dd') and
       TryStrToDate(StringGrid1.Cells[BCol,BRow], D2, 'yy/mm/dd') then
    Result := CompareDateTime(D1, D2)
    else Exit(0);
 end;

begin                                        // Не делайте здесь ничего другого, сортировка может быть выполнена некорректно
  With    StringGrid1 do
  begin
    case ACol of
      0 : ;                                                                     // Не сортируйте столбец 0
      1 :  Result := CompareTextAsText;                                         // Text
      2 :  Result := CompareTextAsInteger;                                      // Integer
      3 :  Result := CompareTextAsFloat;                                        // Float
      4 :  Result := CompareTextAsDateTime;                                     // DateTime
    end; {Case}
  end;
  if  StringGrid1.Tag  = -1 then   Result := -Result;                           // Переключаем порядок сортировки
end;  

// DrawCell сделает трюк, предыдущие изображения стрелки будут удалены: 
procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  aRect: TRect; aState: TGridDrawState);
   
   procedure DrawArrow(ImgIndex:Integer);                  // Нужны стрелки вверх/вниз в ImageList
   begin
     with StringGrid1 do
       TitleImageList.Draw(Canvas,                         // TitleImageList из StringGrid
       ARect.Right - TitleImageList.Width,
      (ARect.Bottom - ARect.Top - TitleImageList.Height) div 2 + 2,  // Положение стрелки, может быть отрегулировано
       ImgIndex );
   end;
begin
  if Col > 0 then                                    // В этом демо нет стрелок в col 0  
    if (ARow = 0) and                                // Предположим, строка 0 в качестве заголовка строк
       Assigned(TitleImageList) then                 // Нужны стрелки вверх/вниз в ImageList
         case Columns.Items[aCol].Tag of             // Использовать тег столбца как индекс Восходящей/Нисходящей стрелки
           -1: DrawArrow(1);                         // Заменяем Index указателем стрелки вниз
            0: DrawArrow(-1);                        // Удаляем предыдущее изображение
            1: DrawArrow(2);                         // Заменяем Index указателем стрелки вверх
         end;
end;

Сортировка столбцов или строк в DBGrid со стрелками сортировки в заголовке столбца

Вот пример, который будет сортировать DBgrid, используя событие OnTitleClick и TSQLQuery и индексы. Это также должно работать для любого совместимого набора данных, такого как TbufDataset. Функция использует свойство column.tag для хранения состояния сортировки для каждого столбца, на который вы нажимаете, поэтому, когда вы вернетесь к тому [столбцу], который вы уже отсортировали, [заголовок столбца] отобразит правильную стрелку сортировки для отображения.

Предпосылки:

  • Для хранения [изображений] стрелок вверх/вниз вам понадобится imagelist. Присвойте свой imagelist свойству TitleImageList dbgrid'а.
  • Убедитесь, что TSQLQuery.MaxIndexesCount достаточно велик, чтобы хранить новые индексы, которые вы будете создавать. Для этого примера я установил значение 100.
  • Вам также понадобится переменная-поле [(в разделе интерфейса private)] для хранения последнего столбца, который используется для сортировки, для этого примера я назвал его FLastColumn.

В этом примере используемый TSQLQuery называется OpenQuery.

Light bulb  Примечание: По состоянию на 21 марта 2013 года TSQLQuery не имеет возможности очищать индексы, но в качестве обходного пути вы можете установить [значение его свойства] unidirectional в true, а затем в false: это очистит индексы.

Чтобы повторно использовать компонент TSQLQuery с другим оператором SQL, индексы должны быть очищены после сортировки, [иначе] при открытии TSQLQuery с помощью другого оператора SQL будет вызвано исключение.

FLastColumn: TColumn; //сохраняем последний столбец сетки, который мы отсортировали

procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
const
  ImageArrowUp=0; //должен соответствовать изображению в imagelist
  ImageArrowDown=1; //должен соответствовать изображению в imagelist
var
  ASC_IndexName, DESC_IndexName:string;
  procedure UpdateIndexes;
  begin
    // Обеспечиваем обновление индексов
    OpenQuery.IndexDefs.Updated:=false; {<<<--Эта строка - решающая. IndexDefs.Update не будет
    обновляться, если уже true, что произойдет в первом отсортированном столбце.}
    Openquery.IndexDefs.Update;
  end;
begin
  ASC_IndexName:='ASC_'+Column.FieldName;
  DESC_IndexName:='DESC_'+Column.FieldName;
  // индексы не могут сортировать двоичные типы, такие как ftMemo, ftBLOB
  if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
    exit;
  // проверяем, существует ли восходящий индекс для этого столбца
  // если нет, создадим [его]
  if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
  begin
    OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
    UpdateIndexes; //обеспечиваем обновление индексов
  end;
  // проверяем, существует ли нисходящий индекс для этого столбца
  // если нет, создадим [его]
  if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
  begin
    OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
    UpdateIndexes; //обеспечиваем обновление индексов
  end;

  // Используем тег столбца для переключения 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;
  // Удаляем стрелку сортировки из предыдущего столбца, который мы отсортировали
  if (FLastColumn <> nil) and (FlastColumn <> Column) then
    FLastColumn.Title.ImageIndex:=-1;
  FLastColumn:=column;
end;

Выбор записей в DBGrid с использованием checkbox'ов

Цель состоит в том, чтобы иметь возможность выбирать произвольные записи в dbgrid с помощью checkbox'ов, сетка имеет возможность показывать checkbox'ы автоматически, когда обнаруживает наличие логических полей, а для других типов полей пользователь может вручную [в свойстве] ButtonStyle столбца выбрать [значение] cbsCheckboxColumn. Для таких столбцов пользователь просто помечает checkbox, и содержимое поля изменяется соответствующим образом.

Но что произойдет, если в нашем наборе данных нет такого доступного поля? или мы не хотим, чтобы сетка входила в состояние редактирования при изменении состояния checkbox'а?. Добавление столбца без полей с помощью ButtonStyle=cbsCheckboxColumn покажет все checkbox'ы с неопределенным значением и отключенными, потому что нет поля, связанного с этим столбцом, и поэтому ничего не нужно изменять. Поскольку мы хотим самостоятельно обрабатывать состояние checkbox'а, нам нужно где-то сохранить состояние для каждой записи. Для этого мы можем использовать класс TBookmarklist (определенный в модуле dbgrids.pas), где свойство CurrentRowSelected может определить, выбрана ли текущая запись или нет. Используя события dbgrid'а OnCellClick и OnUserCheckboxState, мы можем отслеживать состояние checkbox'а.

Обратите внимание, что этой методике нужен Lazarus r31148 или более поздней версии, которая реализует событие 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;

Подсветка выделенного столбца и строки ячейки

В версии Lazarus 40276 добавлена опция (gdRowHighlight), она работает аналогично goRowSelect, но использует более светлый тон для выделения, а прямоугольник фокуса - только выбранная ячейка, а не в целая строка. Это отлично работает для строк, но как насчет столбцов?

В этом разделе показан способ выделения столбцов, строк или и того, и другого (пример, который выделяет только столбцы и заголовки строк, можно найти в lazarus/examples/gridexamples/spreadsheet, этот способ действительно является расширением этого примера). Он использует два события сетки: OnBeforeSelection и OnPrepareCanvas, рисунок не нужен.

Событие OnBeforeSelection запускается, когда начинается смена выделения, в этом случае мы можем узнать, какая ячейка выбрана в данный момент и какая ячейка будет выбрана следующей. Мы используем эту информацию для перерисовки всей строки или столбца как старой, так и новой ячеек. Когда начнется следующий цикл отрисовки, сетке будет предложено перерисовать ячейки, принадлежащие недействительным областям. Одним из первых шагов для перерисовки является вызов события OnPrepareCanvas (если он существует) для настройки свойств холста по умолчанию. Мы используем это событие для настройки выделенной строки или столбца:

procedure TForm1.gridBeforeSelection(Sender: TObject; aCol, aRow: Integer);
begin
  // мы можем здесь принять решение, если хотим выделить столбцы, строки или и те, и другие
  // в этом примере мы выделяем и те, и другие
  if Grid.Col<>aCol then
  begin
    // обнаружено изменение текущего столбца
    grid.InvalidateCol(aCol);      // перерисовываем новый выбранный столбец ячейки
    grid.InvalidateCol(grid.Col);  // перерисовываем текущий (это будет 'старый') выбранный столбец
  end;
  if Grid.Row<>aRow then
  begin
    grid.InvalidateRow(aRow);      // перерисовываем новую выбранную строку ячейки
    grid.InvalidateRow(grid.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; // это подсветило бы также заголовки столбцов или строк
  end else
  if gdFocused in aState then begin
    // мы оставляем в покое текущую выделенную/сфокусированную ячейку
  end else
  if (aCol=Grid.Col) or (aRow=grid.Row) then
    grid.Canvas.Brush.Color := clSkyBlue; // подсветка строк и столбцов цветом clSkyBlue
end;

DBGrids "Как сделать..." и Примеры

Пример: как установить редактор memo для dbgrids

Конечно, вы можете использовать другой элемент управления вместо TMemo. Отрегулируйте по вкусу. Адаптировано из [3] (упрощено использование свойства SelectedFieldRect, см. [4])

  • Поместите элемент управления memo (или любой другой элемент управления) на форму, установите любые свойства, которые вы хотите, и установите видимым значение false. Он будет использоваться при редактировании ячейки сетки. Мы будем использовать GridCellMemo в этом примере.
  • В событии OnSelectEditor вставьте следующий код - адаптируйте, если вы не хотите редактировать логический столбец 3, физический столбец 4:
  if (Column.DesignIndex = 3) then
  begin
      GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
      GridCellMemo.Text:=Column.Field.AsString;
      Editor := GridCellMemo;
  end;
  • Предположим, ваш источник данных называется Datasource1, а DBGrid называется ResultsGrid. Затем в событие OnEditingDone для GridCellMemo поместите следующее:

(В исходном сообщении на форуме использовалось событие OnChange, которое будет запускаться каждый раз при изменении содержимого. Использование OnEditingDone срабатывает только после того, как пользователь завершит редактирование.)

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

Осторожно Использование обычного элемента управления в качестве пользовательского редактора имеет много недостатков: он не будет изменять размеры или перемещать себя при изменении размера столбцов или прокрутке сетки, вы должны позаботиться о модификации данных в сетке. (как объяснено выше), он не работает должным образом с DbGrids и мешает нормальной навигации по сетке с помощью клавиатуры. Сетка взаимодействует с редактором с помощью некоторых специальных сообщений сетки, поэтому для управления этими сообщениями лучше выделить подкласс элемента управления, как объясняется в пятой главе этого документа.

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

Пример: исключение отображения текстовых полей как «(Memo)» для DBGrids

Когда вы используете операторы SQL, такие как "Select * from A", вы можете увидеть «(Memo)» вместо текстового содержимого в ячейке сетки. Есть много способов исправить это, но, возможно, самый простой - изменить выражение "Select * from A" на Select Cast(Column as TEXT) as Column from A. Это распространенная проблема при использовании компонента DBGrid.

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

Сортировка столбцов или строк в DBGrid со стрелками сортировки в заголовке столбца

Вот пример, который будет сортировать DBgrid, используя событие OnTitleClick и TSQLQuery и индексы. Это также должно работать для любого совместимого набора данных, такого как TbufDataset. Функция использует свойство column.tag для хранения состояния сортировки для каждого столбца, по которому вы щелкнули, поэтому, когда вы вернетесь к тому, который уже отсортирован, она выберет правильную стрелку сортировки для отображения.

Предварительные условия:

  • Вам понадобится список изображений для хранения стрелок вверх/вниз. Присвойте свой список изображений свойству dbgrid TitleImageList.
  • Убедитесь, что свойство TSQLQuery.MaxIndexesCount достаточно велико для хранения новых индексов, которые вы будете создавать. Для этого примера у меня было установлено значение 100.
  • Вам также понадобится поле-переменная в разделе private класса для хранения последнего столбца, использованного для сортировки, для этого примера я назвал ее FLastColumn.

В этом примере используемый TSQLQuery назван OpenQuery.

Light bulb  Примечание: по состоянию на 21 марта 2013 года TSQLQuery не имеет возможности очищать индексы, но в качестве обходного пути вы можете установить свойство unidirectional в значение true, а затем установить его в значение false, и это очистит индексы.

Чтобы повторно использовать компонент TSQLQuery с другим оператором SQL, индексы должны быть очищены после сортировки, иначе будет возбуждено исключение при открытии TSQLQuery с другим оператором SQL.

FLastColumn: TColumn; //сохраняем последний столбец сетки, по которому мы делали сортировку

procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
const
  ImageArrowUp=0; //должно соответствовать изображению в imagelist
  ImageArrowDown=1; //должно соответствовать изображению в imagelist
var
  ASC_IndexName, DESC_IndexName:string;
  procedure UpdateIndexes;
  begin
    // Убедитесь, что индексы обновлены
    OpenQuery.IndexDefs.Updated:=false; {<<<--Эта строчка имеет решающее значение. IndexDefs.Update не будет обновляться, если уже имеет значение true, что произойдет в первом отсортированном столбце.}
    Openquery.IndexDefs.Update;
  end;
begin
  ASC_IndexName:='ASC_'+Column.FieldName;
  DESC_IndexName:='DESC_'+Column.FieldName;
  // индексы не могут сортировать двоичные типы, такие как ftMemo, ftBLOB
  if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
    exit;
  // проверяем, существует ли восходящий индекс для этого столбца.
  // если нет, создаем
  if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
  begin
    OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
    UpdateIndexes; //ensure index defs are up to date
  end;
  // проверяем, существует ли нисходящий индекс для этого столбца.
  // если нет, создаем
  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;

  // Используем тег столбца для переключения направления сортировки 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;
  // Удаляем стрелку сортировки из предыдущего столбца, который мы отсортировали
  if (FLastColumn <> nil) and (FlastColumn <> Column) then
    FLastColumn.Title.ImageIndex:=-1;
  FLastColumn:=column;
end;

Выбор записей в DBGrid с помощью флажков(чекбоксов)

Цель состоит в том, чтобы иметь возможность выбирать произвольные записи в dbgrid с помощью флажков, сетка имеет возможность автоматически отображать флажки, когда обнаруживает наличие логических полей, для других типов полей пользователь может вручную выбрать значение cbsCheckboxColumn свойства ButtonStyle для столбца. Для этого типа столбцов пользователь просто устанавливает флажок, и содержимое поля изменяется соответствующим образом.

Но что произойдет, если в нашем наборе данных нет такого доступного поля? Или мы не хотим, чтобы сетка входила в состояние редактирования при установке флажка? При добавлении столбца без полей с помощью ButtonStyle = cbsCheckboxColumn все флажки будут выделены серым цветом и отключены, поскольку поле, связанное с этим столбцом, отсутствует и поэтому изменять нечего. Поскольку мы хотим обрабатывать состояние флажка самостоятельно, нам нужно хранить состояние где-то для каждой записи. Для этого мы можем использовать класс TBookmarklist (определенный в модуле dbgrids.pas), где свойство CurrentRowSelected может сказать, выбрана текущая запись или нет. Используя события dbgrid OnCellClick и OnUserCheckboxState, мы можем отслеживать состояние флажка.

Light bulb  Примечание: эта хитрость требует наличие Lazarus r31148 или новее, который реализует событие 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;

Пример: Как экспортировать данные из DBGrid в текстовый файл

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

Но что произойдет, когда вам понадобится текстовое представление, которое должно соответствовать внешнему виду сетки: видимость колонок, ширина или порядок?

Одним из решений является использование пакета LazReport, в котором есть компонент, специально предназначенный для создания отчета из DbGrid. Затем этот отчет можно экспортировать в несколько других форматов, включая текстовый формат. Здесь представлено простое решение, которое также может быть использовано в качестве основы для других экспортеров текстового формата, например, простого экспортера HTML.

// aGrid: Сетка для экспорта
// lpp: строк на странице. Не включает текстовый заголовок
// pageBreak: вставляет разрыв страницы, который некоторые принтеры могут использовать для запуска новой страницы
// require: uses LazUTF8
function GridTextExporter(aGrid: TDbGrid; lpp: Integer; pageBreak:boolean): TStringList;
const
  PX_PER_CHAR = 7;

var
  Ds: TDataset;
  book: TBookMark;
  recCount: Integer;
  line: string;
  column: TColumn;

  function WidthToChar(aWidth: Integer): integer;
  begin
    result := trunc(aWidth/PX_PER_CHAR);
  end;

  procedure AddNext(theText: string; alignment: TAlignment);
  var
    width: Integer;
  begin
    if (line<>'') and (line<>#12) then
      line := line + ' ';

    width := WidthToChar(Column.Width);

    case Alignment of
      taRightJustify: line := line + UTF8PadLeft(theText, width);
      taCenter:       line := line + UTF8PadCenter(theText, width);
      else            line := line + UTF8PadRight(theText, width);
    end;
  end;

  procedure CollectHeader;
  begin
    AddNext(Column.Title.Caption, Column.Title.Alignment);
  end;

  procedure CollectField;
  var
    field: TField;
  begin
    field := Column.Field;

    if (field.DataType=ftMemo) and (dgDisplayMemoText in aGrid.Options) then
      AddNext(field.AsString, Column.Alignment)
    else if Field.DataType<>ftBlob then
      AddNext(field.DisplayText, Column.Alignment)
    else
      AddNext('(blob)', Column.Alignment);
  end;

  procedure LoopColumns(printingRecord: boolean);
  var
    c: TCollectionItem;
  begin
    if (not printingRecord) and pageBreak and (result.count>0) then
      line := #12
    else
      line := '';
    for c in aGrid.Columns do begin
      Column := TColumn(c);
      if Column.Visible and (Column.Width>=PX_PER_CHAR) then begin
        if printingRecord then CollectField
        else                   CollectHeader;
      end;
    end;
    result.add(line);
  end;

begin
  result := TStringList.create;
  Ds := aGrid.DataSource.DataSet;
  Ds.DisableControls;
  book := Ds.GetBookmark;
  try
    Ds.First;
    recCount := 0;
    while not DS.EOF do begin
      if RecCount mod lpp = 0 then
        LoopColumns(false);
      LoopColumns(true);
      inc(recCount);
      Ds.Next;
    end;
  finally
    Ds.GotoBookmark(book);
    Ds.FreeBookmark(book);
    Ds.EnableControls;
  end;
end;

Чтобы использовать это просто сделайте:

var
  L: TStringList;
begin
  L := GridTextExporter(grid, 80, true);
  L.SaveToFile('grid.txt');
  L.Free;
end;