Каскадированное удаление с проверкой целостности Paradox
Таблицы Paradox имеют характеристику проверки целостности (Referential Integrity). Данная характеристика предотвращает добавление записей в дочернюю таблицу, для которых нет соответствующих записей в родительской таблице. Это также изменяет ключевое(ые) поле(я) в дочерней таблице при изменениях в соответствующем(их) ключевом(ых) поле(ях) родительской таблицы (обычно это называют каскадированным обновлением). Эти события происходят автоматически, и не требуют никакого вмешательства со стороны Delphi-приложений, использующих эти таблицы. Тем не менее, характеристика проверки целостности таблиц Paradox не работает с каскадированным удалением. То есть, Delphi не позволит вам удалять записи в родительской таблице при наличии существующих записей в дочерней таблице. Это могут сделать только дочерние записи «без родителей», обходя проверку целостности. При попытке удаления такой родительской записи, Delphi сгенерит объект исключительной ситуации.
Для того, чтобы каскадированное удаление дало эффект, требуется программное удаление соответствующих дочерних записей прежде, чем будет удалена родительская запись. В приложениях Delphi это делается посредством прерывания удаления записи в родительской таблице, удаление соответствующих записей в дочерней таблице (если таковая имеется), и затем продолжение удаления родительской записи.
Удаление записи таблицы осуществляется вызовом метода Delete компонента TTable, который удаляет текущую запись в связанной с компонентом таблице. Прерывание процесса удаления для выполнения других операций связано с созданием обработчика события BeforeDelete компонента TTable. Любые действия в обработчике события BeforeDelete произойдут прежде, чем приложением будет послана команда Borland Database Engine (BDE) на физическое удаление записи из табличного файла.
Для того, чтобы обработать удаление одной или более дочерних записей, в обработчике события BeforeDelete необходимо организовать цикл, осуществляющий вызов метода Delete компонента TTable для всех записей дочерней таблицы. Цикл основан на условии, что указатель на запись в таблице не позиционируется на конец набора данных, как указано методом Eof компонента TTable. Это также предполагает, что удаляются все дочерние записи, соответствующие родительским записям: если нет соответствующих записей, указатель на запись устанавливается на конец набора данных, условие выполнения цикла равно False, и метод Delete в теле цикла никогда не выполняется.
В вышеуказанном примере родительская таблица представлена компонентом TTable с именем Table1, и дочерняя таблица с именем Table2. Методы DisableControls и EnableControls использованы в «косметических» целях, чтобы «заморозить» любые компоненты для работы с базами данных, которые могли бы отображать данные из таблицы Table2 во время удаления записей. Эти два метода делают процесс визуально «гладким», и не являются обязательными. Метод Next в теле данного цикла вызываться не должен. Дело в том, что цикл начинается с первой записи и, так как каждая запись удаляется, запись, предшествующая удаленной, перемещается в наборе данных вверх, становясь одновременно первой и текущей записью.
Данный пример предполагает, что родительская и дочерняя таблицы связаны отношением Мастер-Деталь, типичным для таблиц, в которых сконфигурирована проверка целостности. В результате, в связанных таблицах становятся доступны только те записи дочерней таблицы, которые соответствуют текущей записи родительской таблицы. Все другие записи в дочерней таблице делаются недоступными через фильтрацию Мастер-Деталь. Если таблицы не связаны отношениями Мастер-Деталь, то есть два дополнительных замечания, которые необходимо принимать во внимание при удалении записи дочерней таблицы. Первое: вызов метода First может и не переместить указатель записи в запись, соответствующей текущей записи родительской таблицы. Для этого необходимо воспользоваться методом search, вручную перемещающий указатель на сопоставимую запись. Второе замечание относится к условию цикла. Поскольку будут доступны другие записи, не относящиеся к записям родительской таблицы, сопоставимым (Мастер-Деталь) к текущей записи, то перед удалением записи необходимо осуществлять проверку на сопоставимость удаляемой записи. Эта проверка должна проводиться дополнительно к методу Eof. Поскольку записи будут сортироваться по ключевому полю (первичного или вторичного индекса), все сопоставления (Мастер-Деталь) будут непрерывными. Это будет истиной до достижения первой не-сопоставимой записи, после чего можно считать, что все сопоставимые записи были удалены. Таким образом, предыдущий пример необходимо изменить следующим образом:
Disablecontrols delphi что это
Управление TDBGrid во время выполнения
Примеры, которые вы увидите в этой статье, продемонстрируют основные способы, которыми пользуются большинство программистов для показа таблиц БД пользователям. Для понимания большей части материала требуется общее знание среды и языка Delphi.
По-умолчанию, все поля в диалоге выбраны. Нажмите на кнопку OK, чтобы выбрать все поля, и закройте редактор. Снова загляните в “Object Selector”, теперь здесь появилось несколько новых объектов, (см. рис.2)
Эти новые объекты будут использоваться для визуального представления таблицы CUSTOMER пользователю.
Вот полный список объектов, которые только что созданы:
Я вырезал и вставил этот список из определения класса TForm1, которое можно найти в окне Редактора исходного текста. Происхождение имен показанных здесь, должно быть достаточно очевидно. Часть «Query1» берется по-умолчанию от имени объекта TQuery, а вторая половина от имени поля в таблице Customer. Если бы мы сейчас переименовали объект Query1 в Customer, то получили бы такие имена:
Это соглашение может быть очень полезно, когда Вы работаете с несколькими таблицами, и сразу хотите знать, на поле какой таблицы ссылается данная переменная.
Любой объект, созданный в редакторе DataSet является наследником класса TField. Точный тип потомка зависит от типа данных в конкретном поле. Например, поле CustNo имеет тип TFloatField, а поле Query1City имеет тип TStringField. Это два типа полей, которые Вы будете встречать наиболее часто. Другие типы включают тип TDateTimeField, который представлен полем Query1LastInvoiceDate, и TIntegerField, который не встречается в этой таблице.
Чтобы понять, что можно делать с потомками TField, откройте Browser, выключите просмотр полей Private и Protected, и просмотрите свойства и методы Public и Published соответствующих классов.
Наиболее важное свойство называется Value. Вы можете получить доступ к нему так:
procedure TForm1.Button1Click(Sender: TObject);
В коде, показанном здесь, сначала присваиваются значения переменным d и S. Следующие две строки изменяют эти значения, а последний две присваивают новые значения объектам. Не имеет большого смысла писать код, подобный этому, в программе, но этот код служит лишь для того, чтобы продемонстрировать синтаксис, используемый с потомками TField.
Это могло бы заставить вас думать, что у Delphi внезапно отключился строгий контроль типов. Ведь TCurrencyField.Value объявлена как Double, и если Вы пробуете присвоить ему строку, Вы получите ошибку “type mismatch” (несоответствие типа). Вышеупомянутый пример демонстрирует на самом деле свойства объектов визуализации данных, а не ослабление проверки типов. (Однако, есть возможность получить значение поля уже преобразованное к другому типу. Для этого у TField и его потомков имеется набор методов типа AsString или AsFloat. Конечно, преобразование происходит только тогда, когда имеет смысл.)
Если нужно получить имена полей в текущем DataSet, то для этого используется свойство FieldName одним из двух способов, показанных ниже:
Если вы хотите получить имя объекта, связанного с полем, то вы должны использовать свойство Name:
Для таблицы CUSTOMER, первый пример вернет строку «CustNo», а любая из строк второго примера строку «Query1CustNo».
Программа CALC_SUM.DPR из примеров к данному уроку иллюстрирует первый случай использования вычисляемых полей.
Последовательность создания проекта CALC_SUM:
Требуется много слов для того, чтобы описать процесс показанный выше, но, фактически, выполнение команд в Эксперте форм БД легко и интуитивно.
Выделите первый из двух объектов TQuery и установят свойство Active в True. Для Query2 в свойстве SQL напишите текст запроса:
select * from Items I, Parts P
where (I.OrderNo =:OrderNo) and
Активизируйте объект Query2 (Active установите в True) и вызовите редактор DataSet (Fields Editor) для него. Вызовите диалог Add Fields и добавьте поля OrderNo, PartNo, Qty и ListPrice.
Нажмите Define и ведите слово Total в поле FieldName. Установите Field Type в CurrencyField. Проверьте что Calculated CheckBox отмечен. Нажмите Ok и закройте редактор DataSet.
Простой процесс описанный в предыдущем абзаце, показывает как создать вычисляемое поле. Если посмотреть в DBGrid, то можно видеть, что там теперь есть еще одно пустое поле. Для того, чтобы поместить значение в это поле, откройте в Инспекторе Объектов страницу событий для объекта Query2 и сделайте двойной щелчок на OnCalcFields. Заполните созданный метод так:
procedure TForm2.Query2CalcFields(DataSet: TDataSet);
procedure TForm1.Query2CalcFields(DataSet: TDataset);
Если теперь запустить программу, то поле Total будет содержать требуемое значение.
В обработчике события OnCalcFields можно выполнять и более сложные вычисления (это будет показано позже), однако следует помнить, что это вызывает соответствующее замедление скорости работы программы.
Теперь давайте добавим вычисляемое поле для первой таблицы (Query1, ORDERS), которое будет отображать сумму значений из поля Total второй таблицы (Query2) для данного заказа. Вызовите редактор DataSet для объекта Query1 и добавьте вычисляемое поле NewItemsTotal типа CurrencyField. В обработчике события OnCalcFields для Query1 нужно подсчитать сумму и присвоить ее полю NewItemsTotal:
procedure TForm1.Query1CalcFields(DataSet: TDataset);
with Query2 do begin
Поместите на форму еще один элемент DBEdit и привяжите его к Query1, полю NewItemsTotal. Запустите программу, ее примерный вид показан на рис.3
Рис.3: Программа CALC_SUM
Как видно из программы, наличие поля ItemsTotal в таблице ORDERS для данного примера необязательно и его можно было бы удалить (однако, оно необходимо в других случаях).
Объект DBGrid может быть полностью реконфигурирован во время выполнения программы. Вы можете прятать и показывать колонки, изменять порядок показа колонок и их ширину.
Вы можете использовать свойство Options объекта DBGrid, чтобы изменить ее представление. Свойство Options может принимать следующие возможные значения:
| DgEditing | Установлен по-умолчанию в true, позволяет пользователю редактировать grid. Вы можете также установить свойство ReadOnly grid в True или False. |
| DgTitles | Будут ли видны названия колонок. |
| DgIndicator | Будут ли видны небольшие иконки слева. |
| DgColumnResize | Может ли пользователь менять размер колонки. |
| dgColLines | Показывать ли линии между колонками. |
| dgRowLines | Показывать ли линии между строками. |
| dgTabs | Может ли пользователь использовать tab и shift-tab для переключения между колонками. |
Как объявлено в этой структуре:
TDBGridOption = (dgEditing, gdAlwaysShowEditor, dgTitles,
dgIndicator, dgColumnResize, dgColLines,
Например Вы можете установить опции в Runtime написав такой код:
DBGrid1.Options := [dgTitles, dgIndicator];
Если Вы хотите включать и выключать опции, это можно сделать с помощью логических операций. Например, следующий код будет добавлять dgTitles к текущему набору параметров:
DBGrid1.Options := DBGrid1.Options + [dgTitles];
Пусть есть переменная ShowTitles типа Boolean, тогда следующий код позволяют включать и выключать параметр одной кнопкой:
procedure TForm1.Button3Click(Sender: TObject);
if ShowTitles then
DBGrid1.Options := DBGrid1.Options + [dgTitles]
ShowTitles := not ShowTitles;
Если Вы хотите скрыть поле в run-time, то можете установить свойство visible в false:
Обе строки кода выполняют идентичную задачу. Чтобы показать поле снова, установите видимый в true:
Если Вы хотите изменить положение колонки в Runtime, можете просто изменить индекс, (первое поле в записи имеет индекс нуль):
По-умолчанию, поле CustNo в таблице Customer является первым. Код в первой строке перемещает это поле во вторую позицию, а следующая строка перемещает его в третью позицию. Помните, что нумерация полей начинается с нуля, так присвоение свойству Index 1 делает поле вторым в записи. Первое поле имеет Index 0.
Когда Вы изменяете индекс поля, индексы других полей в записи изменяются автоматически.
Если Вы хотите изменить ширину колонки в Runtime, только измените свойство DisplayWidth соответствующего TField.
Величина 12 относится к числу символов, которые могут быть показаны в видимом элементе.
Программа DBGR_RT показывает как работать с DBGrid в Runtime. Программа достаточно проста, кроме двух небольших частей, которые описаны ниже. Первая часть показывает, как создать check box в Runtime, а вторая показывает, как изменить порядок пунктов в listbox в Runtime.
При создании формы (событие OnCreate) ListBox заполняется именами полей, далее создается массив объектов CheckBox, соответствующий полям в таблице. Сперва все CheckBox’ы выбраны и все поля в таблице видимы. Программа узнает через TTable1 имена полей и присваивает их свойству Caption соответствующего CheckBox. Кроме того, обработчику события OnClick всех CheckBox’ов присваивается процедура ChBClick, которая и включает/выключает поля в DBGrid.
procedure TForm1.FormCreate(Sender: TObject);
Delphi Notes
Заметки Delphi + Oracle программиста
Страницы
среда, 13 июля 2011 г.
Размышления на тему: недостатки датасетов
Данный пост представляет из себя размышления на тему: недостатки TDataSet. Я предполагаю, что читатель знаком с тем, что TDataSet – это базовый класс для работы с наборами данных (НД). Так же я предполагаю, что читатель знаком с основами программирования приложений для работы с базами данных (БД).
Delphi предлагает такую схему работы с НД:
а) TDataSet – для работы с самим НД;
б) TDataSource – источник данных, используется для связи НД с визуальными компонентами;
в) связь компонент с НД (для отображения и редактирования) через DataSource.
Самое простое – это создание формы для редактирования конкретной записи: каждый контрол на форме связывается с конкретным полем НД. Перед редактированием пользователь выбирает запись, а после редактирования – либо сохраняет изменения, либо отменяет. Работа с конкретной записью не вызывает нареканий.
Но вот когда необходимо работать со всем набором сразу (для отображения в гриде, для сортировки, для решения каких-то аналитических задач) – сразу возникают проблемы-неудобства. Причина этих проблем заключается в том, что перебор записей датастета возможен только с помощью курсора, указывающего на текущую (активную) запись. При этом нужно помнить, что если с датасетом связаны визуальные контролы, то в общем случае надо делать примерно так:
DisableControls/EnableControls необходимо делать, чтобы контролы не реагировали на изменение текущей записи и не вызывали мерцания, Bookmark нужен, чтобы после работы с НД вернуться к той записи, с которой работал пользователь.
У DataSet’ов есть свойство – текущее состояние. Есть промежуточное состояние – dsBrowse, и много разных – запись редактрируется, ищется, и т.п. Причём переход из одного состояния в другой возможен только через dsBrowse. А это означает, что если НД открыт на редактирование, то поиск по этому же НД уже не возможен. Более того, если я работаю, например, с иерархической структурой, то открыв запись на редактирование, я не могу использовать этот же НД для указания родительской записи.
В зависимости от реализации наследника от DataSet’а, можно наткнуться на дополнительные сюрпризы. Например, я сталкивался с тем, что работа с текущей записью осуществляется через некоторый буфер. Т.е. переход от записи к записи вызывает копирования полей записи в буфер, и программист работает с данными из буфера (а не непосредственно с данными записи). С одной стороны – это защита от случайной записи в набор данных. Но с другой – это приводит к неприятным задержкам при копировании.
Вобщем, интерфейс класса TDataSet не позволяет открыть сразу несколько записей на редактирование, приходится создавать модальные формы, что не очень красиво по отношению к пользователю.
Описанные проблемы – это не панацея. Все указанные неприятности либо решаются обходными путями, либо их можно игнорировать: задержки копирования сегодня настолько незначительны, что простому пользователю не заметны; чтобы не писать DisableControls/EnableControls можно создать шаблон кода.
Самым главным неудобством в датасете для меня является понятие текущей (активной) записи. На мой взгляд, это понятие надо отнести к гриду (либо, возможно, к DataSource’у). Если бы так было сделано изначально, то мы (программисты) получили бы возможность прозрачной навигации по НД. А работа с конкретной записью – она ведь определяется пользователем: именно пользователь выбирает строку в таблице (или даже несколько (!) строк), программисту логично запросить у грида выделенную строку (список выделенных строк) и работать с ними.
В подтверждение сказанного я могу привести простой пример: вспомните, сколько существует визуальных компонент для работы с гридам; самые удобные перед работой делают полную копию датасета и не работают с датасетом непосредственно, либо предлагают использовать “свои” датасеты.
Наличие этих недостатков привело к созданию собственных механизмов для работы с НД. Подробно я их пока описывать не буду, приведу только такой пример:
Здесь: TableName – имя таблицы, FieldName – имя поля, Row – ссылка на запись в НД. Так выглядит обращение в наших библиотеках к полю в общем случае. А благодаря автогенерации кода, о которой я попробую рассказать попозже, такой же код мы давно уже пишем так:
Ну и на последок скажу, что изобретение своего велосипеда у нас зашло довольно далеко – написаны механизмы для работы с БД, отдельно обёртки над компонентом VirtualTreeView для визуализации данных в виде таблицы, и отдельно обёртки над обычными компонентами ввода данных типа TEdit, TCombobox и т.п. С одной стороны, много самописного кода – обо всём этом за один раз не рассказать. Но с другой стороны нам это даёт много бонусов, что в конечном итоге (и я в это верю) положительно отражается на пользовательских интерфейсах.
Disablecontrols delphi что это
Есть главная Table1 и подчиненная Table2. В Table2 необходимо внести некоторые изменения. Для этого использую нижеприведенный код. Необходимо расставить в нем DisableControls и EnableControls для ускорения вычислений. Помогите расставить, никак не соображу. В принципе можно убрать связь между таблицами и работать только с Table2, но не желательно.
procedure TForm1.Button1Click(Sender: TObject);
var
z: longint;
begin
Table1.First;
for z := 1 to Table1.RecordCount do
begin
Table2.First;
While not Table2.Eof do
begin
Table2.Edit;
//выполнение опр. операций
Table2.Post;
Table2.Next;
end;
Table1.Next;
end;
end;

