Sign in Join
Wiki copytemporary

Grids Reference Page/ru: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Цель
22
33Этот текст попытается показать пользователю некоторые аспекты использования компонентов сеток (Grid) в Lazarus. Он также предназначен для использования в качестве руководства для пользователей, которые никогда не использовали сетки (опытные пользователи тут обычно получают только информацию по новой функциональности).
44
55Таким образом, этот текст пытается достичь следующих целей:
66
771. Введение в сеточные компоненты для людей с небольшим опытом или без опыта после работы с Delphi.
882. Документирование различий в использовании компонентов сеток в Delphi.
993. Документирование новой функциональности сеток в Lazarus
10104. Описание по созданию и примеры использования таких компонентов в Lazarus
1111
1212## Введение
1313
1414Как компонент сетка обеспечивает средства отображения данных в табличном формате. Наиболее очевидным характеристика сетки является то, что они состоят из клеток, образующих строки и столбцы.
1515
1616Тип информации, которая может быть показана в сетке различна и зависит главным образом от того, что пользователь хочет, чтобы сета отобразила. Вообще эта информация может состоять из текста, цвета, изображения или их комбинации.
1717
1818Учитывая большое разнообразие информации, которая может быть представлена, существует набор различных сеток, цель которых заключается в содействии пользователю в представлении разных видов информации.
1919
2020Например, есть сетка предназначенная для отображения текста: **StringGrid**. Документация для этой сетки может быть найдена [здесь](http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tstringgrid.html)
2121
2222## Дерево наследования
2323
24-`                     `[[TCustomControl]]`           `\
25-`                             |                    `\
26-`                             |                    `\
27-`                        TCustomGrid               `\
28-`                             |                    `\
29-`               +-------------+------------+       `\
30-`               |                          |       `\
31-`         TCustomDrawGrid             TCustomDbGrid`\
32-`               |                          |       `\
33-`      +--------+--------+                 |       `\
34-`      |                 |              `[[TDBGrid]]`    `\
35-`  `[[TDrawGrid]]`      TCustomStringGrid                `\
36-`                        |                         `\
37-`                        |                         `\
38-`                   `[[TStringGrid]]
24+```text
25+ TCustomControl
26+ |
27+ |
28+ TCustomGrid
29+ |
30+ +-------------+------------+
31+ | |
32+ TCustomDrawGrid TCustomDbGrid
33+ | |
34+ +--------+--------+ |
35+ | | TDBGrid
36+ TDrawGrid TCustomStringGrid
37+ |
38+ |
39+ TStringGrid
40+```
3941
4042## Начальный пример
4143
4244Поскольку одной из целей этой страницы является помощь людям с небольшими или отсутствующими предшествующими знаниями Lazarus'а, давайте сделаем быстрый стартовый пример сетки в действии. Почему бы и нет, давайте сделаем традиционный пример "hello world", используя компонент TStringGrid.
4345
44461. Создайте новое приложение.
4547 - В главном меню выберите: project-\>New Project
4648 - В диалоговом окне Create New Project нажмите кнопку "Create"
4749 - Будет показана новая пустая форма..
48502. Поместите сетку в форму
4951 - В палитре компонентов выберите вкладку "additional"
5052 - Нажмите значок TStringGrid ![](/wiki-images/6/67/Tstringgrid.png)
5153 - Нажмите на форму рядом с левым верхним углом. Появится новая пустая сетка.
52543. Поместите кнопку в форму
5355 - с помощью палитры компонентов выберите вкладку "Standard"
5456 - Нажмите значок TButton ![](/wiki-images/b/b2/Tbutton.png)
5557 - Нажмите на пустую область формы. Появится новая кнопка.
56584. Дважды нажмите кнопку из шага 3 и запишите следующий код в обработчике щелчка кнопки:
5759 - ``` pascal
5860 StringGrid1.Cells[1,1] := 'Привет, Мир!';
5961 ```
60625. Запустите программу, щелкнув значок воспроизведения ![](/wiki-images/8/85/Menu_run.png)
6163 - по нажатию кнопки Button1, текст приветствия будет отображаться в столбце 1 ячейки 1 (обратите внимание, что левый столбец и верхняя строка равны 0)
6264
6365## Различия между сетками Lazarus и Delphi
6466
6567Текущий компонент сетки отличается от сетки Delphi по нескольким направления. При начальной разработке сетки Lazarus создавались с нуля без какой-либо попытки сделать их полностью совместимыми с Delphi.
6668
6769На более позднем этапе совместимость с сетками Delphi стала желаемой целью, и сетки Lazarus стали более тесно соответствовать интерфейсу сетки Delphi. Однако даже это было сделано без попытки сделать каждое свойство или метод сетки Lazarus сопоставимым с его аналогом в Delphi. Кроме того (поскольку внутренняя структура сетки Lazarus сильно отличается от внутренних элементов сетки Delphi), некоторая функциональность Delphi невозможна или должна быть эмулирована иначе в сетке Lazarus \[в отличие от того\], как это будет сделано в сетке Delphi. Мы достигли большей совместимости с Delphi в процессе эволюции сетки Lazarus, и это соблазнительно.
6870
6971### Различия
7072
7173Известные различия перечислены ниже, ни в каком специальном порядке.
7274
7375- Редакторы ячеек
7476- Поведение в designtime
7577- Отрисовка ячеек имеет некоторые отличия, см. раздел настройки сетки.
7678
7779### Новая функциональность
7880
7981- Пользовательские столбцы
8082- События
8183- Редактор сетки
8284
8385### Способы, которыми вы можете сделать сетку Lazarus более совместимой с Delphi
8486
8587Вы можете сделать сетку Lazarus похожей и ведущей себя подобно \[сетке\] Delphi. Ниже перечислены параметры свойств, которые \[позволят\] достигнуть этого. Эти корректировки основаны на вновь созданной сетке. \[Свойства,\] помеченные тэгом \[Code\], должны быть определены в коде, тэгом \[Design\] могут быть изменены во время разработки.
8688
8789- \[Design\] TitleStyle := tsStandard;
8890- \[Design\] DefaultRowHeight := 24;
8991- \[Code\] EditorBorderStyle := bsNone; // это может работать только в Windows.
9092- \[Code\] UseXORFeatures := true;
9193- \[Code\] AllowOutBoundEvents := False; {SVN-ревизия 10992 или старше}
9294- \[Code\] FastEditing := False; (поддерживается в dbgrid. StringGrid требует SVN-ревизию 10992 или старше)
9395- \[Design\] AutoAdvance := aaNone;
9496
9597## Справочник по сеткам
9698
9799### Информация
98100
99101Отправной точкой для \[получения\] справки о TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid и TStringGrid является [справка по модулю Grids.pas](http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html)
100102
101103Для TCustomDBGrid и TDBgrid это [справка по модулю DBGrids.pas](http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html)
102104
103105В общем, любая ссылка Delphi о сетках должна помочь нам использовать сетки Lazarus (но не забывайте, что есть несколько различий между сетками Delphi и Lazarus, которые мы задокументировали); имея это в виду и в качестве временного места для справочной информации, это место будет использоваться для документирования тех вещей, которые не работают аналогично Delphi, а также любых новых функций.
104106
105107TODO: остальная часть этого раздела должна исчезнуть; его содержание будет перенесено в [справку по модулю Grids.pas](http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html)
106108
107109### TCustomGrid
108110
109111См. полную [[doc:lcl/grids/tcustomgrid.html|справку TCustomGrid]]
110112
111113#### свойство AllowOutboundEvents
112114
113115Protected\[-свойство\] в TCustomGrid, public\[-свойство\] в TCustomDrawGrid и его наследниках. Обычно, когда пользователь нажимает в точку над пустым пространством после ячеек (например, если сетка имеет три строки, но пользователь нажимает на воображаемую четвертую строку), текущий фокус ячейки перемещается ячейку, ближайшую к только что щелкнутой точке. Мы называем это исходящим событием. Значение по умолчанию - true, так как это изначальное поведение сетки. Это свойство было добавлено для моделирования поведения Delphi, когда исходящие события недоступны, поэтому для обеспечения совместимости с Delphi это свойство имеет значение false.
114116
115117#### свойство Columns
116118
117119Lazarus включает свойства **columns** в сетки TStringGrid и TDrawGrid. Это свойство добавляет так называемые пользовательские столбцы. Пользовательские столбцы представляют собой набор объектов, которые содержат свойства, применяемые к целым столбцам, например заголовки столбцов (для StringGrid он переопределяет значение, указанное в соответствующем свойстве Cells \[ColumnIndex, RowTitle\]), выравнивание текста, цвет фона, предпочтительный редактор и т.д.
118120
119121Пользовательские столбцы добавляют дополнительные свойства или заменяют значения свойств по умолчанию в столбцах стандартной сетки. Но не только это; значение **grid.ColCount** может увеличиваться или уменьшаться, чтобы учесть количество настраиваемых столбцов, которые привязаны к сетке. На этом этапе это означает, что **grid.ColCount** = **grid.FixedCols** + **grid.Columns.Count**.
120122
121123Например, если к базовой сетке с ColCount=5 и FixedCols=2 мы:
122124
123125- Добавим 3 пользовательских столбца, базовая сетка будет точно такой же, как раньше, 2 фиксированных столбца и 3 обычных столбца.
124126- Добавим 1 пользовательский столбец, базовая сетка будет иметь ColCount=3, то есть 2 фиксированных столбца и 1 нормальный столбец.
125127- Добавим 4 пользовательских столбца, базовая сетка будет иметь ColCount=6, то есть 2 фиксированных столбца и 4 нормальных столбца.
126128
127129Из этого можно сделать вывод, что:
128130
129131- Свойства фиксированного столбца или счетчики не увеличиваются или не изменяются соответственно пользовательскими столбцами.
130132- **grid.ColCount** обычно отличается от **grid.Columns.Count** (**grid.ColCount**=**grid.Columns.Count**, только когда **FixedCols**=0)
131133
132134Во время разработки пользователь может получить доступ к свойству **columns** в Object Inspector, чтобы вызвать редактор столбцов. Оттуда вы можете добавлять, удалять или изменять настраиваемые столбцы. Редактор отображает список текущих пользовательских столбцов; путем выбора элементов в этом списке инспектор объектов заполняется свойствами, доступными для каждого столбца. Список настраиваемых столбцов также доступен в представлении дерева компонентов Object Inspector, где столбцы можно добавлять, удалять или изменять. Они появляются на нижнем уровне под сеткой контейнера.
133135
134136В runtime столбцы могут быть изменены с помощью кода:
135137
136138``` pascal
137139 var
138140 c: TGridColumn;
139141 begin
140142 // Добавляем пользовательский столбец в сетку
141143 c := Grid.Columns.Add;
142144 // модифицируем
143145 c.title.caption := 'Цена'; // Устанавливаем заголовок столбца
144146 c.align := taRightJustify; // Выравниваем содержимое столбца по правому краю
145147 c.color := clMoneyGreen; // Изменяем цвет по умолчанию на clMoneyGreen
146148 c.Index := 0; // Делаем этот столбец первым
147149 // доступ к существующему столбцу
148150 grid.columns[0].Width := 60; // Изменяем ширину столбца 0 на 60 пикселей
149151 // удаляем существующий столбец
150152 grid.columns.delete(0); // Удаляем столбец 0
151153 ....
152154 end;
153155```
154156
155157Кроме того, когда задействуются пользовательские столбцы, сетки не допускают прямой модификации \[свойства\] **grids.colcount**; добавление или удаление столбцов должно выполняться с использованием свойства **columns**. Объяснение состоит в том, что существует некоторая несогласованность в постепенном удалении пользовательских столбцов с использованием **ColCount**: когда \[значение\] **ColCount** становится равным **FixedCols**, \[это означает, что\] сетка не имеет больше настраиваемых столбцов. Если теперь увеличить **ColCount**, новый столбец будет не настраиваемым, а нормальным столбцом.
156158
157159В настоящее время нет планов, чтобы сетки использовали только настраиваемые столбцы.
158160
159161#### свойство AutoFillColumns
160162
161163Часто сетки шире, чем горизонтальное пространство, необходимое для столбцов - это оставляет неприятную пустую область справа от последнего столбца. Сетки LCL предоставляют механизм для расширения ширины указанных столбцов, так что пустое пространство заполняется автоматически.
162164
163165Для этой цели необходимо либо добавить **Columns**, как описано [[Grids Reference Page/ru|выше]], либо установив свойство `ColCount`, и свойство сетки `AutoFillColumns` должно быть установлено в `true`. Каждый столбец имеет свойство `SizePriority`. Если оно имеет значение `0`, ширина столбца берется из свойства `Width` столбца. Но когда оно имеет ненулевое значение, ширина столбца корректируется до среднего доступного размера, оставшегося для всех столбцов с ненулевым `SizePriority`.
164166
165167### TCustomDBGrid
166168
167169TCustomDBGrid основан на TDBGrid.
168170
169171Они не отображают свойств Col и Row. Чтобы перейти к определенному столбцу, используйте, например, свойство SelectedIndex.
170172
171173Интересным public-методом является AutoSizeColumns.
172174
173175#### процедура AutoSizeColumns
174176
175177Эта процедура устанавливает ширину столбца по размеру максимально широкого найденного текста. Его можно использовать после загрузки набора данных/установки \[свойства\] Active.
176178
177179Однако, в противоположность \[процедуре\] TCustomStringGrid.AutoSizecolumns (см. ниже), эта \[процедура\] будет устанавливать столбцы максимально широко, пока вы не задействуете \[параметр грида\] **dgAutoSizeColumns**.
178180
179181#### процедура InplaceEditor
180182
181183См. пример из bug 23103 - *и вставьте объяснение того, что он делает/почему это необходимо. Проверить входные значения? Изменить то, что показано?*
182184
183185``` pascal
184186procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: char);
185187var
186188 S: String;
187189begin
188190 if (Key in [',','.']) then
189191 begin
190192 //в отличие от Delphi не все InPlaceEditor'ы - это редакторы для строкового типа, поэтому проверьте!
191193 if (DBGrid1.InplaceEditor is TStringCellEditor) then
192194 begin
193195 S := TStringCellEditor(DBGrid1.InplaceEditor).EditText;
194196 if Pos(',',S) > 0 then
195197 Key := #0
196198 else
197199 Key := ',';
198200 end;
199201 end;
200202end;
201203```
202204
203205### TCustomStringGrid
204206
205207TCustomStringGrid служит основой для TStringGrid. Он может использоваться для \[написания\] производных компонентов \[типа\] TStringGrid, которые хотят скрыть published-свойства. Дополнительную информацию см. в [[new intermediate grids]].
206208
207209Следующие public-свойства или public-методы являются доступными для TStringGrid.
208210
209211См. полную [[doc:lcl/grids/tcustomstringgrid.html|справку TCustomStringGrid]]
210212
211213#### процедура AutoSizeColumn(aCol: Integer);
212214
213215Эта процедура устанавливает ширину столбца \[в соответствии с\] размером самого широкого текст, который был ею найден во всех строках столбца **aCol**. Совет: см. параметр **goDblClickAutoSize**, чтобы позволить автоматически изменять размер столбцов при двойном нажатии на границу столбца.
214216
215217#### процедура AutoSizeColumns;
216218
217219Автоматически изменяет размеры всех столбцов так, чтобы они растягивались под самый длинный текст для каждого столбца. Это быстрый способ применения AutoSizeColumn() для каждого столбца в сетке.
218220
219221#### процедура Clean; overload;
220222
221223Очищает все ячейки в сетке, \[будь то\] фиксированные или нет.
222224
223225#### процедура Clean(CleanOptions: TGridZoneSet); overload;
224226
225227Очищает все ячейки в сетке в соответствии с данными CleanOptions. Дополнительную информацию см. в [[doc:lcl/grids/tgridzoneset.html|TGridZoneSet]]. Некоторые примеры:
226228
227229- Очистить все ячейки: `grid.Clean([]);` (аналогично `grid.clean`)
228230- Очистить все нефиксированные ячейки: `grid.Clean([gzNormal]);`
229231- Очистить все ячейки, но не касается заголовков столбцов сетки: `Grid.Clean([gzNormal, gzFixedRows]);`
230232
231233#### процедура Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TGridZoneSet); overload;
232234
233235делает то же самое, что и `Clean(CleanOptions: TGridZoneSet)`, но ограничивается данными `StartCol, StartRow, EndCol и EndRow`. Примеры:
234236
235237- Очистить индекс столбца с 4 по 6, но не касаться заголовков столбцов сетки, много вариантов:
236238
237239``` pascal
238240Grid.Clean (4,Grid.FixedRows, 6,Grid.RowCount-1,[]); //или Grid.Clean(4,0,6,Grid.RowCount-1, [gzNormal]);
239241```
240242
241243и т.п.
242244
243245#### процедура Clean(aRect: TRect; CleanOptions: TGridZoneSet); overload;
244246
245247То же самое, что и `Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions)`, просто берется \[прямоугольник\] TRect вместо отдельных координат ячейки. Для очистки выделения полезно \[сделать\]:
246248
247249``` pascal
248250grid.Clean(Grid.Selection,[]);
249251```
250252
251253==== процедура SaveToCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ==== Сохраняет содержимое сетки в файл формата данных с разделителями-запятыми (CSV) (добавлен в Lazarus с r32179).
252254
253255Аргумент **AFilename** указывает имя файла, в котором содержимое будет сохранено. Если файл существует, содержимое будет перезаписано. Если он не существует, файл будет создан.
254256
255257**ADelimiter** (необязательный аргумент) используется для предоставления настраиваемого разделителя, если требуется. По умолчанию создается формат CSV (то есть ADelimiter: = ',') для TAB-разделяемого файла ADelimiter должен быть \#9.
256258
257259Параметр **WithHeader** используется для определения необходимости включения или отключения "row header"\[(заголовка строки)\]. Заголовок строки - это список имен полей в начале выходного файла; его содержимое извлекается из последней фиксированной строки в сетке. Существует исключение из этого правила: если сетка имеет пользовательские столбцы, содержимое заголовка строки извлекается из заголовков пользовательских столбцов, а не из содержимого ячейки фиксированной строки.
258260
259261Если WithHeader имеет значение true, а сетка не содержит фиксированную строку или настраиваемые столбцы, содержимое заголовка строки будет взято из первой строки в сетке.
260262
261263Вывод CSV-данных обычно начинается с первой нефиксированной строки в сетке.
262264
263265==== процедура LoadFromCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ==== Загружает содержимое сетки из файла формата данных с разделителями-запятыми (CSV) (добавлен в Lazarus с r32179).
264266
265267Столбцы будут добавлены в сетку или удалены из нее по мере необходимости, в соответствии с количеством полей, включенных в каждую строку файла CSV. Загрузка CSV-файла не будет изменять количество фиксированных строк, которые уже существовали в сетке.
266268
267269Аргумент **Afilename** указывает имя исходного файла с содержимым CSV.
268270
269271Дополнительный параметр **ADelimiter** может использоваться для указания другого разделителя. Например: для файла с TAB-разделителями *ADelimiter* должен быть \#9. Другим популярным файловым форматом является файл с разделителем-запятой.
270272
271273Параметр **WithHeader** используется для определения того, следует ли считать первую строку в файле CSV "строкой заголовка" или нет. Если сетка имеет фиксированные строки, а *WithHeader* - \[значение\] true, заголовки столбцов для последней фиксированной строки будут взяты из строки заголовка. Однако, обратите внимание, что если сетка имеет пользовательские столбцы, строка заголовка будет использоваться как источник заголовков столбцов, а заголовки столбцов всегда \[будут\] отображаются в первой фиксированной строке сетки или \[будут\] скрыты, если в сетке не \[окажется\] фиксированных строк.
272274
273275Если в процедуре *LoadFromCSVFile* возникла проблема с загрузкой файла CSV (например, кавычки или пробелы начинают неправильно интерпретироваться), вы можете загрузить сетку вручную, например, [[CsvDocument/ru|CsvDocument]] ... и, конечно, патч для *LoadFromCSVFile* всегда приветствуется.
274276
275277#### свойство Cols\[index: Integer\]: TStrings read GetCols write SetCols;
276278
277279Получает/устанавливает список строк из/в индекса столбца данной сетки, начиная с индекса строки от 0 до RowCount-1.
278280
279281##### Примеры
280282
281283- Пример определения \[содержимого строки\]:
282284
283285``` pascal
284286//задать содержимое третьего столбца в сетке из списка ListBox
285287Grid.Cols[2] := ListBox1.Items;
286288```
287289
288290- Пример получения \[содержимого строки\]:
289291
290292``` pascal
291293//задать содержимое Listbox, [получив значение] из столбца сетки с индексом 4:
292294procedure TForm1.FillListBox1;
293295var
294296 StrTempList: TStringList;
295297begin
296298 StrTempList := TStringList(Grid.Cols[4]);
297299 if StrTempList<>nil then begin
298300 ListBox1.Items.Assign(StrTempList);
299301 StrTempList.Free;
300302 end;
301303end;
302304```
303305
304306##### На заметку
305307
306308Это свойство работает по-разному в Lazarus и Delphi при получении данных из сетки. В Lazarus создается временный объект TStringList для извлечения содержимого столбца. Пользователь должен освобождать этот объект после использования.
307309
308310Это также означает, что изменения в возвращенном списке не будут влиять на содержимое или макет сетки.
309311
310312См. пример получения \[содержимого строки\] выше.
311313
312314#### свойство Rows\[index: Integer\]: TStrings read GetRows write SetRows;
313315
314316Получает/определяет список строк из/в индекса строки данной сетки, начиная с индекса столбца 0 до столбца ColCount-1.
315317
316318##### На заметку
317319
318320Это свойство работает по-разному в Lazarus и Delphi при получении данных из сетки. В Lazarus создается временный объект TStringList для извлечения содержимого строки. Пользователь должен освобождать этот объект после использования.
319321
320322Это также означает, что изменения в возвращенном списке не будут влиять на содержимое или макет сетки.
321323
322324##### Примеры
323325
324326- Пример определения \[содержимого строки\]:
325327
326328``` pascal
327329//задать содержимое третьего столбца в сетке из списка ListBox
328330Grid.Rows[2] := ListBox1.Items;
329331```
330332
331333- Пример получения \[содержимого строки\]:
332334
333335``` pascal
334336//задать содержимое Listbox, [получив значение] из столбца сетки с индексом 4:
335337procedure TForm1.FillListBox1;
336338var
337339 StrTempList: TStringList;
338340begin
339341 StrTempList := TStringList(Grid.Rows[4]);
340342 if StrTempList<>nil then begin
341343 ListBox1.Items.Assign(StrTempList);
342344 StrTempList.Free;
343345 end;
344346end;
345347```
346348
347349- Пример, который не работает, и его исправление: список извлеченных строк - только для чтения
348350
349351``` pascal
350352// это не сработает и вызовет утечку памяти
351353// потому что возвращаемый StringList не освобождается
352354Grid.Rows[1].CommaText := '1,2,3,4,5';
353355Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g';
354356
355357//исправление первого случая
356358Lst:=TStringList.Create;
357359Lst.CommaText := '1,2,3,4,5';
358360Grid.Rows[1] := Lst;
359361Lst.Free;
360362```
361363
362364#### свойство UseXORFeatures;
363365
364366Boolean свойство, значение по умолчанию: False;
365367
366368Это свойство \[отвечает за то\], как прямоугольник фокуса отображается в сетке. Когда \[его значение равно\] True, прямоугольник отрисовывается \[вокруг ячейки\] с использованием растровой операции XOR. Это позволяет нам видеть прямоугольник фокуса, вне зависимости от того, какой цвет фона \[имеют\] ячейки. Когда \[его значение равно\] False, пользователь может управлять цветом точек фокуса прямоугольника с помощью свойства [[FocusColor property]]
367369
368370Он также контролирует внешний вид изменения размера столбца/строки. Когда \[его значение равно\] True, линия визуально показывает размер, который \[только еще\] будет иметь столбец или строка, если пользователь завершит операцию. Когда \[его значение равно\] False, изменение размера столбца или строки вступает в силу \[сразу же\], как \[только\] пользователь \[начнет\] перетаскивать мышь.
369371
370372### TValueListEditor
371373
372374TValueListEditor - это элемент управления, полученный из TCustomStringGrid для редактирования пар ключ-значение.
373375
374376#### свойство DisplayOptions
375377
376378Управляет различными аспектами внешнего вида TValueListEditor.
377379
378380#### свойство TitleCaptions
379381
380382Устанавливает значения подписи заголовков (если \[свойство\] **doColumnTitles** \[включено в набор свойств\] **DisplayOptions**). Если `DisplayOptions` не \[содержит\] значения `doColumnTitles`, то используются заголовки \[с надписями\] по умолчанию.
381383
382384#### свойство Strings
383385
384386Предоставляет доступ к списку строк, которые содержат пары Key-Value.\
385387Пары «ключ-значение» должны быть в форме:\
386388`KeyName=Value`
387389
388390#### свойство ItemProps
389391
390392Вы можете использовать это свойство для управления тем, как элементы в столбцах \[со значением\] "Value" можно редактировать.\
391393Это контролируется установкой свойств **EditStyle** и **ReadOnly** \[свойства\] **ItemProp**.
392394
393395#### свойство KeyOptions
394396
395397KeyOptions - это набор TKeyOptions, определяющий, может ли пользователь изменять содержимое столбца "Key".
396398
397399- KeyEdit: пользователь может редактировать имя ключа
398400- KeyAdd: пользователь может добавить ключи (нажав Insert в сетке). KeyAdd требует KeyEdit.
399401- KeyDelete: пользователь может удалить пары Key-Value (нажав Ctrl+Delete).
400402- KeyUnique: если установлено, то ключи должны иметь уникальные имена. Попытка ввести дубликат ключа вызовет исключение.
401403
402404#### свойство DropDownRows
403405
404406Если редактор ячейки является списком выбора (ValueEdit1.ItemProps\['key1'\].EditStyle=esPickList), это свойство устанавливает DropDownCount\[(высоту выпадающего)\] отображаемого списка. По умолчанию используется значение 8.
405407
406408#### функция DeleteRow
407409
408410Удаляет пару Key-Value индексированной строки, полностью удаляя строку.
409411
410412#### функция InsertRow
411413
412414Вставляет строку в сетку и устанавливает пару Key-Value. Возвращает индекс вновь вставленной строки.
413415
414416#### функция IsEmptyRow
415417
416418Возвращает \[значение\] true, если ячейки индексированных строк пусты (Keys\[aRrow\]=*; Valves\[aRrow\]=*).
417419
418420#### функция FindRow
419421
420422Возвращает строку с указанным именем ключа.
421423
422424#### функция RestoreCurrentRow
423425
424426Отменяет редактирование в текущей строке (если редактор все еще в фокусе). \[Событие\] случается, когда пользователь нажимает клавишу Escape.
425427
426428#### Измененное поведение некоторых свойств, унаследованных из TCustomStringGrid
427429
428430##### Параметры Options
429431
430432В соответствии с особенностями TValueListEditor его свойство Options имеет определенные ограничения
431433
432434- goColMoving не доступно в Options (вы не можете установить его).
433435- goAutoAddRows может быть установлен только в том случае, если KeyAdd отмечен в KeyOptions. Установка KeyAdd автоматически установит goAutoAddRows.
434436- goAutoAddRowsSkipContentCheck недоступен (на данный момент это приводит к сбою в TValueListeditor: требуется исправление).
435437
436438##### свойство FixedRows
437439
438440Может \[иметь значение\] только 1 (показать названия столбцов) или 0 (не показывать заголовки столбцов)
439441
440442##### свойство ColCount
441443
442444Всегда \[будет значение\] 2.
443445
444446#### Общие замечания по использованию TValueListEditor
445447
446448При манипулировании содержимым ValueListEditor (сетка) рекомендуется манипулировать лежащим в основе свойством Strings.\
447449Если вы хотите вставлять или удалять строки, то либо делайте это путем прямого доступа к свойству Strings, либо используйте общедоступные методы из `TValueListEditor`: `DeleteRow()`, `InsertRow()`, `MoveRow()` и `ExchangeRow()`.\
448450Попытка использовать методы предка для управления строками или столбцами (например, Columns.Add) может привести к сбою.
449451
450452#### Saving and loading the contenst of a TValueListEditor
451453
452454##### Using the Strings property
453455
454456If 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.\
455457In case of LoadFromFile the `RowCount` property will be autmatically adjusted.\
456458No sanity checks are performed upon LoadFromFile (so lines that do not represent a Key/Value pair will end up as Keys without Value).
457459
458460##### Using TValueList.SaveToFile and TValueList.LoadFromFile
459461
460462When using SaveToFile and LoadFromFile you get the additional benefits of also being able to save and load the layout, like with other grids.\
461463SaveToFiel will also save information about wether or not the TValueListEditor uses ColumnTitles, and if so, it saves them.\
462464
463465In 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.\
464466If the file does not seem to be a valid TValueListEditor grid file, and exception is raised.\
465467
466468TValueList.SaveToFile and TValueList.LoadFromFile do not work correctly in Lazarus versions \<= 2.1 r62044.
467469
468470##### Using SaveToCSVFile and LoadFromCSV file
469471
470472These methods should not be used at the moment, since for a TValueListEditor they are flawed.
471473
472474## Работа с сетками
473475
474476### Настройка сеток
475477
476478Сетка - это компоненты, полученные из класса [TCustomControl](http://lazarus-ccr.sourceforge.net/docs/lcl/controls/tcustomcontrol.html) и не имеющие родственного виджета, связанного с ними, что означает, что сетки не ограничены по виду текущей темы интерфейса. Это может быть как преимуществом, так и недостатком: обычно программисты хотят создавать приложение с одинаково выглядящим интерфейсом. Хорошей новостью является то, что сетки Lazarus достаточно гибки, чтобы получить что-то из обоих миров; программисты могут легко сделать сетки похожими на другие собственные элементы управления, или они могут настроить сетку в мельчайших деталях, чтобы они могли получать почти одинаковый вид в любом интерфейсе платформы или виджета (то есть, за исключением полос прокрутки, поскольку их внешний вид все еще определяется текущей темой).
477479
478480### Свойства и События для настройки сеток
479481
480482Некоторые свойства могут влиять на то, как выглядит сетка, действуя, когда ячейка собирается быть отрисованной в PrepareCanvas/OnPrepareCanvas, изменяя свойства холста по умолчанию, такие как цвет кисти или шрифт. Ниже приведен список таких свойств:
481483
482484- **AlternateColor.** Этим \[свойством\] пользователь может изменить цвет фона, появляющийся в чередующихся строках. Это позволяет легко считывать данные строк сетки.
483485- **Color.** Это \[свойство\] устанавливает основной цвет, используемый для отрисовки фона нефиксированных ячеек.
484486- **FixedColor.** Это цвет, используемый для создания фона фиксированных ячеек.
485487- **Flat.** Это \[свойство\] устраняет трехмерный вид фиксированных ячеек \[(т.е. делает их плоскими)\].\
486488- **TitleFont.** Шрифт, используемый для отрисовки текста в фиксированных ячейках.\
487489- **TitleStyle.** Это свойство изменяет трехмерный вид неподвижных ячеек, имеет 3 значения \[стилей\]:
488490 - *tsLazarus.* Это стандартный вид.
489491 - *tsNative.* Это \[свойство\] пытается установить внешний вид, соответствующий текущей теме виджетов.
490492 - *tsStandard.* Этот стиль представляет собой более контрастный вид, как у сетки Delphi.
491493- **AltColorStartNormal.** Boolean\[-свойство\]. Если \[имеет значение\] true: чередующийся цвет всегда находится во второй строке после фиксированных строк, первая строка после фиксированных строк будет всегда цветной. Если \[имеет значение\] false: цвет по умолчанию установлен в первую строку, как если бы не было фиксированных строк.
492494- **BorderColor.** Это \[свойство\] устанавливает цвет границы сетки, используемую, когда Flat:=True и BorderStyle:=bsSingle;
493495- **EditorBorderStyle.** Если установлено значение bsNone под \[ОС\] windows, редакторы ячейки не будут иметь границы, как в delphi по умолчанию для bsSingle, потому что граница может быть специфичной для темы в некоторых виджетах и обеспечивать единообразный внешний вид.\
494496- **FocusColor.** Цвет, используемый для отрисовки текущего фокуса ячейки, если \[свойство\] UseXORFeatures не установлено, по умолчанию это clRed.
495497- **FocusRectVisible.** Включает/Выключает отрисовку фокуса ячейки.
496498- **GridLineColor.** Цвет линий сетки в нефиксированной области.
497499- **GridLineStyle.** Стиль Pen используется для отрисовки линий в нефиксированной области, возможны следующие варианты: *psSolid*, *psDash*, *psDot*, *psDashDot*, *psDashDotDot*, *psinsideFrame*, *psPattern*,*psClear*. По умолчанию - *psSolid*.
498500- **SelectedColor.** Цвет, используемый для отрисовки фона ячейки на выбранных ячейках.
499501- **UseXORFeatures.** Если установлено, прямоугольник фокуса рисуется с использованием режима XOR, поэтому он должен сделать видимым прямоугольник фокуса в сочетании с любым цветом ячейки. Это также влияет на просмотр движущихся столбцов.
500502- **DefaultDrawing.** Boolean\[-свойство\]. Обычно сетки готовят холст сетки с использованием некоторых свойств в соответствии с видом окрашиваемой ячейки. Если пользователь пишет обработчик событий OnDrawCell, набор DefaultDrawing также рисует фон ячейки. Если пользователь сам рисует ячейку, лучше отключить это свойство, чтобы отрисовка не дублировалась. В StringGrid набор DefaultDrawing рисует текст в каждой ячейке.
501503- **AutoAdvance.** \[свойство определяет,\] куда установится курсор ячейки при нажатии \[клавиши\] enter или после редактирования.
502504- **TabAdvance.** \[свойство определяет,\] куда переместится курсор ячейки при нажатии \[клавиши\] Tab или Shift-Tab.
503505- **ExtendedColSizing.** Если \[значение равно\] true, пользователь может изменять размер столбцов не только в заголовках, но и по высоте столбцов.
504506
505507Другие свойства, которые также влияют на то, как выглядят сетки.
506508
507509**Options**.
508510
509511
510512Свойство Options - это набор некоторых элементов для обеспечения разнообразной функциональности, но некоторые из них напрямую связаны с внешним видом сетки. Эти параметры можно установить в режиме разработки(designtime) или времени выполнения(runtime).
511513
512514- **goFixedVertLine, goFixedHorzLine** \[свойство\] рисует вертикальную или горизонтальную линию, соответственно разграничивая ячейки или столбцы в фиксированной области, по умолчанию \[значение равно\] active.
513515- **goVertLine, goHorzLine** \[свойство аналогично\] предыдущему, но для нормальной просматриваемой области. Сетка может быть создана для имитации списка, \[если\] отключить оба этих элемента.
514516- **goDrawFocusSelected** если этот элемент включен, фон выделения отрисовывается в сфокусированной ячейке в дополнение к пунктирному прямоугольнику фокуса (обратите внимание, что это не работает, когда установлена опция goRowSelect, в этом случае строка всегда отрисовывается так, как если бы была выбрана опция goDrawFocusSelected)
515517- **goRowSelect** выбирает \[для выделения\] полную строку вместо отдельных ячеек
516518- **goFixedRowNumbering** если \[свойство\] установлено, сетка будет нумеровать строки в первом фиксированном столбце
517519- **goHeaderHotTracking** если \[свойство\] установлено, сетка попытается показать другой вид \[ячейки, когда\] курсор мыши будет находиться над любой фиксированной ячейкой. Для того, чтобы это сработало, желаемая зона ячейки должна быть включена с помощью свойства HeaderHotZones. Попробуйте комбинировать этот параметр с свойством TitleStyle:=tsNative, чтобы получить подсвечивание \[соответственно\] теме \[виджета\].
518520- **goHeaderPushedLook** если этот параметр установлен, этот элемент включает \[эффект\] "нажатия" при клике \[мышью\] любой фиксированной ячейки. Зона "нажатия" ячеек становится доступной при использовании свойства HeaderPusedZones.
519521
520522(напишите еще)
521523
522524### Описание процесса отрисовки сетки
523525
524526Как и другие настраиваемые элементы управления, сетка отрисовывается с использованием метода рисования. В общих чертах сетка отрисовывается путем рисования всех строк и каждой строки путем рисования ее отдельных ячеек.
525527
526528Процесс заключается в следующем:
527529
528530- Сначала определяется область видимых ячеек: каждая строка проверяется, пересекает ли она область отсечения холста; если все в порядке, то видимая область окрашивается путем рисования столбцов каждой строки.
529531- Значения столбцов и строк используются для идентификации ячейки, которая должна быть окрашена, и снова каждый столбец проверяется на пересечение с областью отсечения; если все в порядке, некоторые дополнительные свойства, такие как прямоугольная протяженность ячейки и визуальное состояние, передаются в качестве аргументов методу DrawCell.
530532- Когда процесс отрисовки выполняется, визуальное состояние каждой ячейки настраивается в соответствии с параметрами сетки и расположением в сетке. Визуальное состояние сохраняется в виде типа TGridDrawState, который представляет собой набор со следующими элементами:
531533 - *gdSelected* У ячейки будет выделенный вид.
532534 - *gdFocused* Ячейка будет иметь \[рамку\] фокуса.
533535 - *gdFixed* Ячейка будет нарисована \[как\] фиксированная.
534536 - *gdHot* \[когда\] мышь находится над этой ячейкой, то она отрисовывается с подсвечиванием.
535537 - *gdPushed* \[когда по\] ячейке \[кликают мышью\], она отрисовывается как нажимаемая \[(кликабельная)\]
536538- **DrawCell.** Метод DrawCell является виртуальным и может быть переопределен в унаследованных сетках для выполнения пользовательского рисования. Информация, переданная DrawCell, помогает идентифицировать конкретную ячейку, которая окрашивается, физическую область, занятую экраном, и ее видимый статус. Подробнее см. ссылку DrawCell. Для каждой ячейки происходит следующее:
537539- **PrepareCanvas.** В этом методе, если установлено свойство DefaultDrawing, холст сетки настраивается со свойствами по умолчанию для кисти и шрифта на основе текущего визуального состояния. Для нескольких свойств дизайн-тайма и времени выполнения выравнивание текста устанавливается в соответствии с выбором программиста в пользовательских столбцах, если они существуют. Если DefaultDrawing \[имеет значение\] false, цвет кисти устанавливается в clWindow и цвет шрифта в clWindowText, выравнивание текста задается значением defaultTextStyle сетки.
538540- **OnPrepareCanvas.** Если программист написал обработчик событий для события OnPrepareCanvas, он вызывается в этот момент. Это событие можно использовать для простой настройки, например: изменения цвета фона ячейки, свойств шрифта, таких как цвет, шрифт и стиль, расположение текста, как различные комбинации левого, центрального, верхнего, нижнего, правого выравнивания и т.д. Любые изменения, сделанные на холсте \[ранее\] в этом событии будут потеряны, потому что следующая перерисовка ячейки снова сбросит содержимое холста до состояния по умолчанию. Поэтому безопасно делать изменения только для конкретной ячейки или клеток и забыть об этом для остальных. Использование этого события иногда помогает избежать использования события сетки OnDrawCell, где пользователи будут вынуждены дублировать код отрисовки сетки.\
539541 *Todo*: образцы того, что можно сделать и что оставить для OnDrawCell? ...
540542- **OnDrawCell.** Если не указан обработчик для события OnDrawCell, следующим сетка вызывает метод DefaultDrawCell, который просто рисует фон ячейки, используя текущий цвет и стиль кисти холста. Если обработчик OnDrawCell существует, сетка сначала рисует фон ячейки, но только если установлено свойство DefaultDrawing, \[которое, в свою очередь,\] вызывает событие OnDrawCell для создания пользовательской отрисовки клетки. Обычно программисты хотят делать пользовательскую отрисовку только для определенных ячеек и стандартную отрисовку для других; в этом случае они могут ограничить пользовательскую операцию определенной ячейкой или ячейками, просмотрев аргументы ACol, ARow и AState, а для других ячеек просто вызывают метод DefaultDrawCell и позволяют сетке позаботиться об этом.
541543- **Text.** В этот момент (только для TStringGrid), если свойство DefaultDrawing \[имеет значение\] true, рисуется текстовое содержимое ячейки.
542544- **Grid lines** Последний шаг для каждой ячейки состоит в том, чтобы нарисовать линии сетки: если параметры сетки goVertLine, goHorzLine, goFixedVertLine и goFixedHorzLine указаны, ячейка сетки рисуется в этой точке. Сетки только со строками или только со столбцами могут быть получены путем изменения этих параметров. Если программист предпочел бы иметь "тематический" вид, то это делается и в этот момент (см. свойство TitleStyle).
543545- **FocusRect.** Когда все столбцы текущей строки были отрисованы, наступает время рисовать прямоугольник фокуса для текущей выбранной ячейки или для всей строки, если установлена опция goRowSelect.
544546
545547#### Различия с Delphi
546548
547549- В Lazarus метод TCustomGrid.DrawCell не является абстрактным, и его реализация по умолчанию выполняет базовое заполнение фона ячейки.
548550- В Delphi текст ячейки рисуется перед входом в событие OnDrawCell (см. [bug report \#9619](http://www.freepascal.org/mantis/view.php?id=9619)).
549551
550552### Выбор ячейки сетки
551553
552554Расположение текущей (сфокусированной) ячейки (или строки) сетки может быть изменено с помощью клавиатуры, мыши или кода. Чтобы успешно сменить фокус ячейки на другую позицию, мы должны проверить целевую позицию, чтобы увидеть, разрешено ли ей получать фокус ячейки. При использовании клавиатуры свойство AutoAdvance выполняет часть процесса, обнаруживая, что должно быть следующей сфокусированной ячейкой. При использовании кликов мыши или перемещения \[посредством\] кода фокус не будет перемещаться из текущей ячейки, если целевой ячейке не разрешено получать фокус.
553555
554556Сетка вызывает функцию SelectCell, чтобы увидеть, является ли ячейка фокусируемой: если эта функция возвращает true, то целевая ячейка, идентифицированная с аргументами aCol и aRow, является фокусируемой (текущая реализация TCustomGrid просто возвращает true). TCustomDrawGrid и, следовательно, TDrawGrid и TStringGrid переопределяют этот метод, чтобы сначала проверить, \[является ли\] ячейка шире, чем 0; обычно вы не хотите, чтобы ячейка с нулевой шириной 0 выбиралась, \[другими словами, вы хотите,\] чтобы ячейка с этими характеристиками пропускалась автоматически в процессе поиска подходящей ячейки. Другая вещь, которую переопределяет метод SelectCell, - это вызов настраиваемое пользователем событие OnSelectCell: это событие принимает координаты ячейки как аргументы и всегда возвращает значение по умолчанию true.
555557
556558Как только \[становится\] известно, что ячейка \[является\] фокусируемой, и мы уверены, что движение будет иметь место, сначала вызывается метод BeforeMoveSelection; это по очереди вызывает событие OnBeforeSelection. Аргументами этого метода являются координаты для новой сфокусированной ячейки. В этот момент \[также будет\] скрыт любой видимый редактор. Слово "перед" означает, что выбор еще не изменен, и к текущим сфокусированным координатам можно получить доступ по свойствам grid.Col и grid.Row.
557559
558560После этого меняются координаты внутренней сфокусированной ячейки, а затем вызывается метод MoveSelection; цель этого метода заключается в том, чтобы инициировать событие OnSelection, если оно установлено (это уведомление, что сфокусированная ячейка к этому времени уже изменилась и координаты ячейки теперь доступны через свойства grid.row и grid.col).
559561
560562Обратите внимание, что неэффективно использовать событие OnSelectCell для обнаружения изменений фокуса ячейки, так как это событие будет срабатывать несколько раз даже для одной и той же ячейки в процессе поиска подходящей ячейки. Для этого лучше использовать события OnBeforeSelection или OnSelection.
561563
562564#### Различия с Delphi
563565
564566- Поведение SelectCell и OnSelectCell, вероятно, различны - в действительности не могу судить о различиях. В Lazarus они используются в таких функциях, как AutoAdvance, которая, насколько я знаю, не существует в Delphi.
565567
566568### Когда встроенных свойств недостаточно: наследники сетки
567569
568570Наследники сетки обычно должны переопределять следующие методы:\
569571DrawAllRows: Отрисовывать все видимые строки\
570572DrawRow: Отрисовывать все ячейки в строке.\
571573DrawRow отрисовывает все ячейки в строке, предварительно проверив, находится ли ячейка в области отсечения, и только \[потом\] рисует ячейку, если она есть.\
572574DrawCell:\
573575DrawCellGrid:\
574576DrawCellText:\
575577DrawFocusRect:\
576578(напишите мне).\
577579
578580### Что происходит в методе `TCustomGrid.Paint`?
579581
580582В следующем списке показан внутренний порядок вызовов методов для отрисовки `TCustomGrid` (или потомков). Каждый элемент в списках представляет вызовы методов во время операции отрисовки. Это должно помочь найти правильный точку для изменения поведения, когда дело доходит до \[написания\] наследников `TCustomGrid`.
581583
582584- **`DrawEdges`**: Рисует внешнюю границу сетки.
583585- **`DrawAllRows`** (virtual): Рисует все строки в видимой части сетки. Это единственный метод, вызываемый в процессе отрисовки, который объявлен *`virtual`*.
584586 - **`DrawRow`** (virtual): Вызывается для каждой строки внутри текущего окна просмотра.
585587 - **`DrawCell`** (virtual): Сначала вызывается для каждой "нормальной" (т.е. не фиксированной) ячейки в строке.
586588 - **`PrepareCanvas`** (virtual): Устанавливает стили рисования холста в соответствии с визуальными свойствами текущей ячейки.
587589 - **`DrawFillRect`**: Рисует фон ячейки со стилями, установленными в `PrepareCanvas`.
588590 - **`DrawCellGrid`** (virtual): Рисует линии границы ячейки.
589591 - **`DrawFocusRect`** (virtual): В `TCustomGrid` этот метод ничего не делает. В наследниках сетки этот метод используется для рисования прямоугольника фокуса внутри активной ячейки.
590592 - **`DrawCell`** (virtual): (см. выше) Вызывается для каждой фиксированной ячейки в видимом окне просмотра строки.
591593- **`DrawColRowMoving`**: активен только при перемещении столбца или строки. Этот метод рисует линию, которая указывает новую позицию строки/столбца.
592594- **`DrawBorder`**: Если необходимо (`Flat=TRUE` и `BorderStyle=bsSingle`), это отрисовывает внутреннюю линию границы.
593595
594596#### Дополнительные методы для рисования `TCustomGrid`
595597
596598Эти методы объявлены и (частично) реализованы в `TCustomGrid`, но они не вызываются непосредственно отсюда. Они используются классами потомков для отрисовки содержимого ячейки.
597599
598600- **`DrawCellText`** (virtual): Записывает/отрисовывает текст, который передается в качестве параметра в ячейку. Текст отформатирован с использованием стилей, которые активны в `Canvas.TextStyle` (см. также `PrepareCanvas`).
599601- **`DrawThemedCell`** (virtual): Используется только для фиксированных ячеек и если `TitleStyle=tsNative`. \[Метод\] отрисовывает фон ячейки, используя `ThemeServices`.
600602- **`DrawColumnText`** (virtual): Используется только для ячеек заголовков столбцов.
601603 - **`DrawColumnTitleImage`**: Если сетке сопоставлен `TitleImageList` и `Columns.Title[x].Title.ImageIndex` содержит допустимое значение, \[указанное в `ImageIndex`\] изображение отрисовывается внутри ячейки.
602604 - **`DrawCellText`** : (см. выше)
603605- **`DrawTextInCell`** (virtual): Используется для "нормальных" (т.е. не фиксированных) ячеек. В `TCustomGrid` этот метод ничего не делает. В наследуемых сетках этот метод используется для отрисовки содержимого ячеек. В `TStringGrid` метод вызывает `DrawCellText` (см. выше).
604606
605607#### Методы рисования, введенные `TCustomDrawGrid`
606608
607609Как вы можете видеть, базовый класс `TCustomGrid` не выводит никакого содержимого в ячейки. Это делается в **`TCustomDrawGrid`**. **`TCustomDrawGrid`** переопределяет метод `DrawCell` следующим образом:
608610
609611- **`DrawCell`** (override):
610612 - **`PrepareCanvas`**: вызывает унаследованный метод из **`TCustomGrid`**.
611613 - **`DefaultDrawCell`** (virtual): Этот метод вызывается только в том случае, если обработчик события для `OnDrawCell` НЕ назначен.
612614 - **`DrawFillRect / DrawThemedCell`** (von `TCustomGrid`): Рисует фон ячейки. Если `TitleStyle=tsNative`, фон фиксированных ячеек рисуется с помощью `DrawThemedCell`.
613615 - **`DrawCellAutonumbering`** (virtual): Если \[свойство\] `goFixedAutonumbering` \[включено\] в \[набор\] `Options`, номера строк отображаются в первом фиксированном столбце \[с помощью метода\] `TCustomGrid.DrawCellText`.
614616 - **`DrawColumnText`** (из `TCustomGrid`): Вызывается только для ячеек заголовков столбцов (см. «Дополнительные методы отрисовки в `TCustomGrid`»).
615617 - **`DrawTextInCell`** (из `TCustomGrid`): Вызывается только для "нормальных" (т.е. не фиксированных) ячеек (см. «Дополнительные методы отрисовки в `TCustomGrid`»).
616618 - **`DrawCellGrid`** (virtual): Рисует границы ячейки.
617619
618620### Сохранение и получение содержимого сетки
619621
620622Методы **SaveToFile** и **LoadFromFile** позволяют сетке сохранять и извлекать ее макет и данные в/из файла формата XML. TStringGrid, как унаследованный от TCustomStringGrid, также имеет возможность "экспортировать" и "импортировать" свой контент в файл формата Comma Separated Values, наиболее известный как CSV-файлы. Это описано в ссылке методов [[Grids Reference Page/ru|SaveToCSVFile]] и [[Grids Reference Page/ru|LoadFromCSVFile]].
621623
622624Тип информации, которая может быть сохранена, а затем извлечена при использовании **SaveToFile** и **LoadFromFile**, определяется свойством SaveOptions (тип TSaveOptions), которое представляет собой набор параметров, описанных ниже:
623625
624-`soDesign:     Сохраняет и загружает ColCount,RowCount,FixedCols,FixedRows,`\
625-`              ColWidths, RowHeights и Options (TCustomGrid)`\
626-`soPosition:   Сохраняет и загружает Scroll position, Row, Col и Selection (TCustomGrid)`\
627-`soAttributes:  Сохраняет и загружает Colors, Text Alignment и Layout, и т.д. (TCustomDrawGrid)`\
628-`soContent:    Сохраняет и загружает текст (TCustomStringGrid)`\
629-`soAll:        набор всех параметров (soAll:=[soDesign,soPosition,soAttributes,soContent];)`
626+``` pascal
627+soDesign: Сохраняет и загружает ColCount,RowCount,FixedCols,FixedRows,
628+ ColWidths, RowHeights и Options (TCustomGrid)
629+soPosition: Сохраняет и загружает Scroll position, Row, Col и Selection (TCustomGrid)
630+soAttributes: Сохраняет и загружает Colors, Text Alignment и Layout, и т.д. (TCustomDrawGrid)
631+soContent: Сохраняет и загружает текст (TCustomStringGrid)
632+soAll: набор всех параметров (soAll:=[soDesign,soPosition,soAttributes,soContent];)
633+```
630634
631635Не все параметры применимы ко всем типам сеток, например, soContent не применяется к наследникам сеток TCustomGrid, таким как TDrawGrid, поскольку такая сетка не имеет понятия "контент". TStringGrid - это специальный вид сетки, который "знает", как обрабатывать строки, и может использовать параметр soContent, если он указан.
632636
633637Опция soAttributes не используется в стандартных сетках Lazarus, \[она предназначена\] для наследников сеток.
634638
635639При использовании **LoadFromFile** сетка также использует свойство SaveOptions, чтобы знать, какая информация должна извлекаться из файла, поэтому вполне возможно указать SaveOptions:=\[soDesign,soContent\] при сохранении и только SaveOptions:=\[soContent\] при загрузке.
636640
637641Для TStringGrid значение SaveOptions по умолчанию - \[soContent\], для других видов сеток SaveOptions - это пустой набор.
638642
639643**На заметку:** Одна из распространенных проблем при сохранении и извлечении данных сетки возникает \[тогда\], когда пользователь определяет свойство SaveOptions перед \[методом\] **SaveToFile**, но не раньше \[вызова метода\] **LoadFromFile**. При использовании **LoadFromFile** через некоторое время после **SaveToFile**, свойство SaveOptions устанавливается должным образом, но если **LoadFromFile** выполняется при следующем запуске программы, свойство SaveOptions может настроиться неправильно, по этой причине рекомендуется всегда указывать свойство SaveOptions непосредственно перед \[вызовом метода\] **LoadFromFile** или делать это глобально при запуске программы, как в следующем примере:
640644
641645------------------------------------------------------------------------
642646
643647**Пример:**
644648
6456491. Сначала перейдите в меню "File -\> New -\> Application";
6466502. Бросьте пустой TStringGrid в форму;
6476513. Бросьте TButton и TOpenDialog на форму;
6486524. Добавьте событие OnCreate для формы;
6496535. Добавьте событие OnClick для кнопки.
650654
651655``` pascal
652656unit Unit1;
653657
654658{$mode objfpc}{$H+}
655659
656660interface
657661
658662uses
659663 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,
660664 Buttons, StdCtrls, XMLCfg;
661665
662666type
663667
664668 { TForm1 }
665669 TForm1 = class(TForm)
666670 StringGrid1: TStringGrid;
667671 Button1: TButton;
668672 OpenDialog1: TOpenDialog;
669673 procedure Button1Click(Sender: TObject);
670674 procedure Form1Create(Sender: TObject);
671675 private
672676 { private declarations }
673677 public
674678 { public declarations }
675679 end;
676680
677681var
678682 Form1: TForm1;
679683
680684implementation
681685
682686{ TForm1 }
683687
684688procedure TForm1.Form1Create(Sender: TObject);
685689begin
686690 //устанавливаем SaveOptions во время создания формы
687691 stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
688692end;
689693
690694procedure TForm1.Button1Click(Sender: TObject);
691695begin
692696 //Спрашиваем, был ли запущен метод Execute [компонента] OpenDialog
693697 //когда это происходит, пользователь выбирает файл XML для загрузки
694698 //имя, которое было сохранено в объявлении FileName.
695699
696700 if opendialog1.Execute then
697701 begin
698702 //Очищаем сетку
699703 StringGrid1.Clear;
700704 //Загружаем XML
701705 StringGrid1.LoadFromFile(OpenDialog1.FileName);
702706 //Обновляем сетку
703707 StringGrid1.Refresh;
704708 end;
705709end;
706710
707711initialization
708712 {$I unit1.lrs}
709713
710714end.
711715```
712716
713717------------------------------------------------------------------------
714718
715719**Образец xml-файла:** (Скопируйте текст ниже в текстовый файл. Не забудьте поставить заголовок xml :-))
716720
717721``` xml
718722<?xml version="1.0"?>
719723<CONFIG>
720724 <grid version="3">
721725 <saveoptions create="True" position="True" content="True"/>
722726 <design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20">
723727 <options>
724728 <goFixedVertLine value="True"/>
725729 <goFixedHorzLine value="True"/>
726730 <goVertLine value="True"/>
727731 <goHorzLine value="True"/>
728732 <goRangeSelect value="True"/>
729733 <goDrawFocusSelected value="False"/>
730734 <goRowSizing value="False"/>
731735 <goColSizing value="False"/>
732736 <goRowMoving value="False"/>
733737 <goColMoving value="False"/>
734738 <goEditing value="False"/>
735739 <goTabs value="False"/>
736740 <goRowSelect value="False"/>
737741 <goAlwaysShowEditor value="False"/>
738742 <goThumbTracking value="False"/>
739743 <goColSpanning value="False"/>
740744 <goRelaxedRowSelect value="False"/>
741745 <goDblClickAutoSize value="False"/>
742746 <goSmoothScroll value="True"/>
743747 </options>
744748 </design>
745749 <position topleftcol="1" topleftrow="1" col="1" row="1">
746750 <selection left="1" top="1" right="1" bottom="1"/>
747751 </position>
748752 <content>
749753 <cells cellcount="10">
750754 <cell1 column="0" row="0" text="Title Col1"/>
751755 <cell2 column="0" row="1" text="value(1.1)"/>
752756 <cell3 column="0" row="2" text="value(2.1)"/>
753757 <cell4 column="0" row="3" text="value(3.1)"/>
754758 <cell5 column="0" row="4" text="value(4.1)"/>
755759 <cell6 column="1" row="0" text="Title Col2"/>
756760 <cell7 column="1" row="1" text="value(1.2)"/>
757761 <cell8 column="1" row="2" text="value(2.2)"/>
758762 <cell9 column="1" row="3" text="value(3.2)"/>
759763 <cell10 column="1" row="4" text="value(4.2)"/>
760764 </cells>
761765 </content>
762766 </grid>
763767</CONFIG>
764768```
765769
766770### Редактор ячеек сетки
767771
768772В сетке используются редакторы ячеек для изменения содержимого ячеек.
769773
770774Для специализированной сетки, такой как TStringGrid, редактор является обычным однострочным текстовым контролом, но иногда желательно иметь другие средства для ввода информации. Например:
771775
7727761. показать диалоговое окно открытия файла, чтобы найти местоположение файла, чтобы пользователь не вводил полный путь вручную
7737772. если текст в ячейке представляет дату, \[вызвать\] всплывающий календарь, чтобы мы могли легко выбрать конкретную дату.
774778
775779Иногда информация, которую пользователь должен ввести в ячейку, ограничена определенным списком слов; в этом случае ввод информации напрямую может привести к ошибкам и, возможно, потребуется выполнить процедуру проверки. Мы можем избежать этого, используя редактор ячейки, который представляет пользователю список, содержащий только легальные значения.
776780
777781Это также относится к общим сеткам типа TDrawGrid, где пользователю нужна какая-то структура для хранения данных, которые будут отображаться в сетке. В этой ситуации информация, введенная в редакторе ячейки, обновляет внутреннюю структуру, чтобы отображать изменения в сетке.
778782
779783#### Встроенный редактор ячейки
780784
781785Модуль grids.pas уже включает некоторые из наиболее используемых редакторов ячеек, готовых для использования в сетках. Также возможно создавать новые редакторы ячеек (настраиваемые редакторы ячеек), если встроенные редакторы не подходят для конкретной задачи.
782786
783787Встроенные редакторы ячеек - это Button, Edit и Picklist.
784788
785789#### Использование редакторов ячейки
786790
787791Пользователи могут указать, какой редактор будет использоваться для ячейки, используя один из двух методов.
788792
7897931. **Using a custom column and selecting the ButtonStyle property of the column** (*использование настраиваемого столбца и выбор свойства ButtonStyle для столбца*). В этом методе пользователь может выбрать стиль редактора, который будет показан. Доступными значениями являются: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn.
7907942. **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), в которой объясняется, как правильно реализовывать пользовательский редактор). Это событие также позволяет настраивать редактор со значениями, специфичными для ячейки, строки или столбца (например, пользовательское всплывающее меню для редактора ячейки).
791795
792796Установка свойства *ButtonStyle* работает только в том случае, если столбец создан с помощью инструкции `StringGrid1.Columns.Add`. Использование выражения типа `StringGrid1.ColCount:=X;` вызовет исключение. Установка свойства ButtonStyle может быть выполнена с помощью подобного кода:
793797
794798``` pascal
795799if ColCB< StringGrid1.Columns.Count
796800 then StringGrid1.Columns.Items[ColCB].ButtonStyle:=cbsCheckboxColumn;
797801```
798802
799803#### Описание стилей редактора
800804
801805Ниже приводится описание стилей редактора. Они являются значениями перечисляемого типа TColumnButtonStyle и поэтому имеют префикс 'cbs'. Этот тип использовался, чтобы оставаться совместимым с DBGrid Delphi.
802806
803807- **cbsAuto**
804808
805809
806810Это стиль редактора по умолчанию для сеток-наследников TCustomGrid. Фактический класс редактора, который будет использоваться для редактирования содержимого ячейки, зависит от нескольких факторов. Для TCustomGrids он использует класс TStringCellEditor, полученный из TCustomMaskEdit. Этот редактор специализирован для редактирования строк одной строки. Затем он используется по умолчанию в TStringGrid и TDrawGrid. При использовании пользовательских столбцов, если программист заполнил свойство PickList столбца, \[редактор\] ведет себя так, как если бы был установлен стиль редактора cbsPickList. Для TCustomDBGrid, который имеет поле типа boolean, он ведет себя так, как если бы был указан стиль редактора cbsCheckBoxColumn. Это рекомендуемое значение для Custom Cell Editors. TODO: сослаться на OnEditingDone.
807811
808812- **cbsEllipsis**
809813
810814
811815Этот стиль редактора является наиболее общим. При \[его\] использовании в ячейке редактирования появляется кнопка, и программисты могут использовать событие сетки OnEditButtonClick, чтобы определить, когда пользователь нажал кнопку, и выполнить любое действие, запрограммированное для такой ячейки. Например, программист может использовать этот стиль редактора, чтобы открыть диалог календаря, чтобы пользователь мог легко выбрать конкретную дату. Другие возможности могут заключаться в том, чтобы показать диалог открытия файла для поиска файлов, калькулятор, чтобы пользователь мог ввести числовой результат вычислений и т.д.
812816
813817
814818OnEditButtonClick - это просто уведомление, чтобы узнать, в какой ячейке нажата кнопка, взглянув на свойства grid.Row и grid.Col.
815819
816820
817821DBGrid имеет определенные свойства для извлечения активного столбца или поля, и, поскольку это событие происходит в активной записи, оно может обновлять информацию в активном поле.
818822
819823
820824Этот стиль редактора реализован с использованием TButtonCellEditor, прямого потомка TButton.
821825
822826- **cbsNone**
823827
824828
825829Этот стиль редактора указывает, что сетка не должна использовать какой-либо редактор для конкретной ячейки или столбца; \[это поведение аналогично тому\], как если бы такая ячейка или столбец сетки были только для чтения.
826830
827831- **cbsPickList**
828832
829833
830834Используется для представления пользователю списка значений, которые могут быть введены. Этот стиль редактора реализован с использованием TPickListCellEditor, компонента, полученного из TCustomComboBox. Список отображаемых значений заполняется одним из двух способов в зависимости от метода, используемого для выбора стиля редактора.
831835
8328361. При использовании настраиваемых столбцов программисты могут вводить список значений, используя свойство PickList столбца. \[ДЛЯ НОВИЧКОВ: TODO: точная процедура для редактирования списка\]
8338372. В OnSelectEditor программисты получают экземпляр TPickListCellEditor, используя функцию EditorByStyle(cbsPickList). [[Grids Reference Page/ru|См. пример здесь]]
834838
835839Значение в сетке TStringGrid будет автоматически отображать выбранное значение. При необходимости программист может определить момент, когда значение выбрано, путем написания обработчика для события OnPickListSelect сетки, поэтому могут быть предприняты дополнительные шаги (например, для обработки нового значения). TODO: связать с OnEditingDone.
836840
837841- **cbsCheckboxColumn**
838842
839843
840844Это может быть полезно, когда содержимое данных, связанное с столбцом, ограничено парой значений, например да-нет, true-false, вкл-выкл, 1-0 и т.д. Вместо того, чтобы принуждать пользователя вводить значения для данных такого типа в StringCellEditor или выбирать один из списка, cbsCheckboxColumn используется для изменения данных столбца с помощью представления флажка, которое пользователь может переключать, используя щелчок мыши или нажимая клавишу SPACE (если столбец, содержащий этот флажок, а также StringGrid доступны для редактирования).
841845
842846Получение или установка значения флажка в ячейке осуществляется следующим образом:
843847
844848``` pascal
845849StringGrid1.Cell[x,y]:='Z';
846850```
847851
848852
849853где Z заменяется на **0** для *Unchecked*, **1** для *Checked* и пустую строку для *Grayed*. Обратите внимание, что любое значение (строка), отличное от *0* и *1*, будет отображаться как серый сheckbox.
850854
851855
852856Если для свойства столбцов ButtonStyle установлено значение cbsAuto, и DBGrid обнаруживает, что поле, связанное с столбцом, является логическим полем, то сетка автоматически использует этот стиль редактора. Этот автоматический выбор может быть отключен или активирован с использованием свойства OptionGxtra DBGrid; установка элемента dgeCheckboxColumn в false отключает эту функцию.
853857
854858
855859Значения, используемые для распознавания *checked* или *unchecked* состояний, задаются в свойствах столбца ValueChecked и ValueUnchecked \[соответственно\].
856860
857861
858862В любой момент значение поля может находиться в одном-трех состояниях: Unchecked, Checked или Grayed. Внутренне эти состояния идентифицируются следующими значениями типа TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked и gcbpGrayed.
859863
860864
861865Этот стиль редактора не использует реальные компоненты TCheckbox для обработки взаимодействия с пользователем: визуальное представление задается тремя встроенными растровыми изображениями, которые соответствуют возможным состояниям флажка. Использованные растровые изображения можно настроить, написав обработчик для события OnUserCheckboxBitmap \[компонента\] DBGrid; обработчик этого события получает состояние флажка в параметре CheckedState типа TDBGridCheckboxState и параметр растрового изображения, который программист может использовать для указания пользовательских растровых изображений.
862866
863867- **cbsButtonColumn**
864868
865869
866870Этот стиль редактора используется для отображения кнопки в каждой ячейке в столбце. Как и в случае cbsCheckboxColumn, этот редактор не использует реальные кнопки, внешний вид определяется текущей темой widgetset.
867871
868872
869873Пользователь знает, какая конкретная кнопка была нажата благодаря обработчику OnEditButtonClick сетки и проверке столбца и строки. Обратите внимание, что в этом конкретном случае столбец и строка сетки идентифицируют не текущую выбранную ячейку, а ячейку нажатой кнопки. Как только событие OnEditButtonClick будет обработано, и если пользователь не изменил столбец или строку в этом обработчике, сетка автоматически сбрасывает столбец и строку, чтобы отобразить выбранную ячейку. Пока обработчик OnEditButtonClick текущего выделения сетки доступен в grid.Selection, который является свойством TRect, левое и правое \[значения\] представляют \[собой\] индексы столбцов, а верхний и нижний - индексы строк.
870874
871875
872876Заголовок кнопки - соответствующая строка ячейки.
873877
874878### Редактирование сеток
875879
876880Эффект редактирования отличается в зависимости от того, какая сетка используется, например, TStringGrid сохраняет отредактированный текст внутри, а TDBGrid влияет на записи в наборе данных. TDrawGrid "не знает", что делать с отредактированным текстом: если программист не контролирует его, \[отредактированный текст\] просто отбрасывается.\
877881Для TDrawGrid (хотя это должно работать для всех классов сетки), чтобы получить доступ к отредактированному тексту, программист может использовать событие **OnSetEditText**, которое запускается каждый раз, когда пользователь что-то изменяет в редакторе, параметры *aCol* и *Row* этого события определяют редактируемую ячейку, параметр *value* содержит \[редактируемый\] текст - это рекомендуемый метод. Другой способ доступа к отредактированному тексту - прием его непосредственно из редактора после завершения процесса редактирования с использованием события **OnEditingDone**. Это можно сделать, обратившись к внутреннему редактору, используемому для редактирования, в данном случае, по умолчанию "String Cell Editor" \[("Редактору строк ячеек")\]. Для этого существует два метода: первый - с использованием события **OnSelectEditor**, где параметр *Editor* - это экземпляр, который будет использоваться для редактирования ячейки; вы должны сохранить этот экземпляр в переменной, например TheEditor (типа *TStringCellEditor*), для последующего использования в **OnEditingDone**. Второй вариант - использование метода **EditorByStyle** сетки, этот метод принимает "стиль редактора" в качестве параметра и возвращает экземпляр редактора, соответствующего этому стилю. В нашем случае, зная, что стиль cbsAuto возвращает редактор ячейки по умолчанию, в обработчике события **OnEditingDone** можно использовать непосредственно TheEditor:=Grid.EditorByStyle(cbsAuto). Затем вы можете получить текст с помощью *TheEditor.Text*. Если вы \[будете\] использовать этот метод, обратите внимание на то, что "великая сила несет большую ответственность".
878882
879883#### Параметры и свойства, которые влияют на редактирование
880884
881885Поведение редактирования контролируется рядом свойств и параметров, фактически любая настройка или параметры, которые влияют на редактирование, требуют редактирования сетки или такая настройка может быть проигнорирована.\
882886При запуске редактируемое состояние сетки различно \[в разных видах сетках\], поскольку редактирование TDrawGrid и TStringGrid отключено, для TDbGrid включено по умолчанию.\
883887Свойство **Options** имеет несколько элементов, которые касаются редактирования, они описаны ниже:\
884888***goEditing, dgEditing (в DbGrid)**. Эта опция изменяет редактируемое состояние сетки, ее можно изменить во время выполнения, поскольку она проверяет, когда начнется редактирование.
885889
886890- **goAlwaysShowEditor**. Обычно редактор скрыт и становится видимым только тогда, когда это необходимо. С помощью этой опции редактор будет отображаться все время, если сетка не редактируется, этот параметр игнорируется, и редактор всегда скрыт.
887891
888892### Хинты (подсказки) ячейки
889893
890894В дополнение к стандартным хинтам большинства элементов управления потомки TCustomGrid могут отображать специальную подсказку для ячейки, над которой курсирует мышь. Эта функция управляется параметрами сетки **goCellHints** и **goTruncCellHints** (или **dgCellHints** и **gdTruncCellHints** для DBGrid соответственно):
891895
892896- **goCellHints** оценивает событие **OnGetCellHint** для извлечения отображаемого текста для ячейки, указанной переданными параметрами. В случае TDrawGrid и TStringGrid задаются индексы столбцов и строк, тогда как в случае TDBGrid текст ячейки может быть извлечен из поля, назначенного указанному объекту столбца.
893897- **goTruncCellHints** отображает подсказку только в том случае, если текст не помещается \[полностью\] в ячейку и \[поэтому\] усечен; текст подсказки - это не обрезанный ячейкой текст. В Lazarus 1.9+ этот текст передается событию **OnGetCellHint** для дальнейшего уточнения (например, отображает весь текст memo в подсказке). В случае обрезания ячейкой текстов рекомендуется также активировать опцию **goCellEllipsis** (или **dgCellEllipsis** для DBGrid), которая добавляет «...» к обрезанному ячейкой тексту.
894898
895899Если сетка имеет значение в своем стандартном свойстве **Hint**, тогда свойство **HintPriority** можно использовать для управления тем, как все эти тексты подсказок могут быть объединены в одно всплывающее окно:
896900
897901- **chpAll** помещает стандартную подсказку в первую строку, подсказку, определяемую событием OnGetCellHint во вторую строку, и подсказку для усеченных ячеек в третью строку
898902- **chpAllNoDefault** отображает подсказки только ячейки, заданные событием OnGetCellHint, и усеченными ячейками.
899903- **chpTruncOnly** отображает подсказки только для усеченных ячеек; это настройки по умолчанию.
900904
901905В дополнение, стандартное свойство **ShowHint** сетки должно быть установлено в true, чтобы отображать подсказки на ячейки.
902906
903907## "Как сделать..." и Примеры
904908
905909### Фокусировка ячейки
906910
907911Фокусировка ячейки в TStringGrid очень проста. Обратите внимание, что подсчет начинается с нуля, а не 1. Таким образом, чтобы перевести фокус на строку 10, столбец 9, выполните:
908912
909913``` pascal
910914StringGrid1.row := 9;
911915StringGrid1.col := 8;
912916```
913917
914918### Пример: Как настроить собственный редактор ячеек.
915919
916920см. lazarus/examples/gridexamples/gridcelleditor/gridcelleditor.lpi (начиная с laz 1.2)
917921
918922### Пример: Как установить memo-редактор для dbgrids
919923
920924Разумеется, вы можете использовать другой контрол вместо TMemo. Откорректируйте \[на свой\] вкус. Адаптирован из [1](http://forum.lazarus.freepascal.org/index.php?topic=3640.0) (упрощенное использование свойства SelectedFieldRect, см. [2](http://forum.lazarus.freepascal.org/index.php/topic,8713.msg43288.html#msg43288))
921925
922926- Поместите элемент управления memo (или любой другой контрол, который вы хотите) на свою форму, установите любые свойства, которые вы хотите, и установите значение visible:= false. Этот \[контрол\] будет использоваться при редактировании ячейки сетки. В этом примере мы будем использовать GridCellMemo.
923927- В событии OnSelectEditor поместите следующий код - адаптируйте, если вы не хотите редактировать логический столбец 3, физический столбец 4:
924928
925929``` pascal
926930if (Column.DesignIndex = 3) then
927931 begin
928932 GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
929933 GridCellMemo.Text:=Column.Field.AsString;
930934 Editor := GridCellMemo;
931935 end;
932936```
933937
934938- Предположим, что ваш источник данных называется Datasource1, а DBGrid называется ResultsGrid. Тогда в событии OnEditingDone для GridCellMemo введите следующее:
935939
936940(В исходном сообщении форума используется событие OnChange, которое будет срабатывать при каждом изменении содержимого. Использование OnEditingDone запускается только после того, как пользователь закончил свои правки.)
937941
938942``` pascal
939943 if not(Datasource1.State in [dsEdit, dsInsert]) then Datasource1.Edit;
940944
941945 Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;
942946```
943947
944948**Осторожно** Использование обычного элемента управления в качестве настраиваемого редактора имеет множество недостатков: при изменении размеров столбцов или скроллинге сетки он не будет изменять размер или положение, вам придется позаботиться о том, чтобы изменить данные в сетке (как объяснялось выше), он не работает должным образом с DbGrids и препятствует нормальной навигации по сетке с помощью клавиатуры. Сетка взаимодействует с редактором, используя некоторые специальные сообщения сетки, поэтому лучше \[выделить этот контрол в\] подкласс для упорядочивания управления этими сообщениями, как описано в пятой главе [этого документа](http://www.freepascal.org/~michael/articles/grids/grids.pdf).
945949
946950**Осторожно** Если у вас более одной сетки в одной и той же форме, используйте разные настраиваемые редакторы для каждой из них: если вы \[будете\] использовать один и тот же элемент управления, начнут происходить плохие вещи с того момента, когда обе сетки попытаются использовать его одновременно.
947951
948952### Пример: как добавить кнопку редактора
949953
950954``` pascal
951955// Условно показываем кнопку редактора в столбцах индекс 1 или 2, если
952956// ячейка в столбце index 1 пуста
953957procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer;
954958 var Editor: TWinControl);
955959begin
956960 if (aCol = 1) or (aCol = 2) then
957961 if StringGrid1.Cells[1,aRow] = '' then
958962 begin
959963 Editor := StringGrid1.EditorByStyle(cbsEllipsis);
960964 end;
961965 end;
962966
963967// Запуск действия ...
964968procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
965969begin
966970 if StringGrid1.Col = 1 then Showmessage('щелкнут редактор столбца 1');
967971 if StringGrid1.Col = 2 then Showmessage('щелкнут редактор столбца 2');
968972end;
969973```
970974
971975### Пример: Запрет отображения текстовых полей как "(Memo)" для DBGrids
972976
973977Когда вы используете SQL-выражения, такие как `"Select * from A"`, вы можете увидеть "(Memo)" вместо своего содержимого в ячейке сетки. Есть много способов исправить это, но, может быть, самым простым является изменение вашего выражения `"Select * from A"` на `"Select Cast(Column as TEXT) as Column from A"`. Это обычная проблема при использовании компонента DBGrid.
974978
975979``` pascal
976980 var
977981 sql : UTF8String;
978982 Query : TSQLQuery;
979983.....
980984 sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
981985 Query.SQL.Text:=sql;
982986```
983987
984988### Пример: Работа с Picklist, Как сделать его только для чтения и Как заполнить его в runtime.
985989
986990Используя событие сетки OnSelectEditor, вы можете настроить, как поведет себя редактор PickList (см. стиль кнопки cbsPickList). В следующем примере редактор списка выбора из столбца 1 изменяется на нечетных строках таким образом, что пользователь может вводить значения, набирая \[их с клавиатуры\], а на четных строках \[вводимые\] значения ограничиваются теми, которые содержатся в его списке. Кроме того, в этом примере показано, как заполнить список разными значениями в зависимости от обрабатываемой строки.
987991
988992``` pascal
989993procedure TForm1.gridSelectEditor(Sender: TObject; aCol, aRow: Integer;
990994 var Editor: TWinControl);
991995begin
992996 if aCol=1 then begin
993997 if (Editor is TCustomComboBox) then
994998 with Editor as TCustomComboBox do begin
995999 if (aRow mod 2=0) then
9961000 Style := csDropDown
9971001 else
9981002 Style := csDropDownList;
9991003 case aRow of
10001004 1:
10011005 Items.CommaText := 'ONE,TWO,THREE,FOUR';
10021006 2:
10031007 Items.CommaText := 'A,B,C,D,E';
10041008 3:
10051009 Items.CommaText := 'MX,ES,NL,UK';
10061010 4:
10071011 Items.CommaText := 'RED,GREEN,BLUE,YELLOW';
10081012 end;
10091013 end;
10101014 end;
10111015end;
10121016```
10131017
10141018### Выравнивание текста в StringGrids
10151019
10161020Этот код показывает, как использовать разное выравнивание текста в столбцах 2 и 3.
10171021
10181022``` pascal
10191023procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
10201024 aState: TGridDrawState);
10211025var
10221026 MyTextStyle: TTextStyle;
10231027begin
10241028 if (aCol=2) or (aCol=3) then
10251029 begin
10261030 MyTextStyle := StringGrid1.Canvas.TextStyle;
10271031 if aCol=2 then
10281032 MyTextStyle.Alignment := taRightJustify
10291033 else
10301034 if aCol=3 then
10311035 MyTextStyle.Alignment := taCenter;
10321036 StringGrid1.Canvas.TextStyle := MyTextStyle;
10331037 end;
10341038end;
10351039```
10361040
10371041### Многострочность в Grids, DBGrid
10381042
10391043В этом примере показано, как сделать многострочный текст в ячейке \[3,2\]. Он работает так же для DBGrid, где \[событие\] OnPrepareCanvas имеет параметры для работы с столбцами, а оттуда и с TFields.
10401044
10411045``` pascal
10421046procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
10431047 aState: TGridDrawState);
10441048var
10451049 MyTextStyle: TTextStyle;
10461050begin
10471051 if (aRow=2) or (aCol=3) then
10481052 begin
10491053 MyTextStyle := StringGrid1.Canvas.TextStyle;
10501054 MyTextStyle.SingleLine := false;
10511055 StringGrid1.Canvas.TextStyle := MyTextStyle;
10521056 end;
10531057end;
10541058```
10551059
10561060### Проверка введенных значений
10571061
10581062Lazarus версии 0.9.29 для StringGrid ввел событие OnValidateEntry типа TValidateEntryEvent, которое имеет следующее объявление:
10591063
10601064``` pascal
10611065TValidateEntryEvent =
10621066 procedure(sender: TObject; aCol, aRow: Integer;
10631067 const OldValue: string; var NewValue: string) of object;
10641068```
10651069
10661070где
10671071
1068-`aCol,aRow являются координатами проверяемой ячейки.`\
1069-`OldValue это значение, которое было в ячейках [aCol, aRow] до начала редактирования.`\
1070-`NewValue это значение, которое будет окончательно вставлено в ячейки [aCol, aRow].`
1072+```text
1073+aCol,aRow являются координатами проверяемой ячейки.
1074+OldValue это значение, которое было в ячейках [aCol, aRow] до начала редактирования.
1075+NewValue это значение, которое будет окончательно вставлено в ячейки [aCol, aRow].
1076+```
10711077
10721078Из-за того, что StringGrid работает, устанавливая значение ячейки во время редактирования пользователем (см. событие OnSetEditText и метод SetEditText сетки), то когда событие OnValidateEntry запускается, ячейка уже содержит введенное значение (корректное или нет); используя аргументы события OldValue и NewValue, значение ячейки может быть проверено и изменено, если необходимо.
10731079
10741080Обычно проверка выполняется, когда пользователь перемещается \[с редактируемой ячейки\] в другую. Если проверка закончилась неудачей, желательно сохранить редактор ячейки видимым/сфокусированным, чтобы введенное значение могло быть исправлено пользователем. Чтобы сетка "узнала", что проверка не прошла, необходимо создать исключение. Сетка будет обрабатывать исключение из Application.HandleException, и любое перемещение отменится.
10751081
10761082Например, предположим, что ячейка \[1,1\] должна содержать только значения 'A' или 'B', \[тогда\] валидация может быть выполнена с помощью:
10771083
10781084``` pascal
10791085procedure TForm1.GridValidateEntry(sender: TObject; aCol,
10801086 aRow: Integer; const OldValue: string; var NewValue: String);
10811087begin
10821088 if (aCol=1) and (aRow=1) then begin
10831089 if grid.Cells[aCol,aRow]<>'A') and grid.Cells[aCol,aRow]<>'B') then begin
10841090 // устанавливаем новое допустимое значение, чтобы пользователь мог просто нажать RETURN для продолжения, например
10851091 NewValue := 'A';
10861092 // другой вариант - вернуться предыдущему значению ячейки (которое считается допустимым)
10871093 // NewValue := OldValue;
10881094 // Используйте EAbort, а не другой тип исключения, чтобы избежать [возникновения] ложных
10891095 // диалоговых окон ошибок и [диалоговых окон] обратной отладки
10901096 raise EAbort.Create('Разрешены только A или B');
10911097 end else begin
10921098 // если исключение не создано, предполагается, что значение действительно, но при необходимости
10931099 // окончательное значение все равно можно изменить, заполнив NewValue другим значением
10941100
10951101 // компьютер знает лучше :)
10961102 if grid.Cells[1,1]='A' then
10971103 NewValue := 'B'
10981104 else
10991105 NewValue := 'A';
11001106 end;
11011107 end;
11021108end;
11031109```
11041110
11051111### Сортировка столбцов или строк
11061112
11071113Свойство ColumnClickSorts позволяет сортировать сетку автоматически, когда пользователь щелкает по заголовку столбца. Многократный щелчок по тому же столбцу переключает порядок сортировки. По умолчанию отображаются значки сортировки, показывающие, какой столбец был нажат.
11081114
11091115В коде вы можете использовать метод SortColRow().
11101116
11111117Первый параметр IsColumn - это логическое значение, которое указывает:
11121118
11131119- True если будет отсортирован столбец
11141120- False если будет отсортирована строка
11151121
11161122Второй параметр, index, является целым значением:
11171123
11181124- Index указывает колонку или строку для сортировки.
11191125
11201126Последние два параметра являются необязательными, \[они\] определяют поддиапазон строк или столбцов для сортировки.
11211127
11221128- FromIndex (От столбца или строки)
11231129- ToIndex (До столбца или строки)
11241130
11251131Если последние два параметра не указаны, сортируется весь столбец или строка.
11261132
11271133Сортировка использует алгоритм QuickSort, его можно изменить, если сетка-наследник переопределяет метод sort() и вызывает doCompareCells для сравнения с ячейкой.
11281134
11291135По умолчанию \[метод SortColRow()\] сортирует содержимое ячейки как строки в порядке возрастания или убывания, которое можно выбрать с помощью свойства SortOrder. По умолчанию он использует порядок \[сортировки\] по возрастанию.
11301136
11311137``` pascal
11321138// Сортировка столбца 3 в порядке возрастания
11331139grid.SortColRow(true, 3);
11341140
11351141// Сортировка столбца 3 в порядке убывания, пропустить фиксированные строки сверху
11361142grid.SortOrder := soDescending; // или soAscending
11371143grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);
11381144```
11391145
11401146Для пользовательской сортировки чисел, дат, состояний и т.д.: у StringGrid есть событие OnCompareCells, с которым пользователи могут обращаться, например, следующим образом:
11411147
11421148``` pascal
11431149procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol, BRow: Integer; var Result: integer);
11441150begin
11451151 // Результат будет либо <0, = 0, либо >0 для нормального порядка.
11461152 result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
11471153 // Для обратного порядка просто [присвойте] негативный результат (напр., на основе SortOrder сетки).
11481154 if StringGrid1.SortOrder = soDescending then
11491155 result := -result;
11501156end;
11511157```
11521158
11531159Вы также можете использовать OnCompareCells, когда автоматическая сортировка столбцов включена через свойство ColumnClickSorts.
11541160
11551161### Переопределить автоматическую сортировку (ColumnClickSorts установлен в False). Исключить сортировку некоторых столбцов. Показать стрелки сортировки в заголовке столбца
11561162
11571163(ColumnClickSorts, установленный в False, НЕ будет показывать стрелки сортировки в заголовке столбца, поэтому мы должны установить стрелки.)
11581164
11591165``` pascal
11601166procedure TForm1.StringGrid1HeaderClick(Sender: TObject; IsColumn: Boolean;
11611167 Index: Integer);
11621168begin
11631169 if isColumn then with StringGrid1 do
11641170 begin
11651171 col:=Index; // Задаем столбец для сортировки
11661172 if Tag = 0 then Tag :=-1 // Предположим, что данные в столбце возрастают
11671173 else Tag := Tag * -1; // Используем StringGrid1.Tag для хранения порядка сортировки
11681174 // Первый щелчок заголовка сортирует по убыванию
11691175
11701176 // Активируем CompareCells (активируем сортировку)
11711177 case Index of
11721178 0: col:=-1; // Не сортируем столбец 0 и не показываем стрелку сортировки
11731179 1: SortColRow(True, Index); // Сортировка будет происходить в CompareCells, как только будет выполнен оператор
11741180 2: SortColRow(True, Index);
11751181 3: SortColRow(True, Index);
11761182 4: SortColRow(True, Index);
11771183 5: col:=-1; // Не сортируем столбец 5 и не показываем стрелку сортировки
11781184 end;
11791185 end;
11801186end;
11811187
11821188// Compare возвращает одно из следующих значений:
11831189// if A<B return: -1 ( Восходящий )
11841190// if A=B return: 0
11851191// if A>B return: 1 ( Нисходящий )
11861192
11871193// Как сортировать: Text, Integer, Float, DateTime:
11881194
11891195procedure TForm1.StringGrid1CompareCells(Sender: TObject; ACol, ARow, BCol,
11901196 BRow: Integer; var Result: integer);
11911197
11921198 function CompareTextAsText: Integer; // Text
11931199 begin
11941200 CompareText(Cells[ACol,ARow], Cells[BCol,BRow]);
11951201 end;
11961202
11971203 function CompareTextAsInteger: Integer;
11981204 var i1,i2:Integer;
11991205 begin
12001206 if TryStrToInt(StringGrid1.Cells[ACol,ARow],i1) and // Integer
12011207 TryStrToInt(StringGrid1.Cells[BCol,BRow],i2) then
12021208 Result := CompareValue(i1, i2)
12031209 else Exit(0);
12041210 end;
12051211
12061212 function CompareTextAsFloat: Integer; // Float
12071213 var f1,f2:Extended;
12081214 begin
12091215 if TryStrToFloat(StringGrid1.Cells[ACol,ARow],f1) and
12101216 TryStrToFloat(StringGrid1.Cells[BCol,BRow],f2) then
12111217 Result := CompareValue(f1, f2)
12121218 else Exit(0);
12131219 end;
12141220
12151221 function CompareTextAsDateTime: Integer; // DateTime
12161222 var
12171223 D1, D2 : TDateTime;
12181224 begin
12191225 if Trim(StringGrid1.Cells[ACol,ARow]) <> Trim(StringGrid1.Cells[BCol,BRow]) then
12201226 begin
12211227 if trim(StringGrid1.Cells[ACol,ARow]) = '' then Exit(1);
12221228 if Trim(StringGrid1.Cells[BCol,BRow]) = '' then Exit(-1);
12231229 end
12241230 else Exit(0);
12251231
12261232 if TryStrToDate(StringGrid1.Cells[ACol,ARow], D1, 'yy/mm/dd') and
12271233 TryStrToDate(StringGrid1.Cells[BCol,BRow], D2, 'yy/mm/dd') then
12281234 Result := CompareDateTime(D1, D2)
12291235 else Exit(0);
12301236 end;
12311237
12321238begin // Не делайте здесь ничего другого, сортировка может быть выполнена некорректно
12331239 With StringGrid1 do
12341240 begin
12351241 case ACol of
12361242 0 : ; // Не сортируйте столбец 0
12371243 1 : Result := CompareTextAsText; // Text
12381244 2 : Result := CompareTextAsInteger; // Integer
12391245 3 : Result := CompareTextAsFloat; // Float
12401246 4 : Result := CompareTextAsDateTime; // DateTime
12411247 end; {Case}
12421248 end;
12431249 if StringGrid1.Tag = -1 then Result := -Result; // Переключаем порядок сортировки
12441250end;
12451251
12461252// DrawCell сделает трюк, предыдущие изображения стрелки будут удалены:
12471253procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
12481254 aRect: TRect; aState: TGridDrawState);
12491255
12501256 procedure DrawArrow(ImgIndex:Integer); // Нужны стрелки вверх/вниз в ImageList
12511257 begin
12521258 with StringGrid1 do
12531259 TitleImageList.Draw(Canvas, // TitleImageList из StringGrid
12541260 ARect.Right - TitleImageList.Width,
12551261 (ARect.Bottom - ARect.Top - TitleImageList.Height) div 2 + 2, // Положение стрелки, может быть отрегулировано
12561262 ImgIndex );
12571263 end;
12581264begin
12591265 if Col > 0 then // В этом демо нет стрелок в col 0
12601266 if (ARow = 0) and // Предположим, строка 0 в качестве заголовка строк
12611267 Assigned(TitleImageList) then // Нужны стрелки вверх/вниз в ImageList
12621268 case Columns.Items[aCol].Tag of // Использовать тег столбца как индекс Восходящей/Нисходящей стрелки
12631269 -1: DrawArrow(1); // Заменяем Index указателем стрелки вниз
12641270 0: DrawArrow(-1); // Удаляем предыдущее изображение
12651271 1: DrawArrow(2); // Заменяем Index указателем стрелки вверх
12661272 end;
12671273end;
12681274```
12691275
12701276### Сортировка столбцов или строк в DBGrid со стрелками сортировки в заголовке столбца
12711277
12721278Вот пример, который будет сортировать **DBgrid**, используя событие **OnTitleClick** и **TSQLQuery** и индексы. Это также должно работать для любого совместимого набора данных, такого как **TbufDataset**. Функция использует свойство **column.tag** для хранения состояния сортировки для каждого столбца, на который вы нажимаете, поэтому, когда вы вернетесь к тому \[столбцу\], который вы уже отсортировали, \[заголовок столбца\] отобразит правильную стрелку сортировки для отображения.
12731279
12741280**Предпосылки:**
12751281
12761282- Для хранения \[изображений\] стрелок вверх/вниз вам понадобится imagelist. Присвойте свой imagelist свойству **TitleImageList** dbgrid'а.
12771283- Убедитесь, что **[[doc:fcl/sqldb/tsqlquery.maxindexescount.html|TSQLQuery.MaxIndexesCount]]** достаточно велик, чтобы хранить новые индексы, которые вы будете создавать. Для этого примера я установил значение 100.
12781284- Вам также понадобится переменная-поле \[(в разделе интерфейса private)\] для хранения последнего столбца, который используется для сортировки, для этого примера я назвал его **FLastColumn**.
12791285
12801286В этом примере используемый **TSQLQuery** называется OpenQuery.
12811287
12821288> **Note:** По состоянию на 21 марта 2013 года **TSQLQuery** не имеет возможности очищать индексы, но в качестве обходного пути вы можете установить \[значение его свойства\] **unidirectional** в true, а затем в false: это очистит индексы.
12831289
12841290Чтобы повторно использовать компонент **TSQLQuery** с другим оператором SQL, индексы должны быть очищены после сортировки, \[иначе\] при открытии **TSQLQuery** с помощью другого оператора SQL будет вызвано исключение.
12851291
12861292``` pascal
12871293FLastColumn: TColumn; //сохраняем последний столбец сетки, который мы отсортировали
12881294
12891295procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
12901296const
12911297 ImageArrowUp=0; //должен соответствовать изображению в imagelist
12921298 ImageArrowDown=1; //должен соответствовать изображению в imagelist
12931299var
12941300 ASC_IndexName, DESC_IndexName:string;
12951301 procedure UpdateIndexes;
12961302 begin
12971303 // Обеспечиваем обновление индексов
12981304 OpenQuery.IndexDefs.Updated:=false; {<<<--Эта строка - решающая. IndexDefs.Update не будет
12991305 обновляться, если уже true, что произойдет в первом отсортированном столбце.}
13001306 Openquery.IndexDefs.Update;
13011307 end;
13021308begin
13031309 ASC_IndexName:='ASC_'+Column.FieldName;
13041310 DESC_IndexName:='DESC_'+Column.FieldName;
13051311 // индексы не могут сортировать двоичные типы, такие как ftMemo, ftBLOB
13061312 if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
13071313 exit;
13081314 // проверяем, существует ли восходящий индекс для этого столбца
13091315 // если нет, создадим [его]
13101316 if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
13111317 begin
13121318 OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
13131319 UpdateIndexes; //обеспечиваем обновление индексов
13141320 end;
13151321 // проверяем, существует ли нисходящий индекс для этого столбца
13161322 // если нет, создадим [его]
13171323 if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
13181324 begin
13191325 OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
13201326 UpdateIndexes; //обеспечиваем обновление индексов
13211327 end;
13221328
13231329 // Используем тег столбца для переключения ASC/DESC
13241330 column.tag := not column.tag;
13251331 if boolean(column.tag) then
13261332 begin
13271333 Column.Title.ImageIndex:=ImageArrowUp;
13281334 Openquery.IndexName:=ASC_IndexName;
13291335 end
13301336 else
13311337 begin
13321338 Column.Title.ImageIndex:=ImageArrowDown;
13331339 OpenQuery.IndexName:=DESC_IndexName;
13341340 end;
13351341 // Удаляем стрелку сортировки из предыдущего столбца, который мы отсортировали
13361342 if (FLastColumn <> nil) and (FlastColumn <> Column) then
13371343 FLastColumn.Title.ImageIndex:=-1;
13381344 FLastColumn:=column;
13391345end;
13401346```
13411347
13421348### Выбор записей в DBGrid с использованием checkbox'ов
13431349
13441350Цель состоит в том, чтобы иметь возможность выбирать произвольные записи в dbgrid с помощью checkbox'ов, сетка имеет возможность показывать checkbox'ы автоматически, когда обнаруживает наличие логических полей, а для других типов полей пользователь может вручную \[в свойстве\] ButtonStyle столбца выбрать \[значение\] cbsCheckboxColumn. Для таких столбцов пользователь просто помечает checkbox, и содержимое поля изменяется соответствующим образом.
13451351
13461352Но что произойдет, если в нашем наборе данных нет такого доступного поля? или мы не хотим, чтобы сетка входила в состояние редактирования при изменении состояния checkbox'а?. Добавление столбца без полей с помощью ButtonStyle=cbsCheckboxColumn покажет все checkbox'ы с неопределенным значением и отключенными, потому что нет поля, связанного с этим столбцом, и поэтому ничего не нужно изменять. Поскольку мы хотим самостоятельно обрабатывать состояние checkbox'а, нам нужно где-то сохранить состояние для каждой записи. Для этого мы можем использовать класс TBookmarklist (определенный в модуле dbgrids.pas), где свойство CurrentRowSelected может определить, выбрана ли текущая запись или нет. Используя события dbgrid'а OnCellClick и OnUserCheckboxState, мы можем отслеживать состояние checkbox'а.
13471353
13481354Обратите внимание, что этой методике нужен Lazarus r31148 или более поздней версии, которая реализует событие OnUserCheckboxState.
13491355
13501356``` pascal
13511357...
13521358uses ..., dbgrids, stdctrls, ...
13531359
13541360type
13551361
13561362 { TForm1 }
13571363
13581364 TForm1 = class(TForm)
13591365 ...
13601366 procedure DBGrid1CellClick(Column: TColumn);
13611367 procedure DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
13621368 procedure FormCreate(Sender: TObject);
13631369 procedure FormDestroy(Sender: TObject);
13641370 ...
13651371 private
13661372 RecList: TBookmarklist;
13671373 ...
13681374 end;
13691375
13701376procedure TForm1.DBGrid1CellClick(Column: TColumn);
13711377begin
13721378 if Column.Index=1 then
13731379 RecList.CurrentRowSelected := not RecList.CurrentRowSelected;
13741380end;
13751381
13761382procedure TForm1.FormCreate(Sender: TObject);
13771383begin
13781384 RecList := TBookmarkList.Create(DbGrid1);
13791385end;
13801386
13811387procedure TForm1.FormDestroy(Sender: TObject);
13821388begin
13831389 RecList.Free;
13841390end;
13851391
13861392procedure TForm1.DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
13871393begin
13881394 if RecList.CurrentRowSelected then
13891395 AState := cbChecked
13901396 else
13911397 AState := cbUnchecked;
13921398end;
13931399```
13941400
13951401### Подсветка выделенного столбца и строки ячейки
13961402
13971403В версии Lazarus 40276 добавлена опция (**gdRowHighlight**), она работает аналогично **goRowSelect**, но использует более светлый тон для выделения, а прямоугольник фокуса - только выбранная ячейка, а не в целая строка. Это отлично работает для строк, но как насчет столбцов?
13981404
13991405В этом разделе показан способ выделения столбцов, строк или и того, и другого (пример, который выделяет только столбцы и заголовки строк, можно найти в lazarus/examples/gridexamples/spreadsheet, этот способ действительно является расширением этого примера). Он использует два события сетки: **OnBeforeSelection** и **OnPrepareCanvas**, рисунок не нужен.
14001406
14011407Событие **OnBeforeSelection** запускается, когда начинается смена выделения, в этом случае мы можем узнать, какая ячейка выбрана в данный момент и какая ячейка будет выбрана следующей. Мы используем эту информацию для перерисовки всей строки или столбца как старой, так и новой ячеек. Когда начнется следующий цикл отрисовки, сетке будет предложено перерисовать ячейки, принадлежащие недействительным областям. Одним из первых шагов для перерисовки является вызов события **OnPrepareCanvas** (если он существует) для настройки свойств холста по умолчанию. Мы используем это событие для настройки выделенной строки или столбца:
14021408
14031409``` pascal
14041410procedure TForm1.gridBeforeSelection(Sender: TObject; aCol, aRow: Integer);
14051411begin
14061412 // мы можем здесь принять решение, если хотим выделить столбцы, строки или и те, и другие
14071413 // в этом примере мы выделяем и те, и другие
14081414 if Grid.Col<>aCol then
14091415 begin
14101416 // обнаружено изменение текущего столбца
14111417 grid.InvalidateCol(aCol); // перерисовываем новый выбранный столбец ячейки
14121418 grid.InvalidateCol(grid.Col); // перерисовываем текущий (это будет 'старый') выбранный столбец
14131419 end;
14141420 if Grid.Row<>aRow then
14151421 begin
14161422 grid.InvalidateRow(aRow); // перерисовываем новую выбранную строку ячейки
14171423 grid.InvalidateRow(grid.Row); // перерисовываем текущую (это будет 'старая') выбранную строку
14181424 end;
14191425end;
14201426
14211427procedure TForm1.gridPrepareCanvas(sender: TObject; aCol, aRow: Integer;
14221428 aState: TGridDrawState);
14231429begin
14241430 if gdFixed in aState then
14251431 begin
14261432 if (aCol=grid.Col) or (aRow=grid.Row) then
14271433 grid.Canvas.Brush.Color := clInactiveCaption; // это подсветило бы также заголовки столбцов или строк
14281434 end else
14291435 if gdFocused in aState then begin
14301436 // мы оставляем в покое текущую выделенную/сфокусированную ячейку
14311437 end else
14321438 if (aCol=Grid.Col) or (aRow=grid.Row) then
14331439 grid.Canvas.Brush.Color := clSkyBlue; // подсветка строк и столбцов цветом clSkyBlue
14341440end;
14351441```
14361442
14371443## DBGrids "Как сделать..." и Примеры
14381444
14391445#### Пример: как установить редактор memo для dbgrids
14401446
14411447Конечно, вы можете использовать другой элемент управления вместо TMemo. Отрегулируйте по вкусу. Адаптировано из [3](http://forum.lazarus.freepascal.org/index.php?topic=3640.0) (упрощено использование свойства SelectedFieldRect, см. [4](http://forum.lazarus.freepascal.org/index.php/topic,8713.msg43288.html#msg43288))
14421448
14431449- Поместите элемент управления memo (или любой другой элемент управления) на форму, установите любые свойства, которые вы хотите, и установите видимым значение false. Он будет использоваться при редактировании ячейки сетки. Мы будем использовать GridCellMemo в этом примере.
14441450- В событии OnSelectEditor вставьте следующий код - адаптируйте, если вы не хотите редактировать логический столбец 3, физический столбец 4:
14451451
14461452``` pascal
14471453 if (Column.DesignIndex = 3) then
14481454 begin
14491455 GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
14501456 GridCellMemo.Text:=Column.Field.AsString;
14511457 Editor := GridCellMemo;
14521458 end;
14531459```
14541460
14551461- Предположим, ваш источник данных называется Datasource1, а DBGrid называется ResultsGrid. Затем в событие OnEditingDone для GridCellMemo поместите следующее:
14561462
14571463(В исходном сообщении на форуме использовалось событие OnChange, которое будет запускаться каждый раз при изменении содержимого. Использование OnEditingDone срабатывает только после того, как пользователь завершит редактирование.)
14581464
14591465``` pascal
14601466 if not(Datasource1.State in [dsEdit, dsInsert]) then Datasource1.Edit;
14611467
14621468 Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;
14631469```
14641470
14651471**Осторожно** Использование обычного элемента управления в качестве пользовательского редактора имеет много недостатков: он не будет изменять размеры или перемещать себя при изменении размера столбцов или прокрутке сетки, вы должны позаботиться о модификации данных в сетке. (как объяснено выше), он не работает должным образом с DbGrids и мешает нормальной навигации по сетке с помощью клавиатуры. Сетка взаимодействует с редактором с помощью некоторых специальных сообщений сетки, поэтому для управления этими сообщениями лучше выделить подкласс элемента управления, как объясняется в пятой главе [этого документа](http://www.freepascal.org/~michael/articles/grids/grids.pdf).
14661472
14671473**Осторожно** Если у вас несколько сеток в одной форме, используйте для каждой свой собственный редактор: если вы используете один и тот же элемент управления, могут произойти плохие вещи, поскольку обе сетки попытаются использовать его одновременно.
14681474
14691475### Пример: исключение отображения текстовых полей как «(Memo)» для DBGrids
14701476
14711477Когда вы используете операторы SQL, такие как `"Select * from A"`, вы можете увидеть «(Memo)» вместо текстового содержимого в ячейке сетки. Есть много способов исправить это, но, возможно, самый простой - изменить выражение `"Select * from A"` на `Select Cast(Column as TEXT) as Column from A`. Это распространенная проблема при использовании компонента DBGrid.
14721478
14731479``` pascal
14741480 var
14751481 sql : UTF8String;
14761482 Query : TSQLQuery;
14771483.....
14781484 sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
14791485 Query.SQL.Text:=sql;
14801486```
14811487
14821488### Сортировка столбцов или строк в DBGrid со стрелками сортировки в заголовке столбца
14831489
14841490Вот пример, который будет сортировать **DBgrid**, используя событие **OnTitleClick** и **TSQLQuery** и индексы. Это также должно работать для любого совместимого набора данных, такого как **TbufDataset**. Функция использует свойство **column.tag** для хранения состояния сортировки для каждого столбца, по которому вы щелкнули, поэтому, когда вы вернетесь к тому, который уже отсортирован, она выберет правильную стрелку сортировки для отображения.
14851491
14861492**Предварительные условия:**
14871493
14881494- Вам понадобится список изображений для хранения стрелок вверх/вниз. Присвойте свой список изображений свойству dbgrid **TitleImageList**.
14891495- Убедитесь, что свойство **[[doc:fcl/sqldb/tsqlquery.maxindexescount.html|TSQLQuery.MaxIndexesCount]]** достаточно велико для хранения новых индексов, которые вы будете создавать. Для этого примера у меня было установлено значение 100.
14901496- Вам также понадобится поле-переменная в разделе private класса для хранения последнего столбца, использованного для сортировки, для этого примера я назвал ее **FLastColumn**.
14911497
14921498В этом примере используемый **TSQLQuery** назван OpenQuery.
14931499
14941500> **Note:** по состоянию на 21 марта 2013 года **TSQLQuery** не имеет возможности очищать индексы, но в качестве обходного пути вы можете установить свойство **unidirectional** в значение true, а затем установить его в значение false, и это очистит индексы.
14951501
14961502Чтобы повторно использовать компонент **TSQLQuery** с другим оператором SQL, индексы должны быть очищены после сортировки, иначе будет возбуждено исключение при открытии **TSQLQuery** с другим оператором SQL.
14971503
14981504``` pascal
14991505FLastColumn: TColumn; //сохраняем последний столбец сетки, по которому мы делали сортировку
15001506
15011507procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
15021508const
15031509 ImageArrowUp=0; //должно соответствовать изображению в imagelist
15041510 ImageArrowDown=1; //должно соответствовать изображению в imagelist
15051511var
15061512 ASC_IndexName, DESC_IndexName:string;
15071513 procedure UpdateIndexes;
15081514 begin
15091515 // Убедитесь, что индексы обновлены
15101516 OpenQuery.IndexDefs.Updated:=false; {<<<--Эта строчка имеет решающее значение. IndexDefs.Update не будет обновляться, если уже имеет значение true, что произойдет в первом отсортированном столбце.}
15111517 Openquery.IndexDefs.Update;
15121518 end;
15131519begin
15141520 ASC_IndexName:='ASC_'+Column.FieldName;
15151521 DESC_IndexName:='DESC_'+Column.FieldName;
15161522 // индексы не могут сортировать двоичные типы, такие как ftMemo, ftBLOB
15171523 if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
15181524 exit;
15191525 // проверяем, существует ли восходящий индекс для этого столбца.
15201526 // если нет, создаем
15211527 if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
15221528 begin
15231529 OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
15241530 UpdateIndexes; //ensure index defs are up to date
15251531 end;
15261532 // проверяем, существует ли нисходящий индекс для этого столбца.
15271533 // если нет, создаем
15281534 if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
15291535 begin
15301536 OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
15311537 UpdateIndexes; //ensure index defs are up to date
15321538 end;
15331539
15341540 // Используем тег столбца для переключения направления сортировки ASC/DESC
15351541 column.tag := not column.tag;
15361542 if boolean(column.tag) then
15371543 begin
15381544 Column.Title.ImageIndex:=ImageArrowUp;
15391545 Openquery.IndexName:=ASC_IndexName;
15401546 end
15411547 else
15421548 begin
15431549 Column.Title.ImageIndex:=ImageArrowDown;
15441550 OpenQuery.IndexName:=DESC_IndexName;
15451551 end;
15461552 // Удаляем стрелку сортировки из предыдущего столбца, который мы отсортировали
15471553 if (FLastColumn <> nil) and (FlastColumn <> Column) then
15481554 FLastColumn.Title.ImageIndex:=-1;
15491555 FLastColumn:=column;
15501556end;
15511557```
15521558
15531559### Выбор записей в DBGrid с помощью флажков(чекбоксов)
15541560
15551561Цель состоит в том, чтобы иметь возможность выбирать произвольные записи в dbgrid с помощью флажков, сетка имеет возможность автоматически отображать флажки, когда обнаруживает наличие логических полей, для других типов полей пользователь может вручную выбрать значение cbsCheckboxColumn свойства ButtonStyle для столбца. Для этого типа столбцов пользователь просто устанавливает флажок, и содержимое поля изменяется соответствующим образом.
15561562
15571563Но что произойдет, если в нашем наборе данных нет такого доступного поля? Или мы не хотим, чтобы сетка входила в состояние редактирования при установке флажка? При добавлении столбца без полей с помощью `ButtonStyle = cbsCheckboxColumn` все флажки будут выделены серым цветом и отключены, поскольку поле, связанное с этим столбцом, отсутствует и поэтому изменять нечего. Поскольку мы хотим обрабатывать состояние флажка самостоятельно, нам нужно хранить состояние где-то для каждой записи. Для этого мы можем использовать класс TBookmarklist (определенный в модуле dbgrids.pas), где свойство CurrentRowSelected может сказать, выбрана текущая запись или нет. Используя события dbgrid OnCellClick и OnUserCheckboxState, мы можем отслеживать состояние флажка.
15581564
15591565> **Note:** эта хитрость требует наличие Lazarus r31148 или новее, который реализует событие OnUserCheckboxState.
15601566
15611567``` pascal
15621568...
15631569uses ..., dbgrids, stdctrls, ...
15641570
15651571type
15661572
15671573 { TForm1 }
15681574
15691575 TForm1 = class(TForm)
15701576 ...
15711577 procedure DBGrid1CellClick(Column: TColumn);
15721578 procedure DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
15731579 procedure FormCreate(Sender: TObject);
15741580 procedure FormDestroy(Sender: TObject);
15751581 ...
15761582 private
15771583 RecList: TBookmarklist;
15781584 ...
15791585 end;
15801586
15811587procedure TForm1.DBGrid1CellClick(Column: TColumn);
15821588begin
15831589 if Column.Index=1 then
15841590 RecList.CurrentRowSelected := not RecList.CurrentRowSelected;
15851591end;
15861592
15871593procedure TForm1.FormCreate(Sender: TObject);
15881594begin
15891595 RecList := TBookmarkList.Create(DbGrid1);
15901596end;
15911597
15921598procedure TForm1.FormDestroy(Sender: TObject);
15931599begin
15941600 RecList.Free;
15951601end;
15961602
15971603procedure TForm1.DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
15981604begin
15991605 if RecList.CurrentRowSelected then
16001606 AState := cbChecked
16011607 else
16021608 AState := cbUnchecked;
16031609end;
16041610```
16051611
16061612### Пример: Как экспортировать данные из DBGrid в текстовый файл
16071613
16081614Есть несколько способов создать текстовый файл из содержимого DBGrid. В большинстве случаев люди будут говорить вам, что вместо того, чтобы думать о DBGrid, как о контейнере данных, лучше поискать связанный набор данных.
16091615
16101616Но что произойдет, когда вам понадобится текстовое представление, которое должно соответствовать внешнему виду сетки: видимость колонок, ширина или порядок?
16111617
16121618Одним из решений является использование пакета LazReport, в котором есть компонент, специально предназначенный для создания отчета из DbGrid. Затем этот отчет можно экспортировать в несколько других форматов, включая текстовый формат. Здесь представлено простое решение, которое также может быть использовано в качестве основы для других экспортеров текстового формата, например, простого экспортера HTML.
16131619
16141620``` pascal
16151621// aGrid: Сетка для экспорта
16161622// lpp: строк на странице. Не включает текстовый заголовок
16171623// pageBreak: вставляет разрыв страницы, который некоторые принтеры могут использовать для запуска новой страницы
16181624// require: uses LazUTF8
16191625function GridTextExporter(aGrid: TDbGrid; lpp: Integer; pageBreak:boolean): TStringList;
16201626const
16211627 PX_PER_CHAR = 7;
16221628
16231629var
16241630 Ds: TDataset;
16251631 book: TBookMark;
16261632 recCount: Integer;
16271633 line: string;
16281634 column: TColumn;
16291635
16301636 function WidthToChar(aWidth: Integer): integer;
16311637 begin
16321638 result := trunc(aWidth/PX_PER_CHAR);
16331639 end;
16341640
16351641 procedure AddNext(theText: string; alignment: TAlignment);
16361642 var
16371643 width: Integer;
16381644 begin
16391645 if (line<>'') and (line<>#12) then
16401646 line := line + ' ';
16411647
16421648 width := WidthToChar(Column.Width);
16431649
16441650 case Alignment of
16451651 taRightJustify: line := line + UTF8PadLeft(theText, width);
16461652 taCenter: line := line + UTF8PadCenter(theText, width);
16471653 else line := line + UTF8PadRight(theText, width);
16481654 end;
16491655 end;
16501656
16511657 procedure CollectHeader;
16521658 begin
16531659 AddNext(Column.Title.Caption, Column.Title.Alignment);
16541660 end;
16551661
16561662 procedure CollectField;
16571663 var
16581664 field: TField;
16591665 begin
16601666 field := Column.Field;
16611667
16621668 if (field.DataType=ftMemo) and (dgDisplayMemoText in aGrid.Options) then
16631669 AddNext(field.AsString, Column.Alignment)
16641670 else if Field.DataType<>ftBlob then
16651671 AddNext(field.DisplayText, Column.Alignment)
16661672 else
16671673 AddNext('(blob)', Column.Alignment);
16681674 end;
16691675
16701676 procedure LoopColumns(printingRecord: boolean);
16711677 var
16721678 c: TCollectionItem;
16731679 begin
16741680 if (not printingRecord) and pageBreak and (result.count>0) then
16751681 line := #12
16761682 else
16771683 line := '';
16781684 for c in aGrid.Columns do begin
16791685 Column := TColumn(c);
16801686 if Column.Visible and (Column.Width>=PX_PER_CHAR) then begin
16811687 if printingRecord then CollectField
16821688 else CollectHeader;
16831689 end;
16841690 end;
16851691 result.add(line);
16861692 end;
16871693
16881694begin
16891695 result := TStringList.create;
16901696 Ds := aGrid.DataSource.DataSet;
16911697 Ds.DisableControls;
16921698 book := Ds.GetBookmark;
16931699 try
16941700 Ds.First;
16951701 recCount := 0;
16961702 while not DS.EOF do begin
16971703 if RecCount mod lpp = 0 then
16981704 LoopColumns(false);
16991705 LoopColumns(true);
17001706 inc(recCount);
17011707 Ds.Next;
17021708 end;
17031709 finally
17041710 Ds.GotoBookmark(book);
17051711 Ds.FreeBookmark(book);
17061712 Ds.EnableControls;
17071713 end;
17081714end;
17091715```
17101716
17111717Чтобы использовать это просто сделайте:
17121718
17131719``` pascal
17141720var
17151721 L: TStringList;
17161722begin
17171723 L := GridTextExporter(grid, 80, true);
17181724 L.SaveToFile('grid.txt');
17191725 L.Free;
17201726end;
17211727```
17221728
17231729---
17241730*From the Free Pascal wiki, page "Grids Reference Page/ru", as it stood on 2022-01-21 after a change by Slawek, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history