Программирование баз данных в Delphi

Работа с сеткой DBGrid

Показывать лекцию целиком

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

Как мы уже знаем, строки сетки DBGrid соответствуют записям подключенного набора данных, а столбцы - полям. Свойство DataSource содержит ссылку на выбранный набор данных. Изменяя эту ссылку во время работы программы, можно изменять выводимые в сетке данные, отображая то одну, то другую таблицу (или запрос) в одной сетке DBGrid.

Столбцы DBGrid

Столбцы содержат значения полей подключенного к сетке набора данных. Этими значениями можно манипулировать, показывая или скрывая поля НД, меняя их местами или добавляя новые столбцы. Нам уже приходилось это делать в редакторе полей набора данных, однако, это не всегда оправдано - один набор данных может использоваться в различных местах приложения, в различных формах и на различных сетках. Изменение свойств полей набора данных в этом случае коснется и всех сеток DBGrid, которые подключены к нему, а это требуется далеко не всегда. Более разумным вариантом будет добавление всех полей в редактор полей набора данных, а изменение их свойств можно сделать в каждой сетке по-своему.

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

Создайте новое приложение. Свойству Name формы, как всегда, присвойте значение fMain, свойству Caption - "Изучение свойств DBGrid". Проект сохраните в отдельную папку, модулю дайте имя Main, а проекту в целом - MyDBGrid. В эту же папку скопируйте базу данных ok.mdb из прошлой лекции. На форме нам понадобятся сетка DBGrid с вкладки Data Controls, с вкладки ADO компоненты ADOConnection и ADOTable, с вкладки Data Access - компонент DataSource. Также для красоты и удобства можно добавить компонент DBNavigator.

Из прошлых лекций вы знаете, как подключить к базе данных компонент ADOConnection, а затем подключить к нему таблицу ADOTable. В свойстве TableName таблицы выберите таблицу LichData, и откройте ее. Компонент DataSource подключите к нашей таблице, а сетку DBGrid и навигатор DBNavigator - к DataSource. В результате у вас должна получиться простая форма с сеткой и навигатором по ней, в которой отображаются все поля таблицы LichData:

(рис 12.1 ) Форма проекта

Допустим, в нашем проекте нам нужны не все поля таблицы, а только некоторые из них. Значит, придется поработать с редактором столбцов сетки DBGrid. Вызвать редактор можно тремя способами: дважды щелкнуть по сетке; щелкнуть правой кнопкой по сетке и в контекстном меню выбрать команду Columns Editor и, наконец, щелкнув дважды по свойству сетки Columns в Инспекторе Объектов:

(рис 12.2 ) Редактор столбцов сетки DBGrid

