Sign in Join
Wiki copytemporary

Grids Reference Page/ru: what changed

Revision 1 to 2. 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
2424`                     `[[TCustomControl]]`           `\
2525`                             |                    `\
2626`                             |                    `\
2727`                        TCustomGrid               `\
2828`                             |                    `\
2929`               +-------------+------------+       `\
3030`               |                          |       `\
3131`         TCustomDrawGrid             TCustomDbGrid`\
3232`               |                          |       `\
3333`      +--------+--------+                 |       `\
3434`      |                 |              `[[TDBGrid]]`    `\
3535`  `[[TDrawGrid]]`      TCustomStringGrid                `\
3636`                        |                         `\
3737`                        |                         `\
3838`                   `[[TStringGrid]]
3939
4040## Начальный пример
4141
4242Поскольку одной из целей этой страницы является помощь людям с небольшими или отсутствующими предшествующими знаниями Lazarus'а, давайте сделаем быстрый стартовый пример сетки в действии. Почему бы и нет, давайте сделаем традиционный пример "hello world", используя компонент TStringGrid.
4343
44441. Создайте новое приложение.
4545 - В главном меню выберите: project-\>New Project
4646 - В диалоговом окне Create New Project нажмите кнопку "Create"
4747 - Будет показана новая пустая форма..
48482. Поместите сетку в форму
4949 - В палитре компонентов выберите вкладку "additional"
5050 - Нажмите значок TStringGrid ![](/wiki-images/6/67/Tstringgrid.png)
5151 - Нажмите на форму рядом с левым верхним углом. Появится новая пустая сетка.
52523. Поместите кнопку в форму
5353 - с помощью палитры компонентов выберите вкладку "Standard"
5454 - Нажмите значок TButton ![](/wiki-images/b/b2/Tbutton.png)
5555 - Нажмите на пустую область формы. Появится новая кнопка.
56564. Дважды нажмите кнопку из шага 3 и запишите следующий код в обработчике щелчка кнопки:
5757 - ``` pascal
5858 StringGrid1.Cells[1,1] := 'Привет, Мир!';
5959 ```
60605. Запустите программу, щелкнув значок воспроизведения ![](/wiki-images/8/85/Menu_run.png)
6161 - по нажатию кнопки Button1, текст приветствия будет отображаться в столбце 1 ячейки 1 (обратите внимание, что левый столбец и верхняя строка равны 0)
6262
6363## Различия между сетками Lazarus и Delphi
6464
6565Текущий компонент сетки отличается от сетки Delphi по нескольким направления. При начальной разработке сетки Lazarus создавались с нуля без какой-либо попытки сделать их полностью совместимыми с Delphi.
6666
6767На более позднем этапе совместимость с сетками Delphi стала желаемой целью, и сетки Lazarus стали более тесно соответствовать интерфейсу сетки Delphi. Однако даже это было сделано без попытки сделать каждое свойство или метод сетки Lazarus сопоставимым с его аналогом в Delphi. Кроме того (поскольку внутренняя структура сетки Lazarus сильно отличается от внутренних элементов сетки Delphi), некоторая функциональность Delphi невозможна или должна быть эмулирована иначе в сетке Lazarus \[в отличие от того\], как это будет сделано в сетке Delphi. Мы достигли большей совместимости с Delphi в процессе эволюции сетки Lazarus, и это соблазнительно.
6868
6969### Различия
7070
7171Известные различия перечислены ниже, ни в каком специальном порядке.
7272
7373- Редакторы ячеек
7474- Поведение в designtime
7575- Отрисовка ячеек имеет некоторые отличия, см. раздел настройки сетки.
7676
7777### Новая функциональность
7878
7979- Пользовательские столбцы
8080- События
8181- Редактор сетки
8282
8383### Способы, которыми вы можете сделать сетку Lazarus более совместимой с Delphi
8484
8585Вы можете сделать сетку Lazarus похожей и ведущей себя подобно \[сетке\] Delphi. Ниже перечислены параметры свойств, которые \[позволят\] достигнуть этого. Эти корректировки основаны на вновь созданной сетке. \[Свойства,\] помеченные тэгом \[Code\], должны быть определены в коде, тэгом \[Design\] могут быть изменены во время разработки.
8686
8787- \[Design\] TitleStyle := tsStandard;
8888- \[Design\] DefaultRowHeight := 24;
8989- \[Code\] EditorBorderStyle := bsNone; // это может работать только в Windows.
9090- \[Code\] UseXORFeatures := true;
9191- \[Code\] AllowOutBoundEvents := False; {SVN-ревизия 10992 или старше}
9292- \[Code\] FastEditing := False; (поддерживается в dbgrid. StringGrid требует SVN-ревизию 10992 или старше)
9393- \[Design\] AutoAdvance := aaNone;
9494
9595## Справочник по сеткам
9696
9797### Информация
9898
9999Отправной точкой для \[получения\] справки о TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid и TStringGrid является [справка по модулю Grids.pas](http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html)
100100
101101Для TCustomDBGrid и TDBgrid это [справка по модулю DBGrids.pas](http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html)
102102
103103В общем, любая ссылка Delphi о сетках должна помочь нам использовать сетки Lazarus (но не забывайте, что есть несколько различий между сетками Delphi и Lazarus, которые мы задокументировали); имея это в виду и в качестве временного места для справочной информации, это место будет использоваться для документирования тех вещей, которые не работают аналогично Delphi, а также любых новых функций.
104104
105105TODO: остальная часть этого раздела должна исчезнуть; его содержание будет перенесено в [справку по модулю Grids.pas](http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html)
106106
107107### TCustomGrid
108108
109109См. полную [[doc:lcl/grids/tcustomgrid.html|справку TCustomGrid]]
110110
111111#### свойство AllowOutboundEvents
112112
113113Protected\[-свойство\] в TCustomGrid, public\[-свойство\] в TCustomDrawGrid и его наследниках. Обычно, когда пользователь нажимает в точку над пустым пространством после ячеек (например, если сетка имеет три строки, но пользователь нажимает на воображаемую четвертую строку), текущий фокус ячейки перемещается ячейку, ближайшую к только что щелкнутой точке. Мы называем это исходящим событием. Значение по умолчанию - true, так как это изначальное поведение сетки. Это свойство было добавлено для моделирования поведения Delphi, когда исходящие события недоступны, поэтому для обеспечения совместимости с Delphi это свойство имеет значение false.
114114
115115#### свойство Columns
116116
117117Lazarus включает свойства **columns** в сетки TStringGrid и TDrawGrid. Это свойство добавляет так называемые пользовательские столбцы. Пользовательские столбцы представляют собой набор объектов, которые содержат свойства, применяемые к целым столбцам, например заголовки столбцов (для StringGrid он переопределяет значение, указанное в соответствующем свойстве Cells \[ColumnIndex, RowTitle\]), выравнивание текста, цвет фона, предпочтительный редактор и т.д.
118118
119119Пользовательские столбцы добавляют дополнительные свойства или заменяют значения свойств по умолчанию в столбцах стандартной сетки. Но не только это; значение **grid.ColCount** может увеличиваться или уменьшаться, чтобы учесть количество настраиваемых столбцов, которые привязаны к сетке. На этом этапе это означает, что **grid.ColCount** = **grid.FixedCols** + **grid.Columns.Count**.
120120
121121Например, если к базовой сетке с ColCount=5 и FixedCols=2 мы:
122122
123123- Добавим 3 пользовательских столбца, базовая сетка будет точно такой же, как раньше, 2 фиксированных столбца и 3 обычных столбца.
124124- Добавим 1 пользовательский столбец, базовая сетка будет иметь ColCount=3, то есть 2 фиксированных столбца и 1 нормальный столбец.
125125- Добавим 4 пользовательских столбца, базовая сетка будет иметь ColCount=6, то есть 2 фиксированных столбца и 4 нормальных столбца.
126126
127127Из этого можно сделать вывод, что:
128128
129129- Свойства фиксированного столбца или счетчики не увеличиваются или не изменяются соответственно пользовательскими столбцами.
130130- **grid.ColCount** обычно отличается от **grid.Columns.Count** (**grid.ColCount**=**grid.Columns.Count**, только когда **FixedCols**=0)
131131
132132Во время разработки пользователь может получить доступ к свойству **columns** в Object Inspector, чтобы вызвать редактор столбцов. Оттуда вы можете добавлять, удалять или изменять настраиваемые столбцы. Редактор отображает список текущих пользовательских столбцов; путем выбора элементов в этом списке инспектор объектов заполняется свойствами, доступными для каждого столбца. Список настраиваемых столбцов также доступен в представлении дерева компонентов Object Inspector, где столбцы можно добавлять, удалять или изменять. Они появляются на нижнем уровне под сеткой контейнера.
133133
134134В runtime столбцы могут быть изменены с помощью кода:
135135
136136``` pascal
137137 var
138138 c: TGridColumn;
139139 begin
140140 // Добавляем пользовательский столбец в сетку
141141 c := Grid.Columns.Add;
142142 // модифицируем
143143 c.title.caption := 'Цена'; // Устанавливаем заголовок столбца
144144 c.align := taRightJustify; // Выравниваем содержимое столбца по правому краю
145145 c.color := clMoneyGreen; // Изменяем цвет по умолчанию на clMoneyGreen
146146 c.Index := 0; // Делаем этот столбец первым
147147 // доступ к существующему столбцу
148148 grid.columns[0].Width := 60; // Изменяем ширину столбца 0 на 60 пикселей
149149 // удаляем существующий столбец
150150 grid.columns.delete(0); // Удаляем столбец 0
151151 ....
152152 end;
153153```
154154
155155Кроме того, когда задействуются пользовательские столбцы, сетки не допускают прямой модификации \[свойства\] **grids.colcount**; добавление или удаление столбцов должно выполняться с использованием свойства **columns**. Объяснение состоит в том, что существует некоторая несогласованность в постепенном удалении пользовательских столбцов с использованием **ColCount**: когда \[значение\] **ColCount** становится равным **FixedCols**, \[это означает, что\] сетка не имеет больше настраиваемых столбцов. Если теперь увеличить **ColCount**, новый столбец будет не настраиваемым, а нормальным столбцом.
156156
157157В настоящее время нет планов, чтобы сетки использовали только настраиваемые столбцы.
158158
159159#### свойство AutoFillColumns
160160
161161Часто сетки шире, чем горизонтальное пространство, необходимое для столбцов - это оставляет неприятную пустую область справа от последнего столбца. Сетки LCL предоставляют механизм для расширения ширины указанных столбцов, так что пустое пространство заполняется автоматически.
162162
163163Для этой цели необходимо либо добавить **Columns**, как описано [[Grids Reference Page/ru|выше]], либо установив свойство `ColCount`, и свойство сетки `AutoFillColumns` должно быть установлено в `true`. Каждый столбец имеет свойство `SizePriority`. Если оно имеет значение `0`, ширина столбца берется из свойства `Width` столбца. Но когда оно имеет ненулевое значение, ширина столбца корректируется до среднего доступного размера, оставшегося для всех столбцов с ненулевым `SizePriority`.
164164
165165### TCustomDBGrid
166166
167167TCustomDBGrid основан на TDBGrid.
168168
169169Они не отображают свойств Col и Row. Чтобы перейти к определенному столбцу, используйте, например, свойство SelectedIndex.
170170
171171Интересным public-методом является AutoSizeColumns.
172172
173173#### процедура AutoSizeColumns
174174
175175Эта процедура устанавливает ширину столбца по размеру максимально широкого найденного текста. Его можно использовать после загрузки набора данных/установки \[свойства\] Active.
176176
177177Однако, в противоположность \[процедуре\] TCustomStringGrid.AutoSizecolumns (см. ниже), эта \[процедура\] будет устанавливать столбцы максимально широко, пока вы не задействуете \[параметр грида\] **dgAutoSizeColumns**.
178178
179179#### процедура InplaceEditor
180180
181181См. пример из bug 23103 - *и вставьте объяснение того, что он делает/почему это необходимо. Проверить входные значения? Изменить то, что показано?*
182182
183183``` pascal
184184procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: char);
185185var
186186 S: String;
187187begin
188188 if (Key in [',','.']) then
189189 begin
190190 //в отличие от Delphi не все InPlaceEditor'ы - это редакторы для строкового типа, поэтому проверьте!
191191 if (DBGrid1.InplaceEditor is TStringCellEditor) then
192192 begin
193193 S := TStringCellEditor(DBGrid1.InplaceEditor).EditText;
194194 if Pos(',',S) > 0 then
195195 Key := #0
196196 else
197197 Key := ',';
198198 end;
199199 end;
200200end;
201201```
202202
203203### TCustomStringGrid
204204
205205TCustomStringGrid служит основой для TStringGrid. Он может использоваться для \[написания\] производных компонентов \[типа\] TStringGrid, которые хотят скрыть published-свойства. Дополнительную информацию см. в [[new intermediate grids]].
206206
207207Следующие public-свойства или public-методы являются доступными для TStringGrid.
208208
209209См. полную [[doc:lcl/grids/tcustomstringgrid.html|справку TCustomStringGrid]]
210210
211211#### процедура AutoSizeColumn(aCol: Integer);
212212
213213Эта процедура устанавливает ширину столбца \[в соответствии с\] размером самого широкого текст, который был ею найден во всех строках столбца **aCol**. Совет: см. параметр **goDblClickAutoSize**, чтобы позволить автоматически изменять размер столбцов при двойном нажатии на границу столбца.
214214
215215#### процедура AutoSizeColumns;
216216
217217Автоматически изменяет размеры всех столбцов так, чтобы они растягивались под самый длинный текст для каждого столбца. Это быстрый способ применения AutoSizeColumn() для каждого столбца в сетке.
218218
219219#### процедура Clean; overload;
220220
221221Очищает все ячейки в сетке, \[будь то\] фиксированные или нет.
222222
223223#### процедура Clean(CleanOptions: TGridZoneSet); overload;
224224
225225Очищает все ячейки в сетке в соответствии с данными CleanOptions. Дополнительную информацию см. в [[doc:lcl/grids/tgridzoneset.html|TGridZoneSet]]. Некоторые примеры:
226226
227227- Очистить все ячейки: `grid.Clean([]);` (аналогично `grid.clean`)
228228- Очистить все нефиксированные ячейки: `grid.Clean([gzNormal]);`
229229- Очистить все ячейки, но не касается заголовков столбцов сетки: `Grid.Clean([gzNormal, gzFixedRows]);`
230230
231231#### процедура Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TGridZoneSet); overload;
232232
233233делает то же самое, что и `Clean(CleanOptions: TGridZoneSet)`, но ограничивается данными `StartCol, StartRow, EndCol и EndRow`. Примеры:
234234
235235- Очистить индекс столбца с 4 по 6, но не касаться заголовков столбцов сетки, много вариантов:
236236
237237``` pascal
238238Grid.Clean (4,Grid.FixedRows, 6,Grid.RowCount-1,[]); //или Grid.Clean(4,0,6,Grid.RowCount-1, [gzNormal]);
239239```
240240
241241и т.п.
242242
243243#### процедура Clean(aRect: TRect; CleanOptions: TGridZoneSet); overload;
244244
245245То же самое, что и `Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions)`, просто берется \[прямоугольник\] TRect вместо отдельных координат ячейки. Для очистки выделения полезно \[сделать\]:
246246
247247``` pascal
248248grid.Clean(Grid.Selection,[]);
249249```
250250
251251==== процедура SaveToCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ==== Сохраняет содержимое сетки в файл формата данных с разделителями-запятыми (CSV) (добавлен в Lazarus с r32179).
252252
253253Аргумент **AFilename** указывает имя файла, в котором содержимое будет сохранено. Если файл существует, содержимое будет перезаписано. Если он не существует, файл будет создан.
254254
255255**ADelimiter** (необязательный аргумент) используется для предоставления настраиваемого разделителя, если требуется. По умолчанию создается формат CSV (то есть ADelimiter: = ',') для TAB-разделяемого файла ADelimiter должен быть \#9.
256256
257257Параметр **WithHeader** используется для определения необходимости включения или отключения "row header"\[(заголовка строки)\]. Заголовок строки - это список имен полей в начале выходного файла; его содержимое извлекается из последней фиксированной строки в сетке. Существует исключение из этого правила: если сетка имеет пользовательские столбцы, содержимое заголовка строки извлекается из заголовков пользовательских столбцов, а не из содержимого ячейки фиксированной строки.
258258
259259Если WithHeader имеет значение true, а сетка не содержит фиксированную строку или настраиваемые столбцы, содержимое заголовка строки будет взято из первой строки в сетке.
260260
261261Вывод CSV-данных обычно начинается с первой нефиксированной строки в сетке.
262262
263263==== процедура LoadFromCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ==== Загружает содержимое сетки из файла формата данных с разделителями-запятыми (CSV) (добавлен в Lazarus с r32179).
264264
265265Столбцы будут добавлены в сетку или удалены из нее по мере необходимости, в соответствии с количеством полей, включенных в каждую строку файла CSV. Загрузка CSV-файла не будет изменять количество фиксированных строк, которые уже существовали в сетке.
266266
267267Аргумент **Afilename** указывает имя исходного файла с содержимым CSV.
268268
269269Дополнительный параметр **ADelimiter** может использоваться для указания другого разделителя. Например: для файла с TAB-разделителями *ADelimiter* должен быть \#9. Другим популярным файловым форматом является файл с разделителем-запятой.
270270
271271Параметр **WithHeader** используется для определения того, следует ли считать первую строку в файле CSV "строкой заголовка" или нет. Если сетка имеет фиксированные строки, а *WithHeader* - \[значение\] true, заголовки столбцов для последней фиксированной строки будут взяты из строки заголовка. Однако, обратите внимание, что если сетка имеет пользовательские столбцы, строка заголовка будет использоваться как источник заголовков столбцов, а заголовки столбцов всегда \[будут\] отображаются в первой фиксированной строке сетки или \[будут\] скрыты, если в сетке не \[окажется\] фиксированных строк.
272272
273273Если в процедуре *LoadFromCSVFile* возникла проблема с загрузкой файла CSV (например, кавычки или пробелы начинают неправильно интерпретироваться), вы можете загрузить сетку вручную, например, [[CsvDocument/ru|CsvDocument]] ... и, конечно, патч для *LoadFromCSVFile* всегда приветствуется.
274274
275275#### свойство Cols\[index: Integer\]: TStrings read GetCols write SetCols;
276276
277277Получает/устанавливает список строк из/в индекса столбца данной сетки, начиная с индекса строки от 0 до RowCount-1.
278278
279279##### Примеры
280280
281281- Пример определения \[содержимого строки\]:
282282
283283``` pascal
284284//задать содержимое третьего столбца в сетке из списка ListBox
285285Grid.Cols[2] := ListBox1.Items;
286286```
287287
288288- Пример получения \[содержимого строки\]:
289289
290290``` pascal
291291//задать содержимое Listbox, [получив значение] из столбца сетки с индексом 4:
292292procedure TForm1.FillListBox1;
293293var
294294 StrTempList: TStringList;
295295begin
296296 StrTempList := TStringList(Grid.Cols[4]);
297297 if StrTempList<>nil then begin
298298 ListBox1.Items.Assign(StrTempList);
299299 StrTempList.Free;
300300 end;
301301end;
302302```
303303
304304##### На заметку
305305
306306Это свойство работает по-разному в Lazarus и Delphi при получении данных из сетки. В Lazarus создается временный объект TStringList для извлечения содержимого столбца. Пользователь должен освобождать этот объект после использования.
307307
308308Это также означает, что изменения в возвращенном списке не будут влиять на содержимое или макет сетки.
309309
310310См. пример получения \[содержимого строки\] выше.
311311
312312#### свойство Rows\[index: Integer\]: TStrings read GetRows write SetRows;
313313
314314Получает/определяет список строк из/в индекса строки данной сетки, начиная с индекса столбца 0 до столбца ColCount-1.
315315
316316##### На заметку
317317
318318Это свойство работает по-разному в Lazarus и Delphi при получении данных из сетки. В Lazarus создается временный объект TStringList для извлечения содержимого строки. Пользователь должен освобождать этот объект после использования.
319319
320320Это также означает, что изменения в возвращенном списке не будут влиять на содержимое или макет сетки.
321321
322322##### Примеры
323323
324324- Пример определения \[содержимого строки\]:
325325
326326``` pascal
327327//задать содержимое третьего столбца в сетке из списка ListBox
328328Grid.Rows[2] := ListBox1.Items;
329329```
330330
331331- Пример получения \[содержимого строки\]:
332332
333333``` pascal
334334//задать содержимое Listbox, [получив значение] из столбца сетки с индексом 4:
335335procedure TForm1.FillListBox1;
336336var
337337 StrTempList: TStringList;
338338begin
339339 StrTempList := TStringList(Grid.Rows[4]);
340340 if StrTempList<>nil then begin
341341 ListBox1.Items.Assign(StrTempList);
342342 StrTempList.Free;
343343 end;
344344end;
345345```
346346
347347- Пример, который не работает, и его исправление: список извлеченных строк - только для чтения
348348
349349``` pascal
350350// это не сработает и вызовет утечку памяти
351351// потому что возвращаемый StringList не освобождается
352352Grid.Rows[1].CommaText := '1,2,3,4,5';
353353Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g';
354354
355355//исправление первого случая
356356Lst:=TStringList.Create;
357357Lst.CommaText := '1,2,3,4,5';
358358Grid.Rows[1] := Lst;
359359Lst.Free;
360360```
361361
362362#### свойство UseXORFeatures;
363363
364364Boolean свойство, значение по умолчанию: False;
365365
366366Это свойство \[отвечает за то\], как прямоугольник фокуса отображается в сетке. Когда \[его значение равно\] True, прямоугольник отрисовывается \[вокруг ячейки\] с использованием растровой операции XOR. Это позволяет нам видеть прямоугольник фокуса, вне зависимости от того, какой цвет фона \[имеют\] ячейки. Когда \[его значение равно\] False, пользователь может управлять цветом точек фокуса прямоугольника с помощью свойства [[FocusColor property]]
367367
368368Он также контролирует внешний вид изменения размера столбца/строки. Когда \[его значение равно\] True, линия визуально показывает размер, который \[только еще\] будет иметь столбец или строка, если пользователь завершит операцию. Когда \[его значение равно\] False, изменение размера столбца или строки вступает в силу \[сразу же\], как \[только\] пользователь \[начнет\] перетаскивать мышь.
369369
370370### TValueListEditor
371371
372372TValueListEditor - это элемент управления, полученный из TCustomStringGrid для редактирования пар ключ-значение.
373373
374374#### свойство DisplayOptions
375375
376376Управляет различными аспектами внешнего вида TValueListEditor.
377377
378378#### свойство TitleCaptions
379379
380380Устанавливает значения подписи заголовков (если \[свойство\] **doColumnTitles** \[включено в набор свойств\] **DisplayOptions**). Если `DisplayOptions` не \[содержит\] значения `doColumnTitles`, то используются заголовки \[с надписями\] по умолчанию.
381381
382382#### свойство Strings
383383
384384Предоставляет доступ к списку строк, которые содержат пары Key-Value.\
385385Пары «ключ-значение» должны быть в форме:\
386386`KeyName=Value`
387387
388388#### свойство ItemProps
389389
390390Вы можете использовать это свойство для управления тем, как элементы в столбцах \[со значением\] "Value" можно редактировать.\
391391Это контролируется установкой свойств **EditStyle** и **ReadOnly** \[свойства\] **ItemProp**.
392392
393393#### свойство KeyOptions
394394
395395KeyOptions - это набор TKeyOptions, определяющий, может ли пользователь изменять содержимое столбца "Key".
396396
397397- KeyEdit: пользователь может редактировать имя ключа
398398- KeyAdd: пользователь может добавить ключи (нажав Insert в сетке). KeyAdd требует KeyEdit.
399399- KeyDelete: пользователь может удалить пары Key-Value (нажав Ctrl+Delete).
400400- KeyUnique: если установлено, то ключи должны иметь уникальные имена. Попытка ввести дубликат ключа вызовет исключение.
401401
402402#### свойство DropDownRows
403403
404404Если редактор ячейки является списком выбора (ValueEdit1.ItemProps\['key1'\].EditStyle=esPickList), это свойство устанавливает DropDownCount\[(высоту выпадающего)\] отображаемого списка. По умолчанию используется значение 8.
405405
406406#### функция DeleteRow
407407
408408Удаляет пару Key-Value индексированной строки, полностью удаляя строку.
409409
410410#### функция InsertRow
411411
412412Вставляет строку в сетку и устанавливает пару Key-Value. Возвращает индекс вновь вставленной строки.
413413
414414#### функция IsEmptyRow
415415
416416Возвращает \[значение\] true, если ячейки индексированных строк пусты (Keys\[aRrow\]=*; Valves\[aRrow\]=*).
417417
418418#### функция FindRow
419419
420420Возвращает строку с указанным именем ключа.
421421
422422#### функция RestoreCurrentRow
423423
424424Отменяет редактирование в текущей строке (если редактор все еще в фокусе). \[Событие\] случается, когда пользователь нажимает клавишу Escape.
425425
426426#### Измененное поведение некоторых свойств, унаследованных из TCustomStringGrid
427427
428428##### Параметры Options
429429
430430В соответствии с особенностями TValueListEditor его свойство Options имеет определенные ограничения
431431
432432- goColMoving не доступно в Options (вы не можете установить его).
433433- goAutoAddRows может быть установлен только в том случае, если KeyAdd отмечен в KeyOptions. Установка KeyAdd автоматически установит goAutoAddRows.
434434- goAutoAddRowsSkipContentCheck недоступен (на данный момент это приводит к сбою в TValueListeditor: требуется исправление).
435435
436436##### свойство FixedRows
437437
438438Может \[иметь значение\] только 1 (показать названия столбцов) или 0 (не показывать заголовки столбцов)
439439
440440##### свойство ColCount
441441
442442Всегда \[будет значение\] 2.
443443
444444#### Общие замечания по использованию TValueListEditor
445445
446446При манипулировании содержимым ValueListEditor (сетка) рекомендуется манипулировать лежащим в основе свойством Strings.\
447447Если вы хотите вставлять или удалять строки, то либо делайте это путем прямого доступа к свойству Strings, либо используйте общедоступные методы из `TValueListEditor`: `DeleteRow()`, `InsertRow()`, `MoveRow()` и `ExchangeRow()`.\
448448Попытка использовать методы предка для управления строками или столбцами (например, Columns.Add) может привести к сбою.
449449
450450#### Saving and loading the contenst of a TValueListEditor
451451
452452##### Using the Strings property
453453
454454If 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.\
455455In case of LoadFromFile the `RowCount` property will be autmatically adjusted.\
456456No sanity checks are performed upon LoadFromFile (so lines that do not represent a Key/Value pair will end up as Keys without Value).
457457
458458##### Using TValueList.SaveToFile and TValueList.LoadFromFile
459459
460460When using SaveToFile and LoadFromFile you get the additional benefits of also being able to save and load the layout, like with other grids.\
461461SaveToFiel will also save information about wether or not the TValueListEditor uses ColumnTitles, and if so, it saves them.\
462-\
462+
463463In 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.\
464464If the file does not seem to be a valid TValueListEditor grid file, and exception is raised.\
465-\
465+
466466TValueList.SaveToFile and TValueList.LoadFromFile do not work correctly in Lazarus versions \<= 2.1 r62044.
467467
468468##### Using SaveToCSVFile and LoadFromCSV file
469469
470-These methods should <b>not</b> be used at the moment, since for a TValueListEditor they are flawed.
470+These methods should not be used at the moment, since for a TValueListEditor they are flawed.
471471
472472## Работа с сетками
473473
474474### Настройка сеток
475475
476476Сетка - это компоненты, полученные из класса [TCustomControl](http://lazarus-ccr.sourceforge.net/docs/lcl/controls/tcustomcontrol.html) и не имеющие родственного виджета, связанного с ними, что означает, что сетки не ограничены по виду текущей темы интерфейса. Это может быть как преимуществом, так и недостатком: обычно программисты хотят создавать приложение с одинаково выглядящим интерфейсом. Хорошей новостью является то, что сетки Lazarus достаточно гибки, чтобы получить что-то из обоих миров; программисты могут легко сделать сетки похожими на другие собственные элементы управления, или они могут настроить сетку в мельчайших деталях, чтобы они могли получать почти одинаковый вид в любом интерфейсе платформы или виджета (то есть, за исключением полос прокрутки, поскольку их внешний вид все еще определяется текущей темой).
477477
478478### Свойства и События для настройки сеток
479479
480480Некоторые свойства могут влиять на то, как выглядит сетка, действуя, когда ячейка собирается быть отрисованной в PrepareCanvas/OnPrepareCanvas, изменяя свойства холста по умолчанию, такие как цвет кисти или шрифт. Ниже приведен список таких свойств:
481481
482482- **AlternateColor.** Этим \[свойством\] пользователь может изменить цвет фона, появляющийся в чередующихся строках. Это позволяет легко считывать данные строк сетки.
483483- **Color.** Это \[свойство\] устанавливает основной цвет, используемый для отрисовки фона нефиксированных ячеек.
484484- **FixedColor.** Это цвет, используемый для создания фона фиксированных ячеек.
485485- **Flat.** Это \[свойство\] устраняет трехмерный вид фиксированных ячеек \[(т.е. делает их плоскими)\].\
486486- **TitleFont.** Шрифт, используемый для отрисовки текста в фиксированных ячейках.\
487487- **TitleStyle.** Это свойство изменяет трехмерный вид неподвижных ячеек, имеет 3 значения \[стилей\]:
488488 - *tsLazarus.* Это стандартный вид.
489489 - *tsNative.* Это \[свойство\] пытается установить внешний вид, соответствующий текущей теме виджетов.
490490 - *tsStandard.* Этот стиль представляет собой более контрастный вид, как у сетки Delphi.
491491- **AltColorStartNormal.** Boolean\[-свойство\]. Если \[имеет значение\] true: чередующийся цвет всегда находится во второй строке после фиксированных строк, первая строка после фиксированных строк будет всегда цветной. Если \[имеет значение\] false: цвет по умолчанию установлен в первую строку, как если бы не было фиксированных строк.
492492- **BorderColor.** Это \[свойство\] устанавливает цвет границы сетки, используемую, когда Flat:=True и BorderStyle:=bsSingle;
493493- **EditorBorderStyle.** Если установлено значение bsNone под \[ОС\] windows, редакторы ячейки не будут иметь границы, как в delphi по умолчанию для bsSingle, потому что граница может быть специфичной для темы в некоторых виджетах и обеспечивать единообразный внешний вид.\
494494- **FocusColor.** Цвет, используемый для отрисовки текущего фокуса ячейки, если \[свойство\] UseXORFeatures не установлено, по умолчанию это clRed.
495495- **FocusRectVisible.** Включает/Выключает отрисовку фокуса ячейки.
496496- **GridLineColor.** Цвет линий сетки в нефиксированной области.
497497- **GridLineStyle.** Стиль Pen используется для отрисовки линий в нефиксированной области, возможны следующие варианты: *psSolid*, *psDash*, *psDot*, *psDashDot*, *psDashDotDot*, *psinsideFrame*, *psPattern*,*psClear*. По умолчанию - *psSolid*.
498498- **SelectedColor.** Цвет, используемый для отрисовки фона ячейки на выбранных ячейках.
499499- **UseXORFeatures.** Если установлено, прямоугольник фокуса рисуется с использованием режима XOR, поэтому он должен сделать видимым прямоугольник фокуса в сочетании с любым цветом ячейки. Это также влияет на просмотр движущихся столбцов.
500500- **DefaultDrawing.** Boolean\[-свойство\]. Обычно сетки готовят холст сетки с использованием некоторых свойств в соответствии с видом окрашиваемой ячейки. Если пользователь пишет обработчик событий OnDrawCell, набор DefaultDrawing также рисует фон ячейки. Если пользователь сам рисует ячейку, лучше отключить это свойство, чтобы отрисовка не дублировалась. В StringGrid набор DefaultDrawing рисует текст в каждой ячейке.
501501- **AutoAdvance.** \[свойство определяет,\] куда установится курсор ячейки при нажатии \[клавиши\] enter или после редактирования.
502502- **TabAdvance.** \[свойство определяет,\] куда переместится курсор ячейки при нажатии \[клавиши\] Tab или Shift-Tab.
503503- **ExtendedColSizing.** Если \[значение равно\] true, пользователь может изменять размер столбцов не только в заголовках, но и по высоте столбцов.
504504
505505Другие свойства, которые также влияют на то, как выглядят сетки.
506506
507507**Options**.
508508
509509
510510Свойство Options - это набор некоторых элементов для обеспечения разнообразной функциональности, но некоторые из них напрямую связаны с внешним видом сетки. Эти параметры можно установить в режиме разработки(designtime) или времени выполнения(runtime).
511511
512512- **goFixedVertLine, goFixedHorzLine** \[свойство\] рисует вертикальную или горизонтальную линию, соответственно разграничивая ячейки или столбцы в фиксированной области, по умолчанию \[значение равно\] active.
513513- **goVertLine, goHorzLine** \[свойство аналогично\] предыдущему, но для нормальной просматриваемой области. Сетка может быть создана для имитации списка, \[если\] отключить оба этих элемента.
514514- **goDrawFocusSelected** если этот элемент включен, фон выделения отрисовывается в сфокусированной ячейке в дополнение к пунктирному прямоугольнику фокуса (обратите внимание, что это не работает, когда установлена опция goRowSelect, в этом случае строка всегда отрисовывается так, как если бы была выбрана опция goDrawFocusSelected)
515515- **goRowSelect** выбирает \[для выделения\] полную строку вместо отдельных ячеек
516516- **goFixedRowNumbering** если \[свойство\] установлено, сетка будет нумеровать строки в первом фиксированном столбце
517517- **goHeaderHotTracking** если \[свойство\] установлено, сетка попытается показать другой вид \[ячейки, когда\] курсор мыши будет находиться над любой фиксированной ячейкой. Для того, чтобы это сработало, желаемая зона ячейки должна быть включена с помощью свойства HeaderHotZones. Попробуйте комбинировать этот параметр с свойством TitleStyle:=tsNative, чтобы получить подсвечивание \[соответственно\] теме \[виджета\].
518518- **goHeaderPushedLook** если этот параметр установлен, этот элемент включает \[эффект\] "нажатия" при клике \[мышью\] любой фиксированной ячейки. Зона "нажатия" ячеек становится доступной при использовании свойства HeaderPusedZones.
519519
520520(напишите еще)
521521
522522### Описание процесса отрисовки сетки
523523
524524Как и другие настраиваемые элементы управления, сетка отрисовывается с использованием метода рисования. В общих чертах сетка отрисовывается путем рисования всех строк и каждой строки путем рисования ее отдельных ячеек.
525525
526526Процесс заключается в следующем:
527527
528528- Сначала определяется область видимых ячеек: каждая строка проверяется, пересекает ли она область отсечения холста; если все в порядке, то видимая область окрашивается путем рисования столбцов каждой строки.
529529- Значения столбцов и строк используются для идентификации ячейки, которая должна быть окрашена, и снова каждый столбец проверяется на пересечение с областью отсечения; если все в порядке, некоторые дополнительные свойства, такие как прямоугольная протяженность ячейки и визуальное состояние, передаются в качестве аргументов методу DrawCell.
530530- Когда процесс отрисовки выполняется, визуальное состояние каждой ячейки настраивается в соответствии с параметрами сетки и расположением в сетке. Визуальное состояние сохраняется в виде типа TGridDrawState, который представляет собой набор со следующими элементами:
531531 - *gdSelected* У ячейки будет выделенный вид.
532532 - *gdFocused* Ячейка будет иметь \[рамку\] фокуса.
533533 - *gdFixed* Ячейка будет нарисована \[как\] фиксированная.
534534 - *gdHot* \[когда\] мышь находится над этой ячейкой, то она отрисовывается с подсвечиванием.
535535 - *gdPushed* \[когда по\] ячейке \[кликают мышью\], она отрисовывается как нажимаемая \[(кликабельная)\]
536536- **DrawCell.** Метод DrawCell является виртуальным и может быть переопределен в унаследованных сетках для выполнения пользовательского рисования. Информация, переданная DrawCell, помогает идентифицировать конкретную ячейку, которая окрашивается, физическую область, занятую экраном, и ее видимый статус. Подробнее см. ссылку DrawCell. Для каждой ячейки происходит следующее:
537537- **PrepareCanvas.** В этом методе, если установлено свойство DefaultDrawing, холст сетки настраивается со свойствами по умолчанию для кисти и шрифта на основе текущего визуального состояния. Для нескольких свойств дизайн-тайма и времени выполнения выравнивание текста устанавливается в соответствии с выбором программиста в пользовательских столбцах, если они существуют. Если DefaultDrawing \[имеет значение\] false, цвет кисти устанавливается в clWindow и цвет шрифта в clWindowText, выравнивание текста задается значением defaultTextStyle сетки.
538538- **OnPrepareCanvas.** Если программист написал обработчик событий для события OnPrepareCanvas, он вызывается в этот момент. Это событие можно использовать для простой настройки, например: изменения цвета фона ячейки, свойств шрифта, таких как цвет, шрифт и стиль, расположение текста, как различные комбинации левого, центрального, верхнего, нижнего, правого выравнивания и т.д. Любые изменения, сделанные на холсте \[ранее\] в этом событии будут потеряны, потому что следующая перерисовка ячейки снова сбросит содержимое холста до состояния по умолчанию. Поэтому безопасно делать изменения только для конкретной ячейки или клеток и забыть об этом для остальных. Использование этого события иногда помогает избежать использования события сетки OnDrawCell, где пользователи будут вынуждены дублировать код отрисовки сетки.\
539539 *Todo*: образцы того, что можно сделать и что оставить для OnDrawCell? ...
540540- **OnDrawCell.** Если не указан обработчик для события OnDrawCell, следующим сетка вызывает метод DefaultDrawCell, который просто рисует фон ячейки, используя текущий цвет и стиль кисти холста. Если обработчик OnDrawCell существует, сетка сначала рисует фон ячейки, но только если установлено свойство DefaultDrawing, \[которое, в свою очередь,\] вызывает событие OnDrawCell для создания пользовательской отрисовки клетки. Обычно программисты хотят делать пользовательскую отрисовку только для определенных ячеек и стандартную отрисовку для других; в этом случае они могут ограничить пользовательскую операцию определенной ячейкой или ячейками, просмотрев аргументы ACol, ARow и AState, а для других ячеек просто вызывают метод DefaultDrawCell и позволяют сетке позаботиться об этом.
541541- **Text.** В этот момент (только для TStringGrid), если свойство DefaultDrawing \[имеет значение\] true, рисуется текстовое содержимое ячейки.
542542- **Grid lines** Последний шаг для каждой ячейки состоит в том, чтобы нарисовать линии сетки: если параметры сетки goVertLine, goHorzLine, goFixedVertLine и goFixedHorzLine указаны, ячейка сетки рисуется в этой точке. Сетки только со строками или только со столбцами могут быть получены путем изменения этих параметров. Если программист предпочел бы иметь "тематический" вид, то это делается и в этот момент (см. свойство TitleStyle).
543543- **FocusRect.** Когда все столбцы текущей строки были отрисованы, наступает время рисовать прямоугольник фокуса для текущей выбранной ячейки или для всей строки, если установлена опция goRowSelect.
544544
545545#### Различия с Delphi
546546
547547- В Lazarus метод TCustomGrid.DrawCell не является абстрактным, и его реализация по умолчанию выполняет базовое заполнение фона ячейки.
548548- В Delphi текст ячейки рисуется перед входом в событие OnDrawCell (см. [bug report \#9619](http://www.freepascal.org/mantis/view.php?id=9619)).
549549
550550### Выбор ячейки сетки
551551
552552Расположение текущей (сфокусированной) ячейки (или строки) сетки может быть изменено с помощью клавиатуры, мыши или кода. Чтобы успешно сменить фокус ячейки на другую позицию, мы должны проверить целевую позицию, чтобы увидеть, разрешено ли ей получать фокус ячейки. При использовании клавиатуры свойство AutoAdvance выполняет часть процесса, обнаруживая, что должно быть следующей сфокусированной ячейкой. При использовании кликов мыши или перемещения \[посредством\] кода фокус не будет перемещаться из текущей ячейки, если целевой ячейке не разрешено получать фокус.
553553
554554Сетка вызывает функцию SelectCell, чтобы увидеть, является ли ячейка фокусируемой: если эта функция возвращает true, то целевая ячейка, идентифицированная с аргументами aCol и aRow, является фокусируемой (текущая реализация TCustomGrid просто возвращает true). TCustomDrawGrid и, следовательно, TDrawGrid и TStringGrid переопределяют этот метод, чтобы сначала проверить, \[является ли\] ячейка шире, чем 0; обычно вы не хотите, чтобы ячейка с нулевой шириной 0 выбиралась, \[другими словами, вы хотите,\] чтобы ячейка с этими характеристиками пропускалась автоматически в процессе поиска подходящей ячейки. Другая вещь, которую переопределяет метод SelectCell, - это вызов настраиваемое пользователем событие OnSelectCell: это событие принимает координаты ячейки как аргументы и всегда возвращает значение по умолчанию true.
555555
556556Как только \[становится\] известно, что ячейка \[является\] фокусируемой, и мы уверены, что движение будет иметь место, сначала вызывается метод BeforeMoveSelection; это по очереди вызывает событие OnBeforeSelection. Аргументами этого метода являются координаты для новой сфокусированной ячейки. В этот момент \[также будет\] скрыт любой видимый редактор. Слово "перед" означает, что выбор еще не изменен, и к текущим сфокусированным координатам можно получить доступ по свойствам grid.Col и grid.Row.
557557
558558После этого меняются координаты внутренней сфокусированной ячейки, а затем вызывается метод MoveSelection; цель этого метода заключается в том, чтобы инициировать событие OnSelection, если оно установлено (это уведомление, что сфокусированная ячейка к этому времени уже изменилась и координаты ячейки теперь доступны через свойства grid.row и grid.col).
559559
560560Обратите внимание, что неэффективно использовать событие OnSelectCell для обнаружения изменений фокуса ячейки, так как это событие будет срабатывать несколько раз даже для одной и той же ячейки в процессе поиска подходящей ячейки. Для этого лучше использовать события OnBeforeSelection или OnSelection.
561561
562562#### Различия с Delphi
563563
564564- Поведение SelectCell и OnSelectCell, вероятно, различны - в действительности не могу судить о различиях. В Lazarus они используются в таких функциях, как AutoAdvance, которая, насколько я знаю, не существует в Delphi.
565565
566566### Когда встроенных свойств недостаточно: наследники сетки
567567
568568Наследники сетки обычно должны переопределять следующие методы:\
569569DrawAllRows: Отрисовывать все видимые строки\
570570DrawRow: Отрисовывать все ячейки в строке.\
571571DrawRow отрисовывает все ячейки в строке, предварительно проверив, находится ли ячейка в области отсечения, и только \[потом\] рисует ячейку, если она есть.\
572572DrawCell:\
573573DrawCellGrid:\
574574DrawCellText:\
575575DrawFocusRect:\
576576(напишите мне).\
577577
578578### Что происходит в методе `TCustomGrid.Paint`?
579579
580580В следующем списке показан внутренний порядок вызовов методов для отрисовки `TCustomGrid` (или потомков). Каждый элемент в списках представляет вызовы методов во время операции отрисовки. Это должно помочь найти правильный точку для изменения поведения, когда дело доходит до \[написания\] наследников `TCustomGrid`.
581581
582582- **`DrawEdges`**: Рисует внешнюю границу сетки.
583583- **`DrawAllRows`** (virtual): Рисует все строки в видимой части сетки. Это единственный метод, вызываемый в процессе отрисовки, который объявлен *`virtual`*.
584584 - **`DrawRow`** (virtual): Вызывается для каждой строки внутри текущего окна просмотра.
585585 - **`DrawCell`** (virtual): Сначала вызывается для каждой "нормальной" (т.е. не фиксированной) ячейки в строке.
586586 - **`PrepareCanvas`** (virtual): Устанавливает стили рисования холста в соответствии с визуальными свойствами текущей ячейки.
587587 - **`DrawFillRect`**: Рисует фон ячейки со стилями, установленными в `PrepareCanvas`.
588588 - **`DrawCellGrid`** (virtual): Рисует линии границы ячейки.
589589 - **`DrawFocusRect`** (virtual): В `TCustomGrid` этот метод ничего не делает. В наследниках сетки этот метод используется для рисования прямоугольника фокуса внутри активной ячейки.
590590 - **`DrawCell`** (virtual): (см. выше) Вызывается для каждой фиксированной ячейки в видимом окне просмотра строки.
591591- **`DrawColRowMoving`**: активен только при перемещении столбца или строки. Этот метод рисует линию, которая указывает новую позицию строки/столбца.
592592- **`DrawBorder`**: Если необходимо (`Flat=TRUE` и `BorderStyle=bsSingle`), это отрисовывает внутреннюю линию границы.
593593
594594#### Дополнительные методы для рисования `TCustomGrid`
595595
596596Эти методы объявлены и (частично) реализованы в `TCustomGrid`, но они не вызываются непосредственно отсюда. Они используются классами потомков для отрисовки содержимого ячейки.
597597
598598- **`DrawCellText`** (virtual): Записывает/отрисовывает текст, который передается в качестве параметра в ячейку. Текст отформатирован с использованием стилей, которые активны в `Canvas.TextStyle` (см. также `PrepareCanvas`).
599599- **`DrawThemedCell`** (virtual): Используется только для фиксированных ячеек и если `TitleStyle=tsNative`. \[Метод\] отрисовывает фон ячейки, используя `ThemeServices`.
600600- **`DrawColumnText`** (virtual): Используется только для ячеек заголовков столбцов.
601601 - **`DrawColumnTitleImage`**: Если сетке сопоставлен `TitleImageList` и `Columns.Title[x].Title.ImageIndex` содержит допустимое значение, \[указанное в `ImageIndex`\] изображение отрисовывается внутри ячейки.
602602 - **`DrawCellText`** : (см. выше)
603603- **`DrawTextInCell`** (virtual): Используется для "нормальных" (т.е. не фиксированных) ячеек. В `TCustomGrid` этот метод ничего не делает. В наследуемых сетках этот метод используется для отрисовки содержимого ячеек. В `TStringGrid` метод вызывает `DrawCellText` (см. выше).
604604
605605#### Методы рисования, введенные `TCustomDrawGrid`
606606
607607Как вы можете видеть, базовый класс `TCustomGrid` не выводит никакого содержимого в ячейки. Это делается в **`TCustomDrawGrid`**. **`TCustomDrawGrid`** переопределяет метод `DrawCell` следующим образом:
608608
609609- **`DrawCell`** (override):
610610 - **`PrepareCanvas`**: вызывает унаследованный метод из **`TCustomGrid`**.
611611 - **`DefaultDrawCell`** (virtual): Этот метод вызывается только в том случае, если обработчик события для `OnDrawCell` НЕ назначен.
612612 - **`DrawFillRect / DrawThemedCell`** (von `TCustomGrid`): Рисует фон ячейки. Если `TitleStyle=tsNative`, фон фиксированных ячеек рисуется с помощью `DrawThemedCell`.
613613 - **`DrawCellAutonumbering`** (virtual): Если \[свойство\] `goFixedAutonumbering` \[включено\] в \[набор\] `Options`, номера строк отображаются в первом фиксированном столбце \[с помощью метода\] `TCustomGrid.DrawCellText`.
614614 - **`DrawColumnText`** (из `TCustomGrid`): Вызывается только для ячеек заголовков столбцов (см. «Дополнительные методы отрисовки в `TCustomGrid`»).
615615 - **`DrawTextInCell`** (из `TCustomGrid`): Вызывается только для "нормальных" (т.е. не фиксированных) ячеек (см. «Дополнительные методы отрисовки в `TCustomGrid`»).
616616 - **`DrawCellGrid`** (virtual): Рисует границы ячейки.
617617
618618### Сохранение и получение содержимого сетки
619619
620620Методы **SaveToFile** и **LoadFromFile** позволяют сетке сохранять и извлекать ее макет и данные в/из файла формата XML. TStringGrid, как унаследованный от TCustomStringGrid, также имеет возможность "экспортировать" и "импортировать" свой контент в файл формата Comma Separated Values, наиболее известный как CSV-файлы. Это описано в ссылке методов [[Grids Reference Page/ru|SaveToCSVFile]] и [[Grids Reference Page/ru|LoadFromCSVFile]].
621621
622622Тип информации, которая может быть сохранена, а затем извлечена при использовании **SaveToFile** и **LoadFromFile**, определяется свойством SaveOptions (тип TSaveOptions), которое представляет собой набор параметров, описанных ниже:
623623
624624`soDesign:     Сохраняет и загружает ColCount,RowCount,FixedCols,FixedRows,`\
625625`              ColWidths, RowHeights и Options (TCustomGrid)`\
626626`soPosition:   Сохраняет и загружает Scroll position, Row, Col и Selection (TCustomGrid)`\
627627`soAttributes:  Сохраняет и загружает Colors, Text Alignment и Layout, и т.д. (TCustomDrawGrid)`\
628628`soContent:    Сохраняет и загружает текст (TCustomStringGrid)`\
629629`soAll:        набор всех параметров (soAll:=[soDesign,soPosition,soAttributes,soContent];)`
630630
631631Не все параметры применимы ко всем типам сеток, например, soContent не применяется к наследникам сеток TCustomGrid, таким как TDrawGrid, поскольку такая сетка не имеет понятия "контент". TStringGrid - это специальный вид сетки, который "знает", как обрабатывать строки, и может использовать параметр soContent, если он указан.
632632
633633Опция soAttributes не используется в стандартных сетках Lazarus, \[она предназначена\] для наследников сеток.
634634
635635При использовании **LoadFromFile** сетка также использует свойство SaveOptions, чтобы знать, какая информация должна извлекаться из файла, поэтому вполне возможно указать SaveOptions:=\[soDesign,soContent\] при сохранении и только SaveOptions:=\[soContent\] при загрузке.
636636
637637Для TStringGrid значение SaveOptions по умолчанию - \[soContent\], для других видов сеток SaveOptions - это пустой набор.
638638
639639**На заметку:** Одна из распространенных проблем при сохранении и извлечении данных сетки возникает \[тогда\], когда пользователь определяет свойство SaveOptions перед \[методом\] **SaveToFile**, но не раньше \[вызова метода\] **LoadFromFile**. При использовании **LoadFromFile** через некоторое время после **SaveToFile**, свойство SaveOptions устанавливается должным образом, но если **LoadFromFile** выполняется при следующем запуске программы, свойство SaveOptions может настроиться неправильно, по этой причине рекомендуется всегда указывать свойство SaveOptions непосредственно перед \[вызовом метода\] **LoadFromFile** или делать это глобально при запуске программы, как в следующем примере:
640640
641641------------------------------------------------------------------------
642642
643643**Пример:**
644644
6456451. Сначала перейдите в меню "File -\> New -\> Application";
6466462. Бросьте пустой TStringGrid в форму;
6476473. Бросьте TButton и TOpenDialog на форму;
6486484. Добавьте событие OnCreate для формы;
6496495. Добавьте событие OnClick для кнопки.
650650
651651``` pascal
652652unit Unit1;
653653
654654{$mode objfpc}{$H+}
655655
656656interface
657657
658658uses
659659 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,
660660 Buttons, StdCtrls, XMLCfg;
661661
662662type
663663
664664 { TForm1 }
665665 TForm1 = class(TForm)
666666 StringGrid1: TStringGrid;
667667 Button1: TButton;
668668 OpenDialog1: TOpenDialog;
669669 procedure Button1Click(Sender: TObject);
670670 procedure Form1Create(Sender: TObject);
671671 private
672672 { private declarations }
673673 public
674674 { public declarations }
675675 end;
676676
677677var
678678 Form1: TForm1;
679679
680680implementation
681681
682682{ TForm1 }
683683
684684procedure TForm1.Form1Create(Sender: TObject);
685685begin
686686 //устанавливаем SaveOptions во время создания формы
687687 stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
688688end;
689689
690690procedure TForm1.Button1Click(Sender: TObject);
691691begin
692692 //Спрашиваем, был ли запущен метод Execute [компонента] OpenDialog
693693 //когда это происходит, пользователь выбирает файл XML для загрузки
694694 //имя, которое было сохранено в объявлении FileName.
695695
696696 if opendialog1.Execute then
697697 begin
698698 //Очищаем сетку
699699 StringGrid1.Clear;
700700 //Загружаем XML
701701 StringGrid1.LoadFromFile(OpenDialog1.FileName);
702702 //Обновляем сетку
703703 StringGrid1.Refresh;
704704 end;
705705end;
706706
707707initialization
708708 {$I unit1.lrs}
709709
710710end.
711711```
712712
713713------------------------------------------------------------------------
714714
715715**Образец xml-файла:** (Скопируйте текст ниже в текстовый файл. Не забудьте поставить заголовок xml :-))
716716
717717``` xml
718718<?xml version="1.0"?>
719719<CONFIG>
720720 <grid version="3">
721721 <saveoptions create="True" position="True" content="True"/>
722722 <design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20">
723723 <options>
724724 <goFixedVertLine value="True"/>
725725 <goFixedHorzLine value="True"/>
726726 <goVertLine value="True"/>
727727 <goHorzLine value="True"/>
728728 <goRangeSelect value="True"/>
729729 <goDrawFocusSelected value="False"/>
730730 <goRowSizing value="False"/>
731731 <goColSizing value="False"/>
732732 <goRowMoving value="False"/>
733733 <goColMoving value="False"/>
734734 <goEditing value="False"/>
735735 <goTabs value="False"/>
736736 <goRowSelect value="False"/>
737737 <goAlwaysShowEditor value="False"/>
738738 <goThumbTracking value="False"/>
739739 <goColSpanning value="False"/>
740740 <goRelaxedRowSelect value="False"/>
741741 <goDblClickAutoSize value="False"/>
742742 <goSmoothScroll value="True"/>
743743 </options>
744744 </design>
745745 <position topleftcol="1" topleftrow="1" col="1" row="1">
746746 <selection left="1" top="1" right="1" bottom="1"/>
747747 </position>
748748 <content>
749749 <cells cellcount="10">
750750 <cell1 column="0" row="0" text="Title Col1"/>
751751 <cell2 column="0" row="1" text="value(1.1)"/>
752752 <cell3 column="0" row="2" text="value(2.1)"/>
753753 <cell4 column="0" row="3" text="value(3.1)"/>
754754 <cell5 column="0" row="4" text="value(4.1)"/>
755755 <cell6 column="1" row="0" text="Title Col2"/>
756756 <cell7 column="1" row="1" text="value(1.2)"/>
757757 <cell8 column="1" row="2" text="value(2.2)"/>
758758 <cell9 column="1" row="3" text="value(3.2)"/>
759759 <cell10 column="1" row="4" text="value(4.2)"/>
760760 </cells>
761761 </content>
762762 </grid>
763763</CONFIG>
764764```
765765
766766### Редактор ячеек сетки
767767
768768В сетке используются редакторы ячеек для изменения содержимого ячеек.
769769
770770Для специализированной сетки, такой как TStringGrid, редактор является обычным однострочным текстовым контролом, но иногда желательно иметь другие средства для ввода информации. Например:
771771
7727721. показать диалоговое окно открытия файла, чтобы найти местоположение файла, чтобы пользователь не вводил полный путь вручную
7737732. если текст в ячейке представляет дату, \[вызвать\] всплывающий календарь, чтобы мы могли легко выбрать конкретную дату.
774774
775775Иногда информация, которую пользователь должен ввести в ячейку, ограничена определенным списком слов; в этом случае ввод информации напрямую может привести к ошибкам и, возможно, потребуется выполнить процедуру проверки. Мы можем избежать этого, используя редактор ячейки, который представляет пользователю список, содержащий только легальные значения.
776776
777777Это также относится к общим сеткам типа TDrawGrid, где пользователю нужна какая-то структура для хранения данных, которые будут отображаться в сетке. В этой ситуации информация, введенная в редакторе ячейки, обновляет внутреннюю структуру, чтобы отображать изменения в сетке.
778778
779779#### Встроенный редактор ячейки
780780
781781Модуль grids.pas уже включает некоторые из наиболее используемых редакторов ячеек, готовых для использования в сетках. Также возможно создавать новые редакторы ячеек (настраиваемые редакторы ячеек), если встроенные редакторы не подходят для конкретной задачи.
782782
783783Встроенные редакторы ячеек - это Button, Edit и Picklist.
784784
785785#### Использование редакторов ячейки
786786
787787Пользователи могут указать, какой редактор будет использоваться для ячейки, используя один из двух методов.
788788
7897891. **Using a custom column and selecting the ButtonStyle property of the column** (*использование настраиваемого столбца и выбор свойства ButtonStyle для столбца*). В этом методе пользователь может выбрать стиль редактора, который будет показан. Доступными значениями являются: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn.
7907902. **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), в которой объясняется, как правильно реализовывать пользовательский редактор). Это событие также позволяет настраивать редактор со значениями, специфичными для ячейки, строки или столбца (например, пользовательское всплывающее меню для редактора ячейки).
791791
792792Установка свойства *ButtonStyle* работает только в том случае, если столбец создан с помощью инструкции `StringGrid1.Columns.Add`. Использование выражения типа `StringGrid1.ColCount:=X;` вызовет исключение. Установка свойства ButtonStyle может быть выполнена с помощью подобного кода:
793793
794794``` pascal
795795if ColCB< StringGrid1.Columns.Count
796796 then StringGrid1.Columns.Items[ColCB].ButtonStyle:=cbsCheckboxColumn;
797797```
798798
799799#### Описание стилей редактора
800800
801801Ниже приводится описание стилей редактора. Они являются значениями перечисляемого типа TColumnButtonStyle и поэтому имеют префикс 'cbs'. Этот тип использовался, чтобы оставаться совместимым с DBGrid Delphi.
802802
803803- **cbsAuto**
804804
805805
806806Это стиль редактора по умолчанию для сеток-наследников TCustomGrid. Фактический класс редактора, который будет использоваться для редактирования содержимого ячейки, зависит от нескольких факторов. Для TCustomGrids он использует класс TStringCellEditor, полученный из TCustomMaskEdit. Этот редактор специализирован для редактирования строк одной строки. Затем он используется по умолчанию в TStringGrid и TDrawGrid. При использовании пользовательских столбцов, если программист заполнил свойство PickList столбца, \[редактор\] ведет себя так, как если бы был установлен стиль редактора cbsPickList. Для TCustomDBGrid, который имеет поле типа boolean, он ведет себя так, как если бы был указан стиль редактора cbsCheckBoxColumn. Это рекомендуемое значение для Custom Cell Editors. TODO: сослаться на OnEditingDone.
807807
808808- **cbsEllipsis**
809809
810810
811811Этот стиль редактора является наиболее общим. При \[его\] использовании в ячейке редактирования появляется кнопка, и программисты могут использовать событие сетки OnEditButtonClick, чтобы определить, когда пользователь нажал кнопку, и выполнить любое действие, запрограммированное для такой ячейки. Например, программист может использовать этот стиль редактора, чтобы открыть диалог календаря, чтобы пользователь мог легко выбрать конкретную дату. Другие возможности могут заключаться в том, чтобы показать диалог открытия файла для поиска файлов, калькулятор, чтобы пользователь мог ввести числовой результат вычислений и т.д.
812812
813813
814814OnEditButtonClick - это просто уведомление, чтобы узнать, в какой ячейке нажата кнопка, взглянув на свойства grid.Row и grid.Col.
815815
816816
817817DBGrid имеет определенные свойства для извлечения активного столбца или поля, и, поскольку это событие происходит в активной записи, оно может обновлять информацию в активном поле.
818818
819819
820820Этот стиль редактора реализован с использованием TButtonCellEditor, прямого потомка TButton.
821821
822822- **cbsNone**
823823
824824
825825Этот стиль редактора указывает, что сетка не должна использовать какой-либо редактор для конкретной ячейки или столбца; \[это поведение аналогично тому\], как если бы такая ячейка или столбец сетки были только для чтения.
826826
827827- **cbsPickList**
828828
829829
830830Используется для представления пользователю списка значений, которые могут быть введены. Этот стиль редактора реализован с использованием TPickListCellEditor, компонента, полученного из TCustomComboBox. Список отображаемых значений заполняется одним из двух способов в зависимости от метода, используемого для выбора стиля редактора.
831831
8328321. При использовании настраиваемых столбцов программисты могут вводить список значений, используя свойство PickList столбца. \[ДЛЯ НОВИЧКОВ: TODO: точная процедура для редактирования списка\]
8338332. В OnSelectEditor программисты получают экземпляр TPickListCellEditor, используя функцию EditorByStyle(cbsPickList). [[Grids Reference Page/ru|См. пример здесь]]
834834
835835Значение в сетке TStringGrid будет автоматически отображать выбранное значение. При необходимости программист может определить момент, когда значение выбрано, путем написания обработчика для события OnPickListSelect сетки, поэтому могут быть предприняты дополнительные шаги (например, для обработки нового значения). TODO: связать с OnEditingDone.
836836
837837- **cbsCheckboxColumn**
838838
839839
840840Это может быть полезно, когда содержимое данных, связанное с столбцом, ограничено парой значений, например да-нет, true-false, вкл-выкл, 1-0 и т.д. Вместо того, чтобы принуждать пользователя вводить значения для данных такого типа в StringCellEditor или выбирать один из списка, cbsCheckboxColumn используется для изменения данных столбца с помощью представления флажка, которое пользователь может переключать, используя щелчок мыши или нажимая клавишу SPACE (если столбец, содержащий этот флажок, а также StringGrid доступны для редактирования).
841841
842842Получение или установка значения флажка в ячейке осуществляется следующим образом:
843843
844844``` pascal
845845StringGrid1.Cell[x,y]:='Z';
846846```
847847
848848
849849где Z заменяется на **0** для *Unchecked*, **1** для *Checked* и пустую строку для *Grayed*. Обратите внимание, что любое значение (строка), отличное от *0* и *1*, будет отображаться как серый сheckbox.
850850
851851
852852Если для свойства столбцов ButtonStyle установлено значение cbsAuto, и DBGrid обнаруживает, что поле, связанное с столбцом, является логическим полем, то сетка автоматически использует этот стиль редактора. Этот автоматический выбор может быть отключен или активирован с использованием свойства OptionGxtra DBGrid; установка элемента dgeCheckboxColumn в false отключает эту функцию.
853853
854854
855855Значения, используемые для распознавания *checked* или *unchecked* состояний, задаются в свойствах столбца ValueChecked и ValueUnchecked \[соответственно\].
856856
857857
858858В любой момент значение поля может находиться в одном-трех состояниях: Unchecked, Checked или Grayed. Внутренне эти состояния идентифицируются следующими значениями типа TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked и gcbpGrayed.
859859
860860
861861Этот стиль редактора не использует реальные компоненты TCheckbox для обработки взаимодействия с пользователем: визуальное представление задается тремя встроенными растровыми изображениями, которые соответствуют возможным состояниям флажка. Использованные растровые изображения можно настроить, написав обработчик для события OnUserCheckboxBitmap \[компонента\] DBGrid; обработчик этого события получает состояние флажка в параметре CheckedState типа TDBGridCheckboxState и параметр растрового изображения, который программист может использовать для указания пользовательских растровых изображений.
862862
863863- **cbsButtonColumn**
864864
865865
866866Этот стиль редактора используется для отображения кнопки в каждой ячейке в столбце. Как и в случае cbsCheckboxColumn, этот редактор не использует реальные кнопки, внешний вид определяется текущей темой widgetset.
867867
868868
869869Пользователь знает, какая конкретная кнопка была нажата благодаря обработчику OnEditButtonClick сетки и проверке столбца и строки. Обратите внимание, что в этом конкретном случае столбец и строка сетки идентифицируют не текущую выбранную ячейку, а ячейку нажатой кнопки. Как только событие OnEditButtonClick будет обработано, и если пользователь не изменил столбец или строку в этом обработчике, сетка автоматически сбрасывает столбец и строку, чтобы отобразить выбранную ячейку. Пока обработчик OnEditButtonClick текущего выделения сетки доступен в grid.Selection, который является свойством TRect, левое и правое \[значения\] представляют \[собой\] индексы столбцов, а верхний и нижний - индексы строк.
870870
871871
872872Заголовок кнопки - соответствующая строка ячейки.
873873
874874### Редактирование сеток
875875
876876Эффект редактирования отличается в зависимости от того, какая сетка используется, например, TStringGrid сохраняет отредактированный текст внутри, а TDBGrid влияет на записи в наборе данных. TDrawGrid "не знает", что делать с отредактированным текстом: если программист не контролирует его, \[отредактированный текст\] просто отбрасывается.\
877877Для TDrawGrid (хотя это должно работать для всех классов сетки), чтобы получить доступ к отредактированному тексту, программист может использовать событие **OnSetEditText**, которое запускается каждый раз, когда пользователь что-то изменяет в редакторе, параметры *aCol* и *Row* этого события определяют редактируемую ячейку, параметр *value* содержит \[редактируемый\] текст - это рекомендуемый метод. Другой способ доступа к отредактированному тексту - прием его непосредственно из редактора после завершения процесса редактирования с использованием события **OnEditingDone**. Это можно сделать, обратившись к внутреннему редактору, используемому для редактирования, в данном случае, по умолчанию "String Cell Editor" \[("Редактору строк ячеек")\]. Для этого существует два метода: первый - с использованием события **OnSelectEditor**, где параметр *Editor* - это экземпляр, который будет использоваться для редактирования ячейки; вы должны сохранить этот экземпляр в переменной, например TheEditor (типа *TStringCellEditor*), для последующего использования в **OnEditingDone**. Второй вариант - использование метода **EditorByStyle** сетки, этот метод принимает "стиль редактора" в качестве параметра и возвращает экземпляр редактора, соответствующего этому стилю. В нашем случае, зная, что стиль cbsAuto возвращает редактор ячейки по умолчанию, в обработчике события **OnEditingDone** можно использовать непосредственно TheEditor:=Grid.EditorByStyle(cbsAuto). Затем вы можете получить текст с помощью *TheEditor.Text*. Если вы \[будете\] использовать этот метод, обратите внимание на то, что "великая сила несет большую ответственность".
878878
879879#### Параметры и свойства, которые влияют на редактирование
880880
881881Поведение редактирования контролируется рядом свойств и параметров, фактически любая настройка или параметры, которые влияют на редактирование, требуют редактирования сетки или такая настройка может быть проигнорирована.\
882882При запуске редактируемое состояние сетки различно \[в разных видах сетках\], поскольку редактирование TDrawGrid и TStringGrid отключено, для TDbGrid включено по умолчанию.\
883883Свойство **Options** имеет несколько элементов, которые касаются редактирования, они описаны ниже:\
884884***goEditing, dgEditing (в DbGrid)**. Эта опция изменяет редактируемое состояние сетки, ее можно изменить во время выполнения, поскольку она проверяет, когда начнется редактирование.
885885
886886- **goAlwaysShowEditor**. Обычно редактор скрыт и становится видимым только тогда, когда это необходимо. С помощью этой опции редактор будет отображаться все время, если сетка не редактируется, этот параметр игнорируется, и редактор всегда скрыт.
887887
888888### Хинты (подсказки) ячейки
889889
890890В дополнение к стандартным хинтам большинства элементов управления потомки TCustomGrid могут отображать специальную подсказку для ячейки, над которой курсирует мышь. Эта функция управляется параметрами сетки **goCellHints** и **goTruncCellHints** (или **dgCellHints** и **gdTruncCellHints** для DBGrid соответственно):
891891
892892- **goCellHints** оценивает событие **OnGetCellHint** для извлечения отображаемого текста для ячейки, указанной переданными параметрами. В случае TDrawGrid и TStringGrid задаются индексы столбцов и строк, тогда как в случае TDBGrid текст ячейки может быть извлечен из поля, назначенного указанному объекту столбца.
893893- **goTruncCellHints** отображает подсказку только в том случае, если текст не помещается \[полностью\] в ячейку и \[поэтому\] усечен; текст подсказки - это не обрезанный ячейкой текст. В Lazarus 1.9+ этот текст передается событию **OnGetCellHint** для дальнейшего уточнения (например, отображает весь текст memo в подсказке). В случае обрезания ячейкой текстов рекомендуется также активировать опцию **goCellEllipsis** (или **dgCellEllipsis** для DBGrid), которая добавляет «...» к обрезанному ячейкой тексту.
894894
895895Если сетка имеет значение в своем стандартном свойстве **Hint**, тогда свойство **HintPriority** можно использовать для управления тем, как все эти тексты подсказок могут быть объединены в одно всплывающее окно:
896896
897897- **chpAll** помещает стандартную подсказку в первую строку, подсказку, определяемую событием OnGetCellHint во вторую строку, и подсказку для усеченных ячеек в третью строку
898898- **chpAllNoDefault** отображает подсказки только ячейки, заданные событием OnGetCellHint, и усеченными ячейками.
899899- **chpTruncOnly** отображает подсказки только для усеченных ячеек; это настройки по умолчанию.
900900
901901В дополнение, стандартное свойство **ShowHint** сетки должно быть установлено в true, чтобы отображать подсказки на ячейки.
902902
903903## "Как сделать..." и Примеры
904904
905905### Фокусировка ячейки
906906
907907Фокусировка ячейки в TStringGrid очень проста. Обратите внимание, что подсчет начинается с нуля, а не 1. Таким образом, чтобы перевести фокус на строку 10, столбец 9, выполните:
908908
909909``` pascal
910910StringGrid1.row := 9;
911911StringGrid1.col := 8;
912912```
913913
914914### Пример: Как настроить собственный редактор ячеек.
915915
916916см. lazarus/examples/gridexamples/gridcelleditor/gridcelleditor.lpi (начиная с laz 1.2)
917917
918918### Пример: Как установить memo-редактор для dbgrids
919919
920920Разумеется, вы можете использовать другой контрол вместо 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))
921921
922922- Поместите элемент управления memo (или любой другой контрол, который вы хотите) на свою форму, установите любые свойства, которые вы хотите, и установите значение visible:= false. Этот \[контрол\] будет использоваться при редактировании ячейки сетки. В этом примере мы будем использовать GridCellMemo.
923923- В событии OnSelectEditor поместите следующий код - адаптируйте, если вы не хотите редактировать логический столбец 3, физический столбец 4:
924924
925925``` pascal
926926if (Column.DesignIndex = 3) then
927927 begin
928928 GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
929929 GridCellMemo.Text:=Column.Field.AsString;
930930 Editor := GridCellMemo;
931931 end;
932932```
933933
934934- Предположим, что ваш источник данных называется Datasource1, а DBGrid называется ResultsGrid. Тогда в событии OnEditingDone для GridCellMemo введите следующее:
935935
936936(В исходном сообщении форума используется событие OnChange, которое будет срабатывать при каждом изменении содержимого. Использование OnEditingDone запускается только после того, как пользователь закончил свои правки.)
937937
938938``` pascal
939939 if not(Datasource1.State in [dsEdit, dsInsert]) then Datasource1.Edit;
940940
941941 Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;
942942```
943943
944944**Осторожно** Использование обычного элемента управления в качестве настраиваемого редактора имеет множество недостатков: при изменении размеров столбцов или скроллинге сетки он не будет изменять размер или положение, вам придется позаботиться о том, чтобы изменить данные в сетке (как объяснялось выше), он не работает должным образом с DbGrids и препятствует нормальной навигации по сетке с помощью клавиатуры. Сетка взаимодействует с редактором, используя некоторые специальные сообщения сетки, поэтому лучше \[выделить этот контрол в\] подкласс для упорядочивания управления этими сообщениями, как описано в пятой главе [этого документа](http://www.freepascal.org/~michael/articles/grids/grids.pdf).
945945
946946**Осторожно** Если у вас более одной сетки в одной и той же форме, используйте разные настраиваемые редакторы для каждой из них: если вы \[будете\] использовать один и тот же элемент управления, начнут происходить плохие вещи с того момента, когда обе сетки попытаются использовать его одновременно.
947947
948948### Пример: как добавить кнопку редактора
949949
950950``` pascal
951951// Условно показываем кнопку редактора в столбцах индекс 1 или 2, если
952952// ячейка в столбце index 1 пуста
953953procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer;
954954 var Editor: TWinControl);
955955begin
956956 if (aCol = 1) or (aCol = 2) then
957957 if StringGrid1.Cells[1,aRow] = '' then
958958 begin
959959 Editor := StringGrid1.EditorByStyle(cbsEllipsis);
960960 end;
961961 end;
962962
963963// Запуск действия ...
964964procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
965965begin
966966 if StringGrid1.Col = 1 then Showmessage('щелкнут редактор столбца 1');
967967 if StringGrid1.Col = 2 then Showmessage('щелкнут редактор столбца 2');
968968end;
969969```
970970
971971### Пример: Запрет отображения текстовых полей как "(Memo)" для DBGrids
972972
973973Когда вы используете SQL-выражения, такие как `"Select * from A"`, вы можете увидеть "(Memo)" вместо своего содержимого в ячейке сетки. Есть много способов исправить это, но, может быть, самым простым является изменение вашего выражения `"Select * from A"` на `"Select Cast(Column as TEXT) as Column from A"`. Это обычная проблема при использовании компонента DBGrid.
974974
975975``` pascal
976976 var
977977 sql : UTF8String;
978978 Query : TSQLQuery;
979979.....
980980 sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
981981 Query.SQL.Text:=sql;
982982```
983983
984984### Пример: Работа с Picklist, Как сделать его только для чтения и Как заполнить его в runtime.
985985
986986Используя событие сетки OnSelectEditor, вы можете настроить, как поведет себя редактор PickList (см. стиль кнопки cbsPickList). В следующем примере редактор списка выбора из столбца 1 изменяется на нечетных строках таким образом, что пользователь может вводить значения, набирая \[их с клавиатуры\], а на четных строках \[вводимые\] значения ограничиваются теми, которые содержатся в его списке. Кроме того, в этом примере показано, как заполнить список разными значениями в зависимости от обрабатываемой строки.
987987
988988``` pascal
989989procedure TForm1.gridSelectEditor(Sender: TObject; aCol, aRow: Integer;
990990 var Editor: TWinControl);
991991begin
992992 if aCol=1 then begin
993993 if (Editor is TCustomComboBox) then
994994 with Editor as TCustomComboBox do begin
995995 if (aRow mod 2=0) then
996996 Style := csDropDown
997997 else
998998 Style := csDropDownList;
999999 case aRow of
10001000 1:
10011001 Items.CommaText := 'ONE,TWO,THREE,FOUR';
10021002 2:
10031003 Items.CommaText := 'A,B,C,D,E';
10041004 3:
10051005 Items.CommaText := 'MX,ES,NL,UK';
10061006 4:
10071007 Items.CommaText := 'RED,GREEN,BLUE,YELLOW';
10081008 end;
10091009 end;
10101010 end;
10111011end;
10121012```
10131013
10141014### Выравнивание текста в StringGrids
10151015
10161016Этот код показывает, как использовать разное выравнивание текста в столбцах 2 и 3.
10171017
10181018``` pascal
10191019procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
10201020 aState: TGridDrawState);
10211021var
10221022 MyTextStyle: TTextStyle;
10231023begin
10241024 if (aCol=2) or (aCol=3) then
10251025 begin
10261026 MyTextStyle := StringGrid1.Canvas.TextStyle;
10271027 if aCol=2 then
10281028 MyTextStyle.Alignment := taRightJustify
10291029 else
10301030 if aCol=3 then
10311031 MyTextStyle.Alignment := taCenter;
10321032 StringGrid1.Canvas.TextStyle := MyTextStyle;
10331033 end;
10341034end;
10351035```
10361036
10371037### Многострочность в Grids, DBGrid
10381038
10391039В этом примере показано, как сделать многострочный текст в ячейке \[3,2\]. Он работает так же для DBGrid, где \[событие\] OnPrepareCanvas имеет параметры для работы с столбцами, а оттуда и с TFields.
10401040
10411041``` pascal
10421042procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
10431043 aState: TGridDrawState);
10441044var
10451045 MyTextStyle: TTextStyle;
10461046begin
10471047 if (aRow=2) or (aCol=3) then
10481048 begin
10491049 MyTextStyle := StringGrid1.Canvas.TextStyle;
10501050 MyTextStyle.SingleLine := false;
10511051 StringGrid1.Canvas.TextStyle := MyTextStyle;
10521052 end;
10531053end;
10541054```
10551055
10561056### Проверка введенных значений
10571057
10581058Lazarus версии 0.9.29 для StringGrid ввел событие OnValidateEntry типа TValidateEntryEvent, которое имеет следующее объявление:
10591059
10601060``` pascal
10611061TValidateEntryEvent =
10621062 procedure(sender: TObject; aCol, aRow: Integer;
10631063 const OldValue: string; var NewValue: string) of object;
10641064```
10651065
10661066где
10671067
10681068`aCol,aRow являются координатами проверяемой ячейки.`\
10691069`OldValue это значение, которое было в ячейках [aCol, aRow] до начала редактирования.`\
10701070`NewValue это значение, которое будет окончательно вставлено в ячейки [aCol, aRow].`
10711071
10721072Из-за того, что StringGrid работает, устанавливая значение ячейки во время редактирования пользователем (см. событие OnSetEditText и метод SetEditText сетки), то когда событие OnValidateEntry запускается, ячейка уже содержит введенное значение (корректное или нет); используя аргументы события OldValue и NewValue, значение ячейки может быть проверено и изменено, если необходимо.
10731073
10741074Обычно проверка выполняется, когда пользователь перемещается \[с редактируемой ячейки\] в другую. Если проверка закончилась неудачей, желательно сохранить редактор ячейки видимым/сфокусированным, чтобы введенное значение могло быть исправлено пользователем. Чтобы сетка "узнала", что проверка не прошла, необходимо создать исключение. Сетка будет обрабатывать исключение из Application.HandleException, и любое перемещение отменится.
10751075
10761076Например, предположим, что ячейка \[1,1\] должна содержать только значения 'A' или 'B', \[тогда\] валидация может быть выполнена с помощью:
10771077
10781078``` pascal
10791079procedure TForm1.GridValidateEntry(sender: TObject; aCol,
10801080 aRow: Integer; const OldValue: string; var NewValue: String);
10811081begin
10821082 if (aCol=1) and (aRow=1) then begin
10831083 if grid.Cells[aCol,aRow]<>'A') and grid.Cells[aCol,aRow]<>'B') then begin
10841084 // устанавливаем новое допустимое значение, чтобы пользователь мог просто нажать RETURN для продолжения, например
10851085 NewValue := 'A';
10861086 // другой вариант - вернуться предыдущему значению ячейки (которое считается допустимым)
10871087 // NewValue := OldValue;
10881088 // Используйте EAbort, а не другой тип исключения, чтобы избежать [возникновения] ложных
10891089 // диалоговых окон ошибок и [диалоговых окон] обратной отладки
10901090 raise EAbort.Create('Разрешены только A или B');
10911091 end else begin
10921092 // если исключение не создано, предполагается, что значение действительно, но при необходимости
10931093 // окончательное значение все равно можно изменить, заполнив NewValue другим значением
10941094
10951095 // компьютер знает лучше :)
10961096 if grid.Cells[1,1]='A' then
10971097 NewValue := 'B'
10981098 else
10991099 NewValue := 'A';
11001100 end;
11011101 end;
11021102end;
11031103```
11041104
11051105### Сортировка столбцов или строк
11061106
11071107Свойство ColumnClickSorts позволяет сортировать сетку автоматически, когда пользователь щелкает по заголовку столбца. Многократный щелчок по тому же столбцу переключает порядок сортировки. По умолчанию отображаются значки сортировки, показывающие, какой столбец был нажат.
11081108
11091109В коде вы можете использовать метод SortColRow().
11101110
11111111Первый параметр IsColumn - это логическое значение, которое указывает:
11121112
11131113- True если будет отсортирован столбец
11141114- False если будет отсортирована строка
11151115
11161116Второй параметр, index, является целым значением:
11171117
11181118- Index указывает колонку или строку для сортировки.
11191119
11201120Последние два параметра являются необязательными, \[они\] определяют поддиапазон строк или столбцов для сортировки.
11211121
11221122- FromIndex (От столбца или строки)
11231123- ToIndex (До столбца или строки)
11241124
11251125Если последние два параметра не указаны, сортируется весь столбец или строка.
11261126
11271127Сортировка использует алгоритм QuickSort, его можно изменить, если сетка-наследник переопределяет метод sort() и вызывает doCompareCells для сравнения с ячейкой.
11281128
11291129По умолчанию \[метод SortColRow()\] сортирует содержимое ячейки как строки в порядке возрастания или убывания, которое можно выбрать с помощью свойства SortOrder. По умолчанию он использует порядок \[сортировки\] по возрастанию.
11301130
11311131``` pascal
11321132// Сортировка столбца 3 в порядке возрастания
11331133grid.SortColRow(true, 3);
11341134
11351135// Сортировка столбца 3 в порядке убывания, пропустить фиксированные строки сверху
11361136grid.SortOrder := soDescending; // или soAscending
11371137grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);
11381138```
11391139
11401140Для пользовательской сортировки чисел, дат, состояний и т.д.: у StringGrid есть событие OnCompareCells, с которым пользователи могут обращаться, например, следующим образом:
11411141
11421142``` pascal
11431143procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol, BRow: Integer; var Result: integer);
11441144begin
11451145 // Результат будет либо <0, = 0, либо >0 для нормального порядка.
11461146 result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
11471147 // Для обратного порядка просто [присвойте] негативный результат (напр., на основе SortOrder сетки).
11481148 if StringGrid1.SortOrder = soDescending then
11491149 result := -result;
11501150end;
11511151```
11521152
11531153Вы также можете использовать OnCompareCells, когда автоматическая сортировка столбцов включена через свойство ColumnClickSorts.
11541154
11551155### Переопределить автоматическую сортировку (ColumnClickSorts установлен в False). Исключить сортировку некоторых столбцов. Показать стрелки сортировки в заголовке столбца
11561156
11571157(ColumnClickSorts, установленный в False, НЕ будет показывать стрелки сортировки в заголовке столбца, поэтому мы должны установить стрелки.)
11581158
11591159``` pascal
11601160procedure TForm1.StringGrid1HeaderClick(Sender: TObject; IsColumn: Boolean;
11611161 Index: Integer);
11621162begin
11631163 if isColumn then with StringGrid1 do
11641164 begin
11651165 col:=Index; // Задаем столбец для сортировки
11661166 if Tag = 0 then Tag :=-1 // Предположим, что данные в столбце возрастают
11671167 else Tag := Tag * -1; // Используем StringGrid1.Tag для хранения порядка сортировки
11681168 // Первый щелчок заголовка сортирует по убыванию
11691169
11701170 // Активируем CompareCells (активируем сортировку)
11711171 case Index of
11721172 0: col:=-1; // Не сортируем столбец 0 и не показываем стрелку сортировки
11731173 1: SortColRow(True, Index); // Сортировка будет происходить в CompareCells, как только будет выполнен оператор
11741174 2: SortColRow(True, Index);
11751175 3: SortColRow(True, Index);
11761176 4: SortColRow(True, Index);
11771177 5: col:=-1; // Не сортируем столбец 5 и не показываем стрелку сортировки
11781178 end;
11791179 end;
11801180end;
11811181
11821182// Compare возвращает одно из следующих значений:
11831183// if A<B return: -1 ( Восходящий )
11841184// if A=B return: 0
11851185// if A>B return: 1 ( Нисходящий )
11861186
11871187// Как сортировать: Text, Integer, Float, DateTime:
11881188
11891189procedure TForm1.StringGrid1CompareCells(Sender: TObject; ACol, ARow, BCol,
11901190 BRow: Integer; var Result: integer);
11911191
11921192 function CompareTextAsText: Integer; // Text
11931193 begin
11941194 CompareText(Cells[ACol,ARow], Cells[BCol,BRow]);
11951195 end;
11961196
11971197 function CompareTextAsInteger: Integer;
11981198 var i1,i2:Integer;
11991199 begin
12001200 if TryStrToInt(StringGrid1.Cells[ACol,ARow],i1) and // Integer
12011201 TryStrToInt(StringGrid1.Cells[BCol,BRow],i2) then
12021202 Result := CompareValue(i1, i2)
12031203 else Exit(0);
12041204 end;
12051205
12061206 function CompareTextAsFloat: Integer; // Float
12071207 var f1,f2:Extended;
12081208 begin
12091209 if TryStrToFloat(StringGrid1.Cells[ACol,ARow],f1) and
12101210 TryStrToFloat(StringGrid1.Cells[BCol,BRow],f2) then
12111211 Result := CompareValue(f1, f2)
12121212 else Exit(0);
12131213 end;
12141214
12151215 function CompareTextAsDateTime: Integer; // DateTime
12161216 var
12171217 D1, D2 : TDateTime;
12181218 begin
12191219 if Trim(StringGrid1.Cells[ACol,ARow]) <> Trim(StringGrid1.Cells[BCol,BRow]) then
12201220 begin
12211221 if trim(StringGrid1.Cells[ACol,ARow]) = '' then Exit(1);
12221222 if Trim(StringGrid1.Cells[BCol,BRow]) = '' then Exit(-1);
12231223 end
12241224 else Exit(0);
12251225
12261226 if TryStrToDate(StringGrid1.Cells[ACol,ARow], D1, 'yy/mm/dd') and
12271227 TryStrToDate(StringGrid1.Cells[BCol,BRow], D2, 'yy/mm/dd') then
12281228 Result := CompareDateTime(D1, D2)
12291229 else Exit(0);
12301230 end;
12311231
12321232begin // Не делайте здесь ничего другого, сортировка может быть выполнена некорректно
12331233 With StringGrid1 do
12341234 begin
12351235 case ACol of
12361236 0 : ; // Не сортируйте столбец 0
12371237 1 : Result := CompareTextAsText; // Text
12381238 2 : Result := CompareTextAsInteger; // Integer
12391239 3 : Result := CompareTextAsFloat; // Float
12401240 4 : Result := CompareTextAsDateTime; // DateTime
12411241 end; {Case}
12421242 end;
12431243 if StringGrid1.Tag = -1 then Result := -Result; // Переключаем порядок сортировки
12441244end;
12451245
12461246// DrawCell сделает трюк, предыдущие изображения стрелки будут удалены:
12471247procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
12481248 aRect: TRect; aState: TGridDrawState);
12491249
12501250 procedure DrawArrow(ImgIndex:Integer); // Нужны стрелки вверх/вниз в ImageList
12511251 begin
12521252 with StringGrid1 do
12531253 TitleImageList.Draw(Canvas, // TitleImageList из StringGrid
12541254 ARect.Right - TitleImageList.Width,
12551255 (ARect.Bottom - ARect.Top - TitleImageList.Height) div 2 + 2, // Положение стрелки, может быть отрегулировано
12561256 ImgIndex );
12571257 end;
12581258begin
12591259 if Col > 0 then // В этом демо нет стрелок в col 0
12601260 if (ARow = 0) and // Предположим, строка 0 в качестве заголовка строк
12611261 Assigned(TitleImageList) then // Нужны стрелки вверх/вниз в ImageList
12621262 case Columns.Items[aCol].Tag of // Использовать тег столбца как индекс Восходящей/Нисходящей стрелки
12631263 -1: DrawArrow(1); // Заменяем Index указателем стрелки вниз
12641264 0: DrawArrow(-1); // Удаляем предыдущее изображение
12651265 1: DrawArrow(2); // Заменяем Index указателем стрелки вверх
12661266 end;
12671267end;
12681268```
12691269
12701270### Сортировка столбцов или строк в DBGrid со стрелками сортировки в заголовке столбца
12711271
12721272Вот пример, который будет сортировать **DBgrid**, используя событие **OnTitleClick** и **TSQLQuery** и индексы. Это также должно работать для любого совместимого набора данных, такого как **TbufDataset**. Функция использует свойство **column.tag** для хранения состояния сортировки для каждого столбца, на который вы нажимаете, поэтому, когда вы вернетесь к тому \[столбцу\], который вы уже отсортировали, \[заголовок столбца\] отобразит правильную стрелку сортировки для отображения.
12731273
12741274**Предпосылки:**
12751275
12761276- Для хранения \[изображений\] стрелок вверх/вниз вам понадобится imagelist. Присвойте свой imagelist свойству **TitleImageList** dbgrid'а.
12771277- Убедитесь, что **[[doc:fcl/sqldb/tsqlquery.maxindexescount.html|TSQLQuery.MaxIndexesCount]]** достаточно велик, чтобы хранить новые индексы, которые вы будете создавать. Для этого примера я установил значение 100.
12781278- Вам также понадобится переменная-поле \[(в разделе интерфейса private)\] для хранения последнего столбца, который используется для сортировки, для этого примера я назвал его **FLastColumn**.
12791279
12801280В этом примере используемый **TSQLQuery** называется OpenQuery.
12811281
12821282> **Note:** По состоянию на 21 марта 2013 года **TSQLQuery** не имеет возможности очищать индексы, но в качестве обходного пути вы можете установить \[значение его свойства\] **unidirectional** в true, а затем в false: это очистит индексы.
12831283
12841284Чтобы повторно использовать компонент **TSQLQuery** с другим оператором SQL, индексы должны быть очищены после сортировки, \[иначе\] при открытии **TSQLQuery** с помощью другого оператора SQL будет вызвано исключение.
12851285
12861286``` pascal
12871287FLastColumn: TColumn; //сохраняем последний столбец сетки, который мы отсортировали
12881288
12891289procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
12901290const
12911291 ImageArrowUp=0; //должен соответствовать изображению в imagelist
12921292 ImageArrowDown=1; //должен соответствовать изображению в imagelist
12931293var
12941294 ASC_IndexName, DESC_IndexName:string;
12951295 procedure UpdateIndexes;
12961296 begin
12971297 // Обеспечиваем обновление индексов
12981298 OpenQuery.IndexDefs.Updated:=false; {<<<--Эта строка - решающая. IndexDefs.Update не будет
12991299 обновляться, если уже true, что произойдет в первом отсортированном столбце.}
13001300 Openquery.IndexDefs.Update;
13011301 end;
13021302begin
13031303 ASC_IndexName:='ASC_'+Column.FieldName;
13041304 DESC_IndexName:='DESC_'+Column.FieldName;
13051305 // индексы не могут сортировать двоичные типы, такие как ftMemo, ftBLOB
13061306 if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
13071307 exit;
13081308 // проверяем, существует ли восходящий индекс для этого столбца
13091309 // если нет, создадим [его]
13101310 if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
13111311 begin
13121312 OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
13131313 UpdateIndexes; //обеспечиваем обновление индексов
13141314 end;
13151315 // проверяем, существует ли нисходящий индекс для этого столбца
13161316 // если нет, создадим [его]
13171317 if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
13181318 begin
13191319 OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
13201320 UpdateIndexes; //обеспечиваем обновление индексов
13211321 end;
13221322
13231323 // Используем тег столбца для переключения ASC/DESC
13241324 column.tag := not column.tag;
13251325 if boolean(column.tag) then
13261326 begin
13271327 Column.Title.ImageIndex:=ImageArrowUp;
13281328 Openquery.IndexName:=ASC_IndexName;
13291329 end
13301330 else
13311331 begin
13321332 Column.Title.ImageIndex:=ImageArrowDown;
13331333 OpenQuery.IndexName:=DESC_IndexName;
13341334 end;
13351335 // Удаляем стрелку сортировки из предыдущего столбца, который мы отсортировали
13361336 if (FLastColumn <> nil) and (FlastColumn <> Column) then
13371337 FLastColumn.Title.ImageIndex:=-1;
13381338 FLastColumn:=column;
13391339end;
13401340```
13411341
13421342### Выбор записей в DBGrid с использованием checkbox'ов
13431343
13441344Цель состоит в том, чтобы иметь возможность выбирать произвольные записи в dbgrid с помощью checkbox'ов, сетка имеет возможность показывать checkbox'ы автоматически, когда обнаруживает наличие логических полей, а для других типов полей пользователь может вручную \[в свойстве\] ButtonStyle столбца выбрать \[значение\] cbsCheckboxColumn. Для таких столбцов пользователь просто помечает checkbox, и содержимое поля изменяется соответствующим образом.
13451345
13461346Но что произойдет, если в нашем наборе данных нет такого доступного поля? или мы не хотим, чтобы сетка входила в состояние редактирования при изменении состояния checkbox'а?. Добавление столбца без полей с помощью ButtonStyle=cbsCheckboxColumn покажет все checkbox'ы с неопределенным значением и отключенными, потому что нет поля, связанного с этим столбцом, и поэтому ничего не нужно изменять. Поскольку мы хотим самостоятельно обрабатывать состояние checkbox'а, нам нужно где-то сохранить состояние для каждой записи. Для этого мы можем использовать класс TBookmarklist (определенный в модуле dbgrids.pas), где свойство CurrentRowSelected может определить, выбрана ли текущая запись или нет. Используя события dbgrid'а OnCellClick и OnUserCheckboxState, мы можем отслеживать состояние checkbox'а.
13471347
13481348Обратите внимание, что этой методике нужен Lazarus r31148 или более поздней версии, которая реализует событие OnUserCheckboxState.
13491349
13501350``` pascal
13511351...
13521352uses ..., dbgrids, stdctrls, ...
13531353
13541354type
13551355
13561356 { TForm1 }
13571357
13581358 TForm1 = class(TForm)
13591359 ...
13601360 procedure DBGrid1CellClick(Column: TColumn);
13611361 procedure DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
13621362 procedure FormCreate(Sender: TObject);
13631363 procedure FormDestroy(Sender: TObject);
13641364 ...
13651365 private
13661366 RecList: TBookmarklist;
13671367 ...
13681368 end;
13691369
13701370procedure TForm1.DBGrid1CellClick(Column: TColumn);
13711371begin
13721372 if Column.Index=1 then
13731373 RecList.CurrentRowSelected := not RecList.CurrentRowSelected;
13741374end;
13751375
13761376procedure TForm1.FormCreate(Sender: TObject);
13771377begin
13781378 RecList := TBookmarkList.Create(DbGrid1);
13791379end;
13801380
13811381procedure TForm1.FormDestroy(Sender: TObject);
13821382begin
13831383 RecList.Free;
13841384end;
13851385
13861386procedure TForm1.DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
13871387begin
13881388 if RecList.CurrentRowSelected then
13891389 AState := cbChecked
13901390 else
13911391 AState := cbUnchecked;
13921392end;
13931393```
13941394
13951395### Подсветка выделенного столбца и строки ячейки
13961396
13971397В версии Lazarus 40276 добавлена опция (**gdRowHighlight**), она работает аналогично **goRowSelect**, но использует более светлый тон для выделения, а прямоугольник фокуса - только выбранная ячейка, а не в целая строка. Это отлично работает для строк, но как насчет столбцов?
13981398
13991399В этом разделе показан способ выделения столбцов, строк или и того, и другого (пример, который выделяет только столбцы и заголовки строк, можно найти в lazarus/examples/gridexamples/spreadsheet, этот способ действительно является расширением этого примера). Он использует два события сетки: **OnBeforeSelection** и **OnPrepareCanvas**, рисунок не нужен.
14001400
14011401Событие **OnBeforeSelection** запускается, когда начинается смена выделения, в этом случае мы можем узнать, какая ячейка выбрана в данный момент и какая ячейка будет выбрана следующей. Мы используем эту информацию для перерисовки всей строки или столбца как старой, так и новой ячеек. Когда начнется следующий цикл отрисовки, сетке будет предложено перерисовать ячейки, принадлежащие недействительным областям. Одним из первых шагов для перерисовки является вызов события **OnPrepareCanvas** (если он существует) для настройки свойств холста по умолчанию. Мы используем это событие для настройки выделенной строки или столбца:
14021402
14031403``` pascal
14041404procedure TForm1.gridBeforeSelection(Sender: TObject; aCol, aRow: Integer);
14051405begin
14061406 // мы можем здесь принять решение, если хотим выделить столбцы, строки или и те, и другие
14071407 // в этом примере мы выделяем и те, и другие
14081408 if Grid.Col<>aCol then
14091409 begin
14101410 // обнаружено изменение текущего столбца
14111411 grid.InvalidateCol(aCol); // перерисовываем новый выбранный столбец ячейки
14121412 grid.InvalidateCol(grid.Col); // перерисовываем текущий (это будет 'старый') выбранный столбец
14131413 end;
14141414 if Grid.Row<>aRow then
14151415 begin
14161416 grid.InvalidateRow(aRow); // перерисовываем новую выбранную строку ячейки
14171417 grid.InvalidateRow(grid.Row); // перерисовываем текущую (это будет 'старая') выбранную строку
14181418 end;
14191419end;
14201420
14211421procedure TForm1.gridPrepareCanvas(sender: TObject; aCol, aRow: Integer;
14221422 aState: TGridDrawState);
14231423begin
14241424 if gdFixed in aState then
14251425 begin
14261426 if (aCol=grid.Col) or (aRow=grid.Row) then
14271427 grid.Canvas.Brush.Color := clInactiveCaption; // это подсветило бы также заголовки столбцов или строк
14281428 end else
14291429 if gdFocused in aState then begin
14301430 // мы оставляем в покое текущую выделенную/сфокусированную ячейку
14311431 end else
14321432 if (aCol=Grid.Col) or (aRow=grid.Row) then
14331433 grid.Canvas.Brush.Color := clSkyBlue; // подсветка строк и столбцов цветом clSkyBlue
14341434end;
14351435```
14361436
14371437## DBGrids "Как сделать..." и Примеры
14381438
14391439#### Пример: как установить редактор memo для dbgrids
14401440
14411441Конечно, вы можете использовать другой элемент управления вместо 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))
14421442
14431443- Поместите элемент управления memo (или любой другой элемент управления) на форму, установите любые свойства, которые вы хотите, и установите видимым значение false. Он будет использоваться при редактировании ячейки сетки. Мы будем использовать GridCellMemo в этом примере.
14441444- В событии OnSelectEditor вставьте следующий код - адаптируйте, если вы не хотите редактировать логический столбец 3, физический столбец 4:
14451445
14461446``` pascal
14471447 if (Column.DesignIndex = 3) then
14481448 begin
14491449 GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
14501450 GridCellMemo.Text:=Column.Field.AsString;
14511451 Editor := GridCellMemo;
14521452 end;
14531453```
14541454
14551455- Предположим, ваш источник данных называется Datasource1, а DBGrid называется ResultsGrid. Затем в событие OnEditingDone для GridCellMemo поместите следующее:
14561456
14571457(В исходном сообщении на форуме использовалось событие OnChange, которое будет запускаться каждый раз при изменении содержимого. Использование OnEditingDone срабатывает только после того, как пользователь завершит редактирование.)
14581458
14591459``` pascal
14601460 if not(Datasource1.State in [dsEdit, dsInsert]) then Datasource1.Edit;
14611461
14621462 Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;
14631463```
14641464
14651465**Осторожно** Использование обычного элемента управления в качестве пользовательского редактора имеет много недостатков: он не будет изменять размеры или перемещать себя при изменении размера столбцов или прокрутке сетки, вы должны позаботиться о модификации данных в сетке. (как объяснено выше), он не работает должным образом с DbGrids и мешает нормальной навигации по сетке с помощью клавиатуры. Сетка взаимодействует с редактором с помощью некоторых специальных сообщений сетки, поэтому для управления этими сообщениями лучше выделить подкласс элемента управления, как объясняется в пятой главе [этого документа](http://www.freepascal.org/~michael/articles/grids/grids.pdf).
14661466
14671467**Осторожно** Если у вас несколько сеток в одной форме, используйте для каждой свой собственный редактор: если вы используете один и тот же элемент управления, могут произойти плохие вещи, поскольку обе сетки попытаются использовать его одновременно.
14681468
14691469### Пример: исключение отображения текстовых полей как «(Memo)» для DBGrids
14701470
14711471Когда вы используете операторы SQL, такие как `"Select * from A"`, вы можете увидеть «(Memo)» вместо текстового содержимого в ячейке сетки. Есть много способов исправить это, но, возможно, самый простой - изменить выражение `"Select * from A"` на `Select Cast(Column as TEXT) as Column from A`. Это распространенная проблема при использовании компонента DBGrid.
14721472
14731473``` pascal
14741474 var
14751475 sql : UTF8String;
14761476 Query : TSQLQuery;
14771477.....
14781478 sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
14791479 Query.SQL.Text:=sql;
14801480```
14811481
14821482### Сортировка столбцов или строк в DBGrid со стрелками сортировки в заголовке столбца
14831483
14841484Вот пример, который будет сортировать **DBgrid**, используя событие **OnTitleClick** и **TSQLQuery** и индексы. Это также должно работать для любого совместимого набора данных, такого как **TbufDataset**. Функция использует свойство **column.tag** для хранения состояния сортировки для каждого столбца, по которому вы щелкнули, поэтому, когда вы вернетесь к тому, который уже отсортирован, она выберет правильную стрелку сортировки для отображения.
14851485
14861486**Предварительные условия:**
14871487
14881488- Вам понадобится список изображений для хранения стрелок вверх/вниз. Присвойте свой список изображений свойству dbgrid **TitleImageList**.
14891489- Убедитесь, что свойство **[[doc:fcl/sqldb/tsqlquery.maxindexescount.html|TSQLQuery.MaxIndexesCount]]** достаточно велико для хранения новых индексов, которые вы будете создавать. Для этого примера у меня было установлено значение 100.
14901490- Вам также понадобится поле-переменная в разделе private класса для хранения последнего столбца, использованного для сортировки, для этого примера я назвал ее **FLastColumn**.
14911491
14921492В этом примере используемый **TSQLQuery** назван OpenQuery.
14931493
14941494> **Note:** по состоянию на 21 марта 2013 года **TSQLQuery** не имеет возможности очищать индексы, но в качестве обходного пути вы можете установить свойство **unidirectional** в значение true, а затем установить его в значение false, и это очистит индексы.
14951495
14961496Чтобы повторно использовать компонент **TSQLQuery** с другим оператором SQL, индексы должны быть очищены после сортировки, иначе будет возбуждено исключение при открытии **TSQLQuery** с другим оператором SQL.
14971497
14981498``` pascal
14991499FLastColumn: TColumn; //сохраняем последний столбец сетки, по которому мы делали сортировку
15001500
15011501procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
15021502const
15031503 ImageArrowUp=0; //должно соответствовать изображению в imagelist
15041504 ImageArrowDown=1; //должно соответствовать изображению в imagelist
15051505var
15061506 ASC_IndexName, DESC_IndexName:string;
15071507 procedure UpdateIndexes;
15081508 begin
15091509 // Убедитесь, что индексы обновлены
15101510 OpenQuery.IndexDefs.Updated:=false; {<<<--Эта строчка имеет решающее значение. IndexDefs.Update не будет обновляться, если уже имеет значение true, что произойдет в первом отсортированном столбце.}
15111511 Openquery.IndexDefs.Update;
15121512 end;
15131513begin
15141514 ASC_IndexName:='ASC_'+Column.FieldName;
15151515 DESC_IndexName:='DESC_'+Column.FieldName;
15161516 // индексы не могут сортировать двоичные типы, такие как ftMemo, ftBLOB
15171517 if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
15181518 exit;
15191519 // проверяем, существует ли восходящий индекс для этого столбца.
15201520 // если нет, создаем
15211521 if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
15221522 begin
15231523 OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
15241524 UpdateIndexes; //ensure index defs are up to date
15251525 end;
15261526 // проверяем, существует ли нисходящий индекс для этого столбца.
15271527 // если нет, создаем
15281528 if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
15291529 begin
15301530 OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
15311531 UpdateIndexes; //ensure index defs are up to date
15321532 end;
15331533
15341534 // Используем тег столбца для переключения направления сортировки ASC/DESC
15351535 column.tag := not column.tag;
15361536 if boolean(column.tag) then
15371537 begin
15381538 Column.Title.ImageIndex:=ImageArrowUp;
15391539 Openquery.IndexName:=ASC_IndexName;
15401540 end
15411541 else
15421542 begin
15431543 Column.Title.ImageIndex:=ImageArrowDown;
15441544 OpenQuery.IndexName:=DESC_IndexName;
15451545 end;
15461546 // Удаляем стрелку сортировки из предыдущего столбца, который мы отсортировали
15471547 if (FLastColumn <> nil) and (FlastColumn <> Column) then
15481548 FLastColumn.Title.ImageIndex:=-1;
15491549 FLastColumn:=column;
15501550end;
15511551```
15521552
15531553### Выбор записей в DBGrid с помощью флажков(чекбоксов)
15541554
15551555Цель состоит в том, чтобы иметь возможность выбирать произвольные записи в dbgrid с помощью флажков, сетка имеет возможность автоматически отображать флажки, когда обнаруживает наличие логических полей, для других типов полей пользователь может вручную выбрать значение cbsCheckboxColumn свойства ButtonStyle для столбца. Для этого типа столбцов пользователь просто устанавливает флажок, и содержимое поля изменяется соответствующим образом.
15561556
15571557Но что произойдет, если в нашем наборе данных нет такого доступного поля? Или мы не хотим, чтобы сетка входила в состояние редактирования при установке флажка? При добавлении столбца без полей с помощью `ButtonStyle = cbsCheckboxColumn` все флажки будут выделены серым цветом и отключены, поскольку поле, связанное с этим столбцом, отсутствует и поэтому изменять нечего. Поскольку мы хотим обрабатывать состояние флажка самостоятельно, нам нужно хранить состояние где-то для каждой записи. Для этого мы можем использовать класс TBookmarklist (определенный в модуле dbgrids.pas), где свойство CurrentRowSelected может сказать, выбрана текущая запись или нет. Используя события dbgrid OnCellClick и OnUserCheckboxState, мы можем отслеживать состояние флажка.
15581558
15591559> **Note:** эта хитрость требует наличие Lazarus r31148 или новее, который реализует событие OnUserCheckboxState.
15601560
15611561``` pascal
15621562...
15631563uses ..., dbgrids, stdctrls, ...
15641564
15651565type
15661566
15671567 { TForm1 }
15681568
15691569 TForm1 = class(TForm)
15701570 ...
15711571 procedure DBGrid1CellClick(Column: TColumn);
15721572 procedure DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
15731573 procedure FormCreate(Sender: TObject);
15741574 procedure FormDestroy(Sender: TObject);
15751575 ...
15761576 private
15771577 RecList: TBookmarklist;
15781578 ...
15791579 end;
15801580
15811581procedure TForm1.DBGrid1CellClick(Column: TColumn);
15821582begin
15831583 if Column.Index=1 then
15841584 RecList.CurrentRowSelected := not RecList.CurrentRowSelected;
15851585end;
15861586
15871587procedure TForm1.FormCreate(Sender: TObject);
15881588begin
15891589 RecList := TBookmarkList.Create(DbGrid1);
15901590end;
15911591
15921592procedure TForm1.FormDestroy(Sender: TObject);
15931593begin
15941594 RecList.Free;
15951595end;
15961596
15971597procedure TForm1.DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
15981598begin
15991599 if RecList.CurrentRowSelected then
16001600 AState := cbChecked
16011601 else
16021602 AState := cbUnchecked;
16031603end;
16041604```
16051605
16061606### Пример: Как экспортировать данные из DBGrid в текстовый файл
16071607
16081608Есть несколько способов создать текстовый файл из содержимого DBGrid. В большинстве случаев люди будут говорить вам, что вместо того, чтобы думать о DBGrid, как о контейнере данных, лучше поискать связанный набор данных.
16091609
16101610Но что произойдет, когда вам понадобится текстовое представление, которое должно соответствовать внешнему виду сетки: видимость колонок, ширина или порядок?
16111611
16121612Одним из решений является использование пакета LazReport, в котором есть компонент, специально предназначенный для создания отчета из DbGrid. Затем этот отчет можно экспортировать в несколько других форматов, включая текстовый формат. Здесь представлено простое решение, которое также может быть использовано в качестве основы для других экспортеров текстового формата, например, простого экспортера HTML.
16131613
16141614``` pascal
16151615// aGrid: Сетка для экспорта
16161616// lpp: строк на странице. Не включает текстовый заголовок
16171617// pageBreak: вставляет разрыв страницы, который некоторые принтеры могут использовать для запуска новой страницы
16181618// require: uses LazUTF8
16191619function GridTextExporter(aGrid: TDbGrid; lpp: Integer; pageBreak:boolean): TStringList;
16201620const
16211621 PX_PER_CHAR = 7;
16221622
16231623var
16241624 Ds: TDataset;
16251625 book: TBookMark;
16261626 recCount: Integer;
16271627 line: string;
16281628 column: TColumn;
16291629
16301630 function WidthToChar(aWidth: Integer): integer;
16311631 begin
16321632 result := trunc(aWidth/PX_PER_CHAR);
16331633 end;
16341634
16351635 procedure AddNext(theText: string; alignment: TAlignment);
16361636 var
16371637 width: Integer;
16381638 begin
16391639 if (line<>'') and (line<>#12) then
16401640 line := line + ' ';
16411641
16421642 width := WidthToChar(Column.Width);
16431643
16441644 case Alignment of
16451645 taRightJustify: line := line + UTF8PadLeft(theText, width);
16461646 taCenter: line := line + UTF8PadCenter(theText, width);
16471647 else line := line + UTF8PadRight(theText, width);
16481648 end;
16491649 end;
16501650
16511651 procedure CollectHeader;
16521652 begin
16531653 AddNext(Column.Title.Caption, Column.Title.Alignment);
16541654 end;
16551655
16561656 procedure CollectField;
16571657 var
16581658 field: TField;
16591659 begin
16601660 field := Column.Field;
16611661
16621662 if (field.DataType=ftMemo) and (dgDisplayMemoText in aGrid.Options) then
16631663 AddNext(field.AsString, Column.Alignment)
16641664 else if Field.DataType<>ftBlob then
16651665 AddNext(field.DisplayText, Column.Alignment)
16661666 else
16671667 AddNext('(blob)', Column.Alignment);
16681668 end;
16691669
16701670 procedure LoopColumns(printingRecord: boolean);
16711671 var
16721672 c: TCollectionItem;
16731673 begin
16741674 if (not printingRecord) and pageBreak and (result.count>0) then
16751675 line := #12
16761676 else
16771677 line := '';
16781678 for c in aGrid.Columns do begin
16791679 Column := TColumn(c);
16801680 if Column.Visible and (Column.Width>=PX_PER_CHAR) then begin
16811681 if printingRecord then CollectField
16821682 else CollectHeader;
16831683 end;
16841684 end;
16851685 result.add(line);
16861686 end;
16871687
16881688begin
16891689 result := TStringList.create;
16901690 Ds := aGrid.DataSource.DataSet;
16911691 Ds.DisableControls;
16921692 book := Ds.GetBookmark;
16931693 try
16941694 Ds.First;
16951695 recCount := 0;
16961696 while not DS.EOF do begin
16971697 if RecCount mod lpp = 0 then
16981698 LoopColumns(false);
16991699 LoopColumns(true);
17001700 inc(recCount);
17011701 Ds.Next;
17021702 end;
17031703 finally
17041704 Ds.GotoBookmark(book);
17051705 Ds.FreeBookmark(book);
17061706 Ds.EnableControls;
17071707 end;
17081708end;
17091709```
17101710
17111711Чтобы использовать это просто сделайте:
17121712
17131713``` pascal
17141714var
17151715 L: TStringList;
17161716begin
17171717 L := GridTextExporter(grid, 80, true);
17181718 L.SaveToFile('grid.txt');
17191719 L.Free;
17201720end;
17211721```
17221722
17231723---
17241724*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