Телефон: +7 (926) 245-03-63

Сортировка колонок в DBGrid

Следующая статья: Перекодировка текста из DOS в Windows и наоборот.

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

Главное препятствие в решении задачи – сам DBGrid. Проблема в отсутствии событий OnClick или OnMouseDown, позволяющих реагировать на элементарные манипуляции с заголовком. Правда, существует событие OnDoubleClick, но для данной цели оно не слишком изящно. Нам нужно лишь создать заголовок, реагирующий на однократный щелчок мышью. Обратимся к компоненту THeaderControl.

Этот компонент, введенный в палитру компонентов еще в Delphi 2.0, обеспечивает необходимые нам функции. Главное достоинство – реакция компонента при щелчке по отдельным панелям. Панели также обеспечивают визуальное отображение подобно кнопке (могут вдавливаться и отжиматься). Нам необходимо «прицепить» THeaderControl к DBGrid. Вот как это сделать:

Во-первых, создайте новое приложение. Положите THeaderControl на форму. Он автоматически выровняется по верхнему краю формы. Затем поместите на форму DBGrid и присвойте свойству Align значение alClient. Затем добавьте компоненты TTable и TDataSource. В компоненте TTable присвойте свойству DatabaseName значение DBDEMOS, а свойству TableName значение EVENTS.DB. В TDataSource укажите в свойстве DataSet на компонент Table1, а в TDBGrid в свойстве DataSource на DataSource1. Если свойство Active компонента TTable равно True, выключите его (значение False).

Сделаем так, чтобы компонент THeaderControl выглядел похожим на заголовок компонента DBGrid. Произведем необходимые манипуляции в момент создания формы. Дважды щелкните на событии OnCreate формы и введите следующий код:

procedure TForm1.FormCreate(Sender: TObject); var TheCap: String; Thewidth, a: Integer; begin DBGrid1.Options:=DBGrid1.Options - [dgTitles]; HeaderControl1.Sections.Add; HeaderControl1.Sections.Items[0].width:=12; Table1.Active:=False; Table1.Exclusive:=True; Table1.Active := True; for a:=1 to DBGrid1.Columns.Count do begin with DBGrid1.Columns.Items[a - 1] do begin TheCap:=Title.Caption; Thewidth:=width; end; with HeaderControl1.Sections do begin Add; Items[a].Text:=TheCap; Items[a].width:=Thewidth+1; Items[a].Minwidth:=Thewidth+1; Items[a].Maxwidth := Thewidth+1; end; try Table1.AddIndex(TheCap, TheCap, []); except HeaderControl1.Sections.Items[a].AllowClick:=False; end; end; Table1.Active := False; Table1.Exclusive:=False; Table1.Active:=True; end; После того как THeaderControl заменил стандартный заголовок DBGrid, в первую очередь мы сбрасываем (устанавливаем в False) флаг dgTitles в свойстве Options компонента DBGrid. Затем мы добавляем колонку в HeaderControl и устанавливаем ее ширину равной 12. Это будет пустой колонкой, которая имеет ту же ширину, что и левая колонка статуса в DBGrid.

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

Теперь добавляем секции в HeaderControl. Для каждой добавленной колонки мы создаем в заголовке тот же текст, что и в соответствующей колонке DBGrid. В цикле мы проходим по всем колонкам DBGrid и повторяем текст заголовка колонки и его высоту. Мы также устанавливаем для HeaderControl значения свойств Minwidth и Maxwidth равным ширине соответствующей колонки в DBGrid. Это предохранит колонки от изменения их ширины. Для изменяющих размер колонок нужно дополнительное кодирование. Этот вопрос читателю предлагается решить самостоятельно.

Теперь самое интересное. Мы собираемся создать индекс для каждой колонки в DBGrid. Имя индекса будет таким же, как и название колонки. Данный код мы должны заключить в конструкцию try..finally, поскольку существуют некоторые поля, которые не могут быть проиндексированы (например, поля Blob и Memo). При попытке индексации этих полей генерируется исключительная ситуация. Мы перехватываем это исключение и не допускаем возможности щелчка в данной колонке. Это означает, что колонки, содержащие неиндексированные поля, не будут реагировать на щелчок мышью. Создание этих индексов объясненяет, почему таблица должна быть открыта в режиме эксклюзивного (монопольного) доступа. И в заключение мы закрываем таблицу, сбрасываем флаг эксклюзивности и снова делаем таблицу активной.

Последний шаг. При щелчке на HeaderControl нам необходимо включить правильный индекс таблицы. Создадим обработчик события OnSectionclick компонента HeaderControl как показано ниже:

procedure TForm1.HeaderControl1Sectionclick(HeaderControl: THeaderControl; Section: THeaderSection); begin Table1.IndexName:=Section.Text; end;

После щелчка в заголовке колонки значение свойства таблицы IndexName становится равным заголовку компонента HeaderControl.

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

Улучшения

Здесь приведен код, улучшенный по сравнению с предыдущей версией «Совета», он заключается в использовании в качестве имени индекса имя поля вместо заголовка.

Это улучшает гибкость.

procedure TForm1.FormCreate(Sender: TObject); var TheCap: String; TheFn: String; Thewidth:Integer; a: Integer; begin Table1.Active:=True; DBGrid1.Options:=DBGrid1.Options - [DGTitles]; HeaderControl1.Sections.Add; HeaderControl1.Sections.Items[0].width := 12; for a:=1 to DBGrid1.Columns.Count do begin with DBGrid1.Columns.Items[a - 1] do begin TheFn:=FieldName; TheCap:=Title.Caption; Thewidth := width; end; with Headercontrol1.Sections do begin Add; Items[a].Text:=TheCap; Items[a].width:=Thewidth+1; Items[a].Minwidth:=Thewidth +1; Items[a].Maxwidth:=Thewidth+1; end; try { Используем индексы с тем же именем, что и имя поля } { Пробуем задать имя индекса } (DataSource1.Dataset as TTable).IndexName:=TheFn;
except HeaderControl1.Sections.Items[a].AllowClick:=False; { Индекс недоступен } end; end; end;

Используем свойство FieldName компонента DBGrid для задания индекса с тем же именем, что и имя поля.

procedure TfrmDoc.HeaderControl1Sectionclick(HeaderControl: THeaderControl; Section: THeaderSection); begin (DataSource1.Dataset as TTable).IndexName:=DBGrid1.Columns.Items[Section.Index - 1].FieldName; end;

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

Интересное в интернете: Аксессуары для мужчин и женщин, интернет магазин кожгалантереи