Внук © ( 2004-10-02 14:05 ) [1]
procedure TForm1.Button1Click(Sender: TObject);
var z: longint;
begin
try
Table1.DisableControls;
Table2.DisableControls;
Table1.First;
for z := 1 to Table1.RecordCount do
begin
Table2.First;
While not Table2.Eof do
begin
Table2.Edit;
//выполнение опр. операций
Table2.Post;
Table2.Next;
end;
Table1.Next;
end;
finally
Table1.EnableControls;
Table2.EnableControls;
end;
end;

Vemer © ( 2004-10-02 14:13 ) [2]

тот же ( 2004-10-02 14:48 ) [3]

Внук © ( 2004-10-02 15:03 ) [4]
Очевидно, надо в отладчике смотреть, какое значение возвращает Table1.RecordCount. DisableControls здесь ни при чем. Хотя нафига там RecordCount тоже непонятно. Лучше писать
While not Table1.Eof do
> Внук © (02.10.04 15:03) [4]
> Очевидно, надо в отладчике смотреть, какое значение возвращает
> Table1.RecordCount. DisableControls здесь ни при чем. Хотя
> нафига там RecordCount тоже непонятно. Лучше писать
> While not Table1.Eof do
с While not Table1.Eof do результат тот же
Если оставить только Table1.DisableControls; то ничего не меняется
а что ты хочеш чтобы изменилось? скорость? тогда скажи у тебя таблици с визуальными контролами связаны?