Работа с этим редактором очень похожа на редактор полей набора данных, но есть и отличия. В верхней части окна вы видите четыре кнопки, слева - направо:

  • Add New (Добавить новый столбец).
  • Delete Selected (Удалить выделенный столбец).
  • Add All Fields (Добавить все столбцы из набора данных).
  • Restore Defaults (Восстановить значения по умолчанию для выделенного столбца).
  • Если столбцов в редакторе нет, то сетка отображает все поля НД. Добавим один столбец. Для этого нажмем первую кнопку. Сразу же все поля НД исчезли, а сетка отображает пустой столбец. Выделим его в редакторе столбцов, а в Инспекторе объектов в свойстве FieldName выберем поле "Фамилия". Сразу же столбец отобразит это поле. Заголовок столбца будет соответствовать названию поля. В нашей БД имена полей мы задавали русскими буквами, однако это бывает не всегда, особенно если вы работаете с таблицами Paradox или клиент-серверными БД. В этом случае названия полей будут выводиться латиницей, а это не удобно для пользователя. Изменить параметры заголовка столбца можно в раскрывающемся свойстве Title, которое имеет ряд собственных свойств:

    Alignment - свойство устанавливает выравнивание заголовка и может быть taCenter (по центру), taLeftJustify (по левому краю) и taRightJustify (по правому краю). По умолчанию, заголовок выровнен по левому краю.

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

    Color - цвет заголовка, по умолчанию это свойство равно clBtnFace, что обеспечивает стандартный серый цвет. Если вы желаете украсить программу, можете выбрать другой цвет.

    Font - шрифт заголовка. Если дважды щелкнуть по этому свойству, откроется диалоговое окно, в котором можно изменить шрифт, начертание, размер и цвет шрифта. То же самое можно сделать, если раскрыть это свойство и непосредственно изменять нужные свойства.

    Надо заметить, что свойства Font, Alignment и Color внутри свойства Title меняют шрифт, выравнивание и цвет фона только заголовка столбца, а не его содержимого. Но у столбца имеются эти же свойства, они меняют шрифт, выравнивание и цвет фона выводимых в столбце данных.

    Свойство Visible разрешает или запрещает отображение столбца, а свойство Width позволяет изменить его ширину. О других свойствах поговорим чуть позже.

    Добавьте в сетку другие поля НД: "Имя", "Отчество", "Пол" и "Военнообязанный".

    Обратите внимание, что перетаскивая мышью столбцы в редакторе столбцов, вы можете менять их порядок в сетке. Пользователь же имеет возможность менять их порядок, перетаскивая мышью заголовки столбцов.

    У сетки DBGrid имеется свойство Columns, которое содержит столбцы. Щелчок по этому свойству как раз и откроет редактор столбцов сетки. Столбцы хранятся в свойстве Items в виде массива и имеют индексы от 0 до DBGrid1.Columns.Items.Count-1. Заметим, что свойство Items используется по умолчанию, и его можно не указывать:

    DBGrid1.Columns[0] = DBGrid1.Columns.Items[0]

    Шрифт и цвет можно менять не только в Инспекторе объектов, но и программно. Добавим на форму две кнопки, на которых напишем "Шрифт" и "Цвет". А также два диалога: FontDialog и ColorDialog. Создадим процедуру нажатия на первую кнопку и впишем в нее следующее:

    {Меняем шрифт}
    procedure TfMain.Button1Click(Sender: TObject);
    begin
      //считаем в диалог шрифта установленный шрифт
      FontDialog1.Font := DBGrid1.Columns.Items[DBGrid1.SelectedIndex].Font;
      //установим выбранный шрифт:
      if FontDialog1.Execute then
        DBGrid1.Columns.Items[DBGrid1.SelectedIndex].Font :=
          FontDialog1.Font;
    end;

    Здесь вначале мы свойству Font диалога FontDialog присвоили тот же шрифт, который был в текущем столбце сетки. Затем вызвали выбор диалога, и если пользователь выбрал другой шрифт (название, начертание, размер, цвет шрифта), то изменяем шрифт всего столбца на выбранный пользователем. Аналогичным образом меняем и цвет столбца. Создайте процедуру обработки второй кнопки, и впишите код:

    {Меняем цвет}
    procedure TfMain.Button2Click(Sender: TObject);
    begin
      //считаем в диалог цвета установленный цвет:
      ColorDialog1.Color :=
        DBGrid1.Columns.Items[DBGrid1.SelectedIndex].Color;
      //установим выбранный цвет:
      if ColorDialog1.Execute then
        DBGrid1.Columns.Items[DBGrid1.SelectedIndex].Color :=
          ColorDialog1.Color;
    end;

    Сохраните проект, скомпилируйте его и проверьте работу кнопок. И шрифт, и цвет текущего столбца будут меняться:

    (рис 12.3 ) Изменение шрифта и цвета текущего столбца

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

    DBGrid1.Columns.Items[DBGrid1.SelectedIndex].Color

    использовать

    DBGrid1.Columns.Items[DBGrid1.SelectedIndex].Title.Color

    А если применить цикл, то можно изменить параметры всех столбцов:

    for i:= 0 to DBGrid1.Columns.Count-1 do
        DBGrid1.Columns.Items[i].Color :=

    Пустые столбцы

    Если добавить в редактор столбцов сетки DBGrid новый столбец, но в свойстве FieldName не выбирать поле БД, а оставить его пустым, мы получим пустой столбец. Для чего нужны пустые столбцы в сетке? Во-первых, в них можно выводить обработанные данные из других столбцов. К примеру, пользователю неудобно просматривать три столбца "Фамилия", "Имя" и "Отчество". Ему было бы удобней просмотреть один сборный столбец в формате "Фамилия И.О.". Во-вторых, пустое поле может выводить информацию по требованию.

    Рассмотрим эти случаи.

    Создайте новый столбец, но не назначайте ему поле из НД. Выделите этот столбец, и в его свойстве Title.Caption впишите "Фамилия И.О.", а в свойстве Width укажите ширину в 150 пикселей. Эти сборные данные не будут видны в момент проектирования таблицы, они выйдут только во время работы программы.

    Столбцы "Фамилия", "Имя" и "Отчество" нам уже не нужны, скройте их, установив их свойство Visible в False. А новый столбец перетащите мышью наверх, его индекс будет равен 0.

    Нам придется написать код, который нужно вписать в событие OnDrawColumnCell сетки. Это событие наступает при прорисовке каждой ячейки столбца. Также имеется событие OnDrawDataCell, которое выполняет схожие функции, но оно оставлено для поддержки старых версий, и использовать его не желательно. Итак, выделяем сетку, генерируем событие OnDrawColumnCell и вписываем код:

    {Прорисовка таблицы}
    procedure TfMain.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
      DataCol: Integer; Column: TColumn; State: TGridDrawState);
    var
      s: String;
    begin
      //если это пустой столбец
      if Column.Index = 0 then begin
        if ADOTable1['Фамилия'] <> Null then
          s:= ADOTable1['Фамилия'] + ' ';
        if ADOTable1['Имя'] <> Null then
          s:= s + Copy(ADOTable1['Имя'], 1, 1) + '.';
        if ADOTable1['Отчество'] <> Null then
          s:= s + Copy(ADOTable1['Отчество'], 1, 1)+ '.';
        DBGrid1.Canvas.TextOut(Rect.Left, Rect.Top, s);
      end; //if
    end;

    Здесь мы вначале проверяем - наш ли это столбец (равен ли индекс нулю)? Если наш, то в переменную s начинаем собирать нужный текст. При этом имеем в виду, что пользователь мог и не заполнить некоторые поля. Чтобы у нас не произошло ошибки, вначале убеждаемся, что поле не равно Null (то есть, текст есть). Если текст есть, добавляем его в переменную s. Причем если это имя или отчество, с помощью функции Copy() получаем только первую букву и добавляем к ней точку. Когда s сформирована, добавляем этот текст в наш столбец с помощью метода TextOut() свойства Canvas сетки. В метод передаются три параметра: координаты левой позиции, верхней позиции и сам текст. Эти координаты мы берем из параметра события OnDrawColumnCell - Rect, который имеет такие свойства, как Left и Top, показывающие, соответственно, левую и верхнюю позиции текущей ячейки.

    В результате программа будет иметь вид:

    (рис 12.4 ) Заполнение пустого столбца

    Теперь о том, как использовать пустой столбец для вывода информации по требованию пользователя. Допустим, в сетке DBGrid нам нужна кнопка, нажатие на которую привело бы к выводу сообщения об образовании сотрудника.

    Создайте новый пустой столбец. Перетаскивать его не нужно, пусть будет последним. Свойство Width (ширина) установите в 20 пикселей. Название столбца ( Title.Caption ) вписывать не нужно, пусть будет пустым. В свойстве ButtonStyle выберите значение cbsEllipsis. Это приведет к тому, что при попытке редактировать этот столбец образуется кнопка с тремя точками:

    (рис 12.5 ) Кнопка в пустом столбце

    Нужный код пишется в событии OnEditButtonClick() сетки DBGrid, которое происходит всякий раз, когда пользователь нажимает на кнопку "…". Сгенерируйте это событие и впишите только одну строку:

    ShowMessage(ADOTable1['Образование']);

    Теперь, когда пользователь нажмет на эту кнопку, ему будет выведено сообщение с текстом об образовании текущего сотрудника.

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

    Для организации списков выбора служит компонент ComboBox. Однако сетка DBGrid позволяет устроить такой же список в одном из своих столбцов без использования каких-либо других компонентов. В нашем примере есть поле "Пол". Это текстовое поле из трех символов. Во время конструирования базы данных ok.mdb с помощью программы MS Access мы указывали, что это поле может хранить значения либо "муж", либо "жен". То же самое можно было сделать с помощью сетки. Откройте редактор столбцов сетки и выделите столбец "Пол". Обратите внимание на свойство PickList в Инспекторе объектов. Это свойство имеет тип TStrings, то есть представляет собой набор строк, так же как и свойство Items у компонента ComboBox. Щелкните дважды по PickList, чтобы открыть редактор, и впишите туда

    муж
    жен

    именно так, каждое значение на своей строке. Сохраните проект, скомпилируйте его и попробуйте редактировать этот столбец. При попытке редактирования в ячейке покажется похожий на ComboBox список, в котором можно будет выбрать одно из указанных значений:

    (рис 12.6 ) Список выбора в сетке

    Следует иметь в виду, что наличие такого списка не препятствует пользователю ввести какое-то иное значение. Этот список нужен не для контроля, а только для облегчения пользователю ввода данных. Если же вы не желаете, чтобы пользователь имел возможность вводить другие данные, контроль следует организовать иным способом.

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

    DBGrid1.Columns.Items[4].PickList.Add('абв');

    Выделение отдельных строк

    Очень часто в практике приходится выделять какие-то строки, изменяя их фон или цвет шрифта. Например, в бухгалтерии обычно выделяют строки, в которых значение меньше нуля. Допустим, ваша программа показывает клиентов, а какой-то столбец содержит их баланс на счету вашей компании. Если этот баланс меньше 0, значит, клиент имеет задолженность перед вашей фирмой. Бухгалтеру будет очень удобно, если дебиторы (должники) будут выделяться в общем списке красным цветом.

    Способ прорисовки данных в сетке DBGrid зависит от значения ее свойства DefaultDrawing. По умолчанию свойство равно True, то есть данные прорисовываются автоматически. Если свойство содержит False, то прорисовку придется кодировать самостоятельно в свойствах OnDrawColumnCell или OnDrawDataCell, о которых уже упоминалось в этой лекции.

    Если мы написали алгоритм прорисовки, но свойство DefaultDrawing содержит True, то вначале сетка заполнится данными автоматически, а затем будет выполнен наш алгоритм. Другими словами, прорисовка некоторых частей сетки будет выполнена дважды. Это не очень хорошо для быстродействия программы, однако нам придется поступать именно так: ведь мы не все строки и столбцы собираемся выводить другим способом, а только некоторые. Остальные будут заполнены данными по умолчанию.

    Разберем этот метод подробней. Если найти его в справке Delphi, то увидим:

    property OnDrawColumnCell: TDrawColumnCellEvent;

    То есть, этот метод имеет тип TDrawColumnCellEvent. Описание типа такое:

    type TDrawColumnCellEvent = procedure (Sender:TObject; const Rect:TRect; 
    DataCol:Integer; Column:TColumn; State:TGridDrawState) of object;

    Разберемся с параметрами.

    Rect - координаты прорисовки.

    DataCol - порядковый номер текущего столбца (начиная с 0).

    Column - данные текущего столбца.

    State - состояние ячейки. Может быть:

  • gdSelected - ячейка выделена
  • gdFocused - фокус ввода в ячейке
  • gdFixed - ячейка - заголовок столбца.
  • Допустим, в нашем примере требуется, чтобы строки с военнообязанными сотрудниками выделялись красным цветом:

    (рис 12.7 ) Выделение строк

    Заметим сразу, что наличие пустых столбцов создает дополнительные, но решаемые проблемы. Код события OnDrawColumnCell придется переделать, он будет таким:

    {Прорисовка таблицы}
    procedure TfMain.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
      DataCol: Integer; Column: TColumn; State: TGridDrawState);
    var
      s: String;
    begin
    
      with DBGrid1.Canvas do begin
        //поле "Военнообязанный" содержит истину?
          if (ADOTable1['Военнообязанный'])= True and
             not (gdSelected in State) then begin
            //выводим все ячейки строки красным по белому:
            Font.Color:= clRed;
            FillRect(Rect);
          end; //if
    
      //если это пустой сборный столбец
      if Column.Index = 0 then begin
        if ADOTable1['Фамилия'] <> Null then
          s:= ADOTable1['Фамилия'] + ' ';
        if ADOTable1['Имя'] <> Null then
          s:= s + Copy(ADOTable1['Имя'], 1, 1) + '.';
        if ADOTable1['Отчество'] <> Null then
          s:= s + Copy(ADOTable1['Отчество'], 1, 1)+ '.';
        DBGrid1.Canvas.TextOut(Rect.Left, Rect.Top, s);
      end //if
      //если это пустой столбец с кнопкой "..."
      else if Column.Index = 6 then begin
        DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State);
        Exit;
      end //if
      //все остальные столбцы
      else
        TextOut(Rect.Left+2, Rect.Top+2, Column.Field.Text);
      end; //with
    end;

    Разберемся с кодом. Вначале с помощью with мы указываем, что будем работать непосредственно со свойством DBGrid1.Canvas, которое отвечает за стиль прорисовки ячейки. Далее мы смотрим, содержится ли True в поле "Военнообязанный" текущей записи. Если да, то указываем, что цвет шрифта должен быть красным, а затем функцией FillRect(Rect) мы стираем стандартный вывод.

    Далее мы определяем, прорисовывается ли в данный момент пустой сборный столбец с "Фамилия И.О.". Если это он, то мы формируем переменную s с нужными данными и выводим их, как в прошлый раз.

    Если же это пустой столбец с кнопкой "…", то мы делаем стандартный вывод и выходим из процедуры. Если мы этого не сделаем, то получим ошибку программы.

    Все остальные столбцы мы выводим строкой

    TextOut(Rect.Left+2, Rect.Top+2, Column.Field.Text);

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

    (рис 12.8 ) Некорректная прорисовка

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

    Сделать проверку на тип данных можно, например, так:

    //если текст, сдвинем только на 2 пикселя
    if Column.Field.DataType = ftString then
      TextOut(Rect.Left, Rect.Top+2, Column.Field.Text)
    //если цифры, сдвинем их вправо на 28 пикселей
    else TextOut(Rect.Left+28, Rect.Top+2, Column.Field.Text);

    Выделение строки красным текстом может оказаться недостаточным для заказчика. Что, если он потребует, чтобы эти строки выделялись красной строкой с белым шрифтом? Тогда вместо

    Font.Color:= clRed;
    FillRect(Rect);

    вам придется написать

    //выводим все ячейки строки белым текстом по красному фону:
    Brush.Color:= clRed;
    Font.Color:= clWhite;
    FillRect(Rect);

    Как видите, подсвойство

    DBGrid1.Canvas.Brush.Color

    отвечает за цвет заливки ячейки, а

    DBGrid1.Canvas.Font.Color

    за цвет выводимого в ней шрифта. Список доступных цветов вы можете открыть в любом свойстве Color любого компонента. Теперь вы сможете создать сетку со сложными элементами прорисовки.

    Вернуться к учебному плану