тот же ( 2004-10-02 15:52 ) [7]
> sniknik © (02.10.04 15:29) [6]
Обе таблицы связаны с DBGrid»ами.Естественно, нужна скорость. Если на период расчетов разрывать связь с ними, то пользователя это может смутить.

Внук © ( 2004-10-02 16:48 ) [8]
Я все-таки не понял, у тебя при выполнении Table1.Next данные в Table2 не меняются (тот же (02.10.04 14:48) [3])? Или внешний цикл только одну итерацию делает?
Или что? Или скорость не устраивает?
если сделать как > Внук © (02.10.04 14:05) [1]
то единственно почему как DisableControls; так и без его скорость почти одна и таже, это то, что «//выполнение опр. операций» настолько длительное, что скрывает весь выыгрыш от его применения. (хотя нет. ;), еще может быть так мало данных в таблицах что выигрыша просто не видно)
> если сделать как > Внук © (02.10.04 14:05) [1]
> Table1.DisableControls;
>Но в данном случае вычисления проводятся только для данных Table2, связанных только с первой записью Table1
Именно так и происходит 🙂
Инструменты пользователя
Инструменты сайта
Боковая панель
Навигация

Связь
Содержание
EhLib
TDbGridEh
визуальные свойства TDBGridEh
Выравнивание
Выравнивание заголовка всех колонок:
Автоподгонка
Чтобы при изменении размера шрифта грида высота строки автоматически подгонялась выставить options dghFitRowHeightToText. Можно включить EndEllipsis
Опции
Скрыть повторяющиеся значения в столбце
TColumnEh.HideDuplicates
Существуют ситуации, когда необходимо указать, что фактически началось новое значение в ячейке, даже если текстовое представление значений совпадает. Например: имена пользователей могут совпадать, хотя идентификаторы пользователей разные.
Форматы сумм
выставить для всех колонок формат сумм:
Выделение и подсветка текущей записи
Мультиселект (множественный выбор)
опция dgMultiSelect в свойстве Options
DBGridEh2.SelectedRows.SelectAll;
DBGridEh2.SelectedRows.Clear;
При изменении выбранной области вызывается событие OnSelectionChanged.
Затемнение грида
Затемнение грида на время загрузки данных
Сохранить/восстановить позицию в гриде
Вывести картинку в поле
Колонка содержит числовое поле
Колонка содержит строковое поле
Колонка содержит мемо-поле
Можно присвоить свойство Column.NotInKeyListIndex индексом картинки, выводимой в случае если значение поля не совпадает ни с одним значением в KeyList (например, задать индекс картинки для значения Null).
Checkbox’ы для не-boolean полей
Кнопка EditButton
Если не назначать обработчик, то при нажатии на кнопку автоматически выпадает форма-редактор для значения поля. Удобно для многострочных мемо-полей.
По умолчанию кнопка видна только при переходе в режим редактирования поля.
На уровне грида видимость кнопок регулирует свойство EditButtonsShowOptions (чекбоксы)
Удалить выделенные записи
(при включенном мультиселекте)
Суммировать выделенное
Выделение контуром
выделять в гриде контуром всю строку (как в экселе, без закрашивания ячеек)
Используйте событие Column.OnAdvDrawDataCell

отключать автоинкрементность поля
хочу ветки дерева подгружать динамически […] И все бы отлично, но MemTableEh определяет поле id (MemTableEh.TreeList.KeyField = id) как инкриментное, а мне это не нужно. т.к. вместо тех значений что прилетают с сервера он подставляет свои отрицательные. как это обойти?
Попробуйте на время загрузки отключать автоинкрементность поля.
Обязательные поля
Если вы попытаетесь перейти на новую запись в DataSet’е (гриде), не заполнив одно из полей, требующих значение, DataSet вызовет исключение и не даст покинуть запись пока поле не будет заполнено или все изменения записи не будут отменены.
Установите TDBGridEh.ColumnDefValues.HighlightRequired или TColumnEh.HighlightRequired, чтобы подсвечивать такие ячейки.
Грид будет подчеркивать красной пунктирной линией обязательные для ввода значения.
Также можно написать событие TColumnEh.OnCheckDrawRequiredState, чтобы самому управлять, когда необходимо рисовать линию-индикатор обязательного ввода данных. В обработчике события установите значение свойства DrawState в True, чтобы отобразить линию-индикатор для нужных записей и полей.
Lookup поля
Устанавливается у выбранной колонки грида, содержащей внешний ключ так чтобы вместо кода поля из БД в поле отображается текст из справочника.
DBGridEh и DBVertGridEh автоматически определяют тип поля FieldKind и создают редактор ячейки с выпадающим списком таким образом, что он выглядит как Lookup ComboBox и позволяет выбирать значения из списка, изменяя внешний ключ.
В результате колонка в гриде имеет свойства:


Для отображения нескольких полей в выпадающем lookup-списке, заполните свойство Column.LookupDisplayFields списком полей, разделяя поля символом «;» (точка с запятой).
По умолчанию в выпадающем списке отображается один столбец со значением поля, заданным в TField.LookupResultField, но можно настроить список так, чтобы в нем отображалось несколько полей.
Способ 1: Заполните свойство TColumnEh.DropDownBox.ListFieldNames списком полей. Несколько полей разделяются символом «;» (точка с запятой).
Способ 2: Заполните коллекцию TColumnEh.DropDownBox.Columns элементами, соответствующими столбцам выпадающего списка. При этом способе также можно настроить шрифт и другие свойства для каждого столбца выпадающего списка.
Настройка lookup отношений непосредственно в гриде
используйте подсвойства свойства TColumn.LookupParams аналогично тому, как это настраивается в TField. Свойство TColumn.FieldName заполнять не нужно.
При большом количестве настроенных lookup столбцов в гриде, отрисовка данных может замедлиться, т.к. поиск lookup значений происходит при каждой отрисовке ячейки.
Отрисовку можно ускорить, если в качестве DataSet’а использовать TMemTableEh. MemTableEh предоставляет возможность создавать в DBGridEh ассоциированный буфер найденных значений и динамически обновляет его по мере изменений данных в MemTableEh.
Расширенная настройка
свойства объекта TColumnEh (TFieldRow для DBVertGridEh)
Это свойство можно использовать для нескольких случаев:
1. Вы можете присвоить этому свойству TDataSource объект, который ссылается на специальный DataSet, список значений которого отличается от полного списка значений, заданного в TField.LookupDataSet. Например, DataSet, указанный в DBGridEh.DataSrouce.DataSet и DataSet, указанный в TColumnEh.DropDownBox.ListSource.DataSet состоят в отношении Master-Detail. При выборе определенной записи в DBGridEh список ListSource.DataSet будет заполнен только нужными значениями согласно Master-Detail отношению, который и будет отображаться, когда пользователь нажмет кнопку открытия выпадающего списка в редакторе текста в гриде для этого lookup столбца.
2. Можно настроить выпадающий список для фильтрации данных по мере набора текста в редакторе ячейки. Детальную информацию смотрите в разделе «Фильтрация данных в выпадающих lookup списках при наборе текста»
Определяет, что при нажатии на заголовок в выпадающем списке необходимо выполнять сортировку локально внутри DataSet’а. Для настройки сортировки также задайте необходимые значения в свойстве DropDownBox.Options и установите Title.TitleButton в True в свойстве DropDownBox.AutoFitColWidths.
KeyList и PickList
Вы можете отображать в столбце другой текст, зависящий от значения поля.
Установите Column.NotInKeyListIndex в индекс текста из PickList, который отображается если значение поля не содержится в KeyList (например, вы можете задать индекс текста для Null-значения поля).
Установите Column.DblClickNextval в True, чтобы менять значение double-кликом.
Работа с кнопками редактирования
Все контролы редактирования, а также классы столбцов в гриде TColumnEh (для DBGridEh) и TFieldRow (для DBVertGridEh), содержат два свойства EditButton и EditButtons.
EditButton задает параметры для кнопок редактирования. Кнопки редактирования отображаются внутри контрола редактирования или внутри ячейки грида. Чтобы активировать одну кнопку, используйте свойство EditButton.Visible.

property TColumnEh.EditButton.DrawBackTimeStored: Boolean
Определяет, что значение в свойстве DrawBackTime фиксировано и не зависит от значения других свойств. Если DrawBackTimeStored = False, значение по умолчанию для DrawBackTime берется из свойства TDBGridEh.ColumnDefValues.EditButtonDrawBackTime.
OnClick, OnDown
Можно написать свое действие только на нажатие или на «клик» (нажатие-отпускание) кнопки редактирования. Для этого напишите обработчик события OnButtonDown или OnButtonClick для контрола редактирования. Для TColumnEh (для DBGridEh) или TFieldRow (для DBVerGridEh) события имеют имена OnEditButtonDown и OnEditButtonClick. Если кнопка создана через коллекцию EditButtons, то необходимо написать обработчик события для элемента коллекции.
Класс TEditButtonEh
MRUList
список последних введенных значений
Все контролы редактирования, а также классы столбцов в гриде TColumnEh (для DBGridEh) и TFieldRow (для DBVerGridEh) содержат свойство MRUList.
MRUList начинает работать только после активации. MRUList.Active = True.
Можно заполнить начальные значения списка, используя свойство MRUList.Items типа TStrings.
Список значений MRUList можно формировать автоматически на основе списка уникальных значений в поле DataSet’а. Чтобы указать, что данные для списка должны браться из DataSet’а установите свойство MRUList.ListSourceKind в lskDataSetFieldValuesEh.
Список со значениями должен отобразиться, когда пользователь начинает вводить данные редакторе текста.
Не все DataSet’ы поддерживают доступ к внутренней структуре данных для получения списка уникальных значений столбца.
В текущей версии библиотеки режим MRUList.ListSourceKind = lskDataSetFieldValuesEh работает для DataSet’а типа TMemTableEh. В один из модулей проекта в раздел uses надо добавить модуль EhLibMTE. Этот модуль содержит класс, который умеет запрашивать данные об уникальных значениях поля из компоненты TMemTableEh.
Если ваш DataSet поддерживает возможность формирования списка уникальных значений поля, то вы можете написать и зарегистрировать свой класс наследник TDatasetFeaturesEh. В классе необходимо написать реализацию функции FillFieldUniqueValues. Пример реализации смотрите в модуле EhLibMTE.
ячейка-ссылка
у колонки включить свойство CellDataIsLink
выставить курсор на нужную колонку
Чтобы выставить курсор на нужную колонку:
Как программно установить фокус на определенный Column грида?
Собственный хинт ячейки
В событии столбца OnDataHintShow прописал
также надо включить DBGridEh.Columns.ToolTips = True
CellButtons
Элемент CellButton похож на элемент EditButton (кнопки редактирования), но в отличии от EditButton нажимается без открытия редактора текста. Набор свойств TCellButtonEh аналогичен набору свойств класса TEditButtonEh и имеет дополнительные свойства.

Ошибка Grid index out of range
[бывает как в DBGrid, так и в DBGgridEh]
Замучила ошибка «Grid index out of range». Причем она появляется в совершенно разных местах программы. Воспроизводится не всегда
Проблема оказалась в модуле dbgrids.pas (DBGgridEh.pas в моем случае).
Нашел решение в интернете. Вот оно
1. Copy the file dbgrids.pas to your project directory this is to avoid re-compiling the vcl, which by the way i don’t know how to do
2. Edit the dbgrids.pas file you just copied and make the TCustomDBGrid.UpdateActive procedure look like the following lines:
Сортировка и фильтрация данных
DBGridEh не может сортировать или фильтровать данные самостоятельно. Но он может послать команду для сортировки или фильтрации специальному объекту, который сделает это в DataSet’е.
Можно выбрать 2 типа сортировки (локальная и серверная), используя свойство SortLocal, и 2 типа фильтрации (также локальная и серверная), но реальная возможность применения каждого типа операции зависит от типа DataSet’а.
Например, TBDEDataSet не поддерживает локальную сортировку (внутри DataSet’а), так что невозможно сортировать данные локально, когда грид присоединен к TQuery или TTable.
Библиотека имеет набор специальных объектов для сортировки/фильтрации стандартных типов DataSet’ов.
| Unit | DataSet | Локальная сортировка | Серверная сортировка | Локальная фильтрация | Серверная фильтрация |
|---|---|---|---|---|---|
| EhLibBDE | TQuery, TTable | N | Y | Y | Y |
| EhLibADO | TADOQuery, TADODataSet | Y | Y | Y | Y |
| EhLibCDS | TClientDataSet | Y | Y | Y | Y |
| EhLibDBX | TSQLQuery, TSQLDataSet | N | Y | N | Y |
| EhLibIBX | TIBQuery, TIBDataSet | N | Y | Y | Y |
| EhLibMTE | TMemTableEh | Y | Y* | Y | Y* |
| EhLibFireDAC | TFDQuery, TFDStoredProc, TFDTable, TFDMemTable | Y | Y | Y | Y |
* Когда грид настроен на серверную сортировку или фильтрацию (SortLocal=False, STFilter.Local=False), то выполнение сортировки передается объекту TMemTableEh.DataDriver.
Когда грид настроен на серверную сортировку, специальный объект строит SQL-выражение ORDER BY, заменяет строку ORDER BY в свойстве Command или SQL DataSet’а и переоткрывает его.
Когда грид настроен на локальную сортировку, специальный объект производит сортировку, учитывая специфику конкретного типа DataSet’а.
Когда грид настроен на локальную фильтрацию, специальный объект строит выражение и присваивает его свойству Filter DataSet’а. Перед этим необходимо заранее выставить свойство Filtered в True.
Для автоматической фильтрации/сортировки данных в DataSet’е требуется добавить один из модулей EhLibXXX (EhLibADO, EhLibBDE, EhLibCDS … в зависимости от DataSet’а, подключенного к гриду) в раздел uses любого модуля проекта.
Эти EhLibXXX модули содержат код для регистрации класса, который наследуется от TDatasetFeaturesEh (специальный объект) и осуществляет фильтрацию/сортировку в DataSet’е. Для DataSet’ов сторонних разработчиков можно написать и зарегистрировать свой собственный класс или написать обработчики событий OnApplyFilter и/или OnSortMarkingChanged для каждого требуемого грида.
Кроме того, можно написать OnApplyFilter и/или OnSortMarkingChanged для глобальной переменной DBGridEhDataService. Эти события имеют тип TNotifyEvent, где в качестве Sender’а выступает TCustomDBGridEh.
Настройка грида для сортировки данных
Можно установить свойство грида ColumnDefValues.Title.TitleButton, чтобы добавить sortmarkers для всех столбцов, у которых не менялось свойство Column.Title.TitleButton.
Очерёдность сортировки задаётся в свойствах столбцов
Направление сортировки (возрастание/убывание) задаётся параметром
Для текстовых полей изначальное направление сортировки задается по возрастанию, для остальных полей по убыванию.
Для изменения изначального направления сортировки перепишите виртуальную функцию TDBGridEhCenter.GetFirstSortMarkerState. Для установки нового центра гридов используйте глобальную функцию SetDBGridEhCenter.
Можно задать вид значка сортировки с помощью свойства TDBGridEh.TitleParams.SortMarkerStyle для определенного грида или через глобальное свойство DBGridEhDefaultStyle.SortMarkerStyle для всех гридов проекта.
В Run-Time щелчок на заголовке изменяет порядок сортировки. При нажатой клавише Ctrl можно произвести сортировку по нескольким столбцам одновременно.
После щелчка на заголовке вызывается событие OnSortMarkingChanged, если оно задано, иначе действие передается на выполнение специальному объекту, если тот зарегистрирован.
Специальный объект использует свойство Grid.SortLocal для определения типа сортировки данных: локально либо на сервере. Если задан обработчик события OnSortMarkingChanged, то можно использовать свойство TDBGrid.SortMarkedColumns для доступа к сортируемым столбцам и свойство TColumnEh.Title.SortMarker для получения состояния маркера сортировки.
Пример. Выделение столбца сортировки цветом: demos\DBGridEh.ExtendVerLines
В событии DbGridEh.OnGetBtnParams можно маркером указывать колонку по которой таблица отсортирована SortMarker := smDownEh иначе SortMarker := smNoneEh или менять фонт заголовка этой колонки
Cобытие DbGridEh.OnTitleBtnClick реагирует на клик по заголовку колонки с TitleButton=True. В нем и менять сортировку. Для разных DataSet в соответствии со способами связи с СУБД допустимы различные приёмы: переключение индексов, использование Sort, повторный запрос с другим ORDER BY
Сортировка с помощью ADOQuery1.Sort (не проверяла):







