уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

С появлением в Delphi XE2 FireMonkey иногда ощущаю себя прям каким-то первоклассником =) Не сказать, чтобы прям уж так совсем все непонятно и сложно…скорее немного не привычно использовать новые FMX-контролы. Вот и сегодня от темы статьи так и напоминает что-то из разряда «Delphi для начинающих». Но, как говориться, из песни слов не выкинешь — раз решил поработать с FireMonkey, то начинать надо с простых вещей. Ну, а так как в статье про SQLite  для Delphi XE2 было предложение расписать работу с Grid’ами в FMX, то попробуем написать небольшое приложение, которое будет заполнять табличку на основании запроса к базе SQLite.

В Delphi XE3 имеется поддержка SQLite через DBExpress, а также значительно усовершенствован механизм LiveBindings, который как раз можно использовать для заполнения таблиц данными из БД. Более подробную информацию по SQLite и LiveBindings в Delphi XE3 Вы можете узнать из статей:

Статья устарела в связи со значительными изменениями в библиотеке FireMonkey (FMX)

И в начале несколько слов про SQLite в Delphi, а точнее про ту обертку с которой я буду работать. Дело в том, что это, наверное, самая простая обертка из всех, что мне встречались (собственно этим она мне и понравилась). Здесь нет компонентов, все объекты в модуле SQLiteTable3 — это классы-наследники от TObject, а вся работа напрямую связана с запросами к библиотеке SQLite (в Win-приложениях — это будет sqlite3.dll). Естественно, такая организация работы с SQLite накладывает некоторые ограничения на работу с БД SQLite. Например, я не могу взять и без лишних заморочек использовать TDataSet или использовать также просто как и при работе, например, с MSSQL DBGrid — придется искать свое решение, использовать TClientDataSet’ы и т.д. Но, мне такие решения в принципе не нужны были. Единственный раз когда мне приходилось использовать TDataSet, TDBGrid и т.д. был лет эдак пять-семь назад и то всё это «творчество» тогда использовало BDE.  В общем, если у кого-то возникнет желание/потребность связать эту обертку с TDataSet — знайте, что сделать это просто и быстро врядли получится. Ну, а я рассмотрю пример того, как, используя SQLiteTable2.pas можно формировать таблицы в приложении Firemonkey.

В качестве примера, воспользуемся базой данных, в которой будет содержаться таблица, рассмотренная в предыдущей статье про SQLite:

CREATE TABLE [TestTable] (
  [id] INTEGER PRIMARY KEY AUTOINCREMENT, 
  [StringRow] TEXT, 
  [NumberRow] INTEGER, 
  [DateTimeRow] DATETIME);

Теперь посмотрим, что нам необходимо знать для того, чтобы заполнить таблицу.

Для того, чтобы заполнить таблицу на форме (не важно какую — из FMX или VCL) нам необходимо знать помимо значений полей набора данных ещё как минимум два значения: количество полей (столбцов) и количество записей (строк) в таблице. Иначе мы просто не сможем правильно установить количество строк и столбцов у нашего контрола на форме.

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

Для примера создадим новое приложение «Firemonkey HD Application» и разместим на форме следующие элементы:

  1. TMemo — здесь мы будем просматривать/редактировать SQL-запросы
  2. TStringGrid (со вкладки Grids) — сюда будем выводить результат выполнения запроса
  3. 2 TButton для выполнения операции подключения к БД и выполнения SQL-запроса
  4. TComboBox — для хранения списка таблиц БД
  5. TOpenDialog — для выбора файла БД.
Внешний вид приложения у меня получился вот такой:
При нажатии на кнопку «Connect» мы будем подключаться к базе данных и считывать из неё названия всех таблиц. Я предложил использовать БД из предыдущей статьи, но фактически приложение сможет работать с любой указанной вами БД, поэтому списочек получать мы будем так:
  • Подключаем в uses модули
uses
  [..], SQLite3, SQLiteTable3;
  • создаем в секции private класса формы следующие переменные:
type
  TForm1 = class(TForm)
    [...]
  private
    FBase: TSQLiteDatabase;
    FStmt: TSQLitePreparedStatement;
    FTable: TSQLiteUniTable;
  public
 
  end;
  •  На OnClick кнопки «Connect» пишем
 if dlgOpenBase.Execute then
    begin
      try
        FBase:=TSQLiteDatabase.Create(dlgOpenBase.FileName);
        FBase.GetTableStrings('SELECT name FROM sqlite_master WHERE type="table" ORDER BY name',cbTables.Items);
        ShowMessage('Подключились. Названия таблиц прочитали');
      except
        ShowMessage('Не смогли подключиться к базе данных');
      end;
    end;

Здесь стоит отметить следующие моменты.
Во-первых, что за таблица sqlite_master к которой я так смело обратился? Это специальная таблица, которая содержится в любой БД SQLite и содержит сведения по базе данных. В моем случае я выбрал из sqlite_master только те записи, в которых содержатся сведения по таблицам БД, т.е. проверил значение поля type на присутствие в нем значения «table«. Можно было бы запросить и всю таблицу целиком. sqlite_master выглядит следующим образом:

CREATE TABLE sqlite_master (
  TYPE TEXT,
  name TEXT,
  tbl_name TEXT,
  rootpage INTEGER,
  SQL TEXT
);

Более подробно про эту таблицу можно почитать в официальном FAQ.
Во-вторых, стоит немного рассказать про метод GetTableStrings у TSQLiteDatabase. Дело в том, что не важно какого содержания SELECT вы отправите к базе данных, этот метод всегда вернет строковые значения из первого столбца (с индексом 0). Ну, а так как у меня результат запроса и содержит всего одно поле, то я без лишних проверок записал все значения в ComboBox.

Следующий момент — это отправка запроса к произвольной таблице БД. Здесь все довольно просто. Например, на OnChange ComboBox’а можно написать такой код:

TableName:=;
  FStmt:=TSQLitePreparedStatement.Create(FBase,'SELECT * FROM '+ComboBox1.Items[cbTables.ItemIndex]);
  FTable:=FStmt.ExecQuery;
  Memo1.Lines.Clear;
  Memo1.Lines.Add(FStmt.SQL);

То есть после выбора названия таблички в ComboBox’е просто считываем все значения.
Ну, а теперь подходим к главному. Получили мы все, что хотели из БД, а как теперь эти значения «вытянуть» на наш StringGrid? Вот здесь мы сделаем небольшое отступление от работы над приложением и проясним некоторые моменты по поводу FMX.


StringGrid в FireMonkey

Помните в начале статьи я сказал, что работать с контролами в FMX мне несколько непривычно? Про это я немного говорил в «Знакомстве с FireMonkey«, теперь же постараюсь рассказать более подробно.

Итак, раз вы (и я с вами) решили использовать в проекте FireMonkey, то первое, что стоит для себя уяснить — это то, что несмотря на схожесть названий контрол в FMX — это вообще не тоже самое, что контрол в VCL. У них общего столько же сколько у мопеда и мотоцикла Harley Davidson — оба на двух колесах.

Например, все, что общего у двух TButton — это родитель TComponent.

В FMX любой компонент — это контейнер. Контейнер может содержать в себе ещё один контейнер, а в том ещё 5 контейнеров и т.д. и при этом чем сложнее контрол — тем сложнее иерархия объектов.  Все компоненты FMX собираются из базовых элементов, таких как TLayout, TRectangle и т.д. Вот она та самая «необычность» к которой надо привыкать в случае работы с FireMonkey.

Теперь, что касается StringGrid в FMX. Чтобы разобраться из чего состоит таблица обратимся к StyleBook. Бросаем на форму компонент TStyleBook, дважды кликаем по нему мышкой и в дереве ищем запись gridstyle:

Как видите в таблице FMX заголовок таблицы (header) и основное содержимое (content) — это разные элементы. То есть мы можем изменять стиль заголовков, не затрагивая при этом стиль строк таблиц. Но до этого нам пока ещё рано — надо разобраться как вообще управляться с таблицей.

Для того, чтобы добавить в таблицу новый столбец, достаточно дважды кликнуть по таблице на форме и в редакторе нажать кнопку AddItem. Например, на рисунке ниже представлен редактор в котором я добавил в таблицу пять столбцов и вид окна Structure, чтобы дать вам небольшое предстовление о том, что в итоге получится, так сказать «концепция контейнеров в действии»:


Можно через тот же редактор сделать и так, что каждый столбец таблицы будет содержать в себе ещё с десяток столбцов, но в визуальном плане это никак не отразиться — таблица в приложении так и будет двухмерная. Для каждого столбца таблицы мы можем задать свои особые свойства, например, для нас сегодня будет важно свойство Header:string — заголовок столбца.

Что касается ячеек таблицы, то каждый столбец (TStringColumn) содержит поле FCells, которое представляет собой массив строк. Свойства для этого поля нет, а вся работа с ячейками и строками осуществляется в родителе, т.е. с TStringGrid. И здесь отличий никаких в названиях свойств и методов от VCL нет. Например, чтобы указать количество строк, надо указать значение свойства RowCount:

  MyStringGrid.RowCount:=10;

А чтобы указать значение для ячейки, воспользоваться свойством Cells:

  MyStringGrid.Cells[0,0]:='Ячейка 0,0'

При этом помните, что заголовок таблицы и содержимое таблицы — разные элементы (см. структуру компонента выше). То есть значение индексов строк в VCL и FMX.StringGrid’ах будут различаться на 1 (см. рисунок ниже):

Двигаемся далее. Раз решал рассмотреть свойства, то стоит отметить, что у FMX.StringGrid нет свойства ColCount для изменения количества столбцов в runtime. Вместо него есть свойство только для чтения под названием ColumnCount. Но это отнюдь не означает, что мы не в состоянии поменять количество столбцов у FXM.StringGrid в runtime. Сделать это можно, например так:

//удаляем все столбцы из таблицы
  for I := StringGrid1.ColumnCount-1 downto 0 do
    StringGrid1.Columns[i].Release;
//добавляем 4 новых столбца
for i:=1 to 4 do
  StringGrid1.AddObject(TStringColumn.Create(nil));

В целом это все особенности, которые нам необходимо уяснить для дальнейшей работы с FMX.StringGrid. Резюмируем и двигаемся обратно в наше приложение:

  1. Количество строк меняется также как и в VCL — через свойство RowCount
  2. Строка с индексом 0 в FMX.StringGrid — это то же самое, что строка с индексом 1 в VCL, т.к. заголовок таблицы и набор строк — разные элементы компонента.
  3. У FMX.StringGrid нет свойства ColCount — изменение количества столбцов необходимо проводить через работу со свойством Columns и добавление новых столбцов как дочерних элементов, используя метод AddObject.

Краткий экскурс в FMX.StringGrid закончили — продолжим работу над приложением. Нам осталось только правильно заполнять табличку и научить программу выполнять произвольные запросы. Начнем с таблицы и напишем метод, с помощью которого будет строится таблица с данными из запроса:

procedure TForm1.DrawTable(ATable: TSQLiteUniTable);
var i:integer;
    delta: integer;
begin
  //смотрим сколько надо удалить/добавить столбуов
  delta:=StringGrid1.ColumnCount-ATable.FieldCount;
  if delta>0 then //в таблице столбцов больше чем надо - удаляем
    begin
      for I := 1 to delta do
        StringGrid1.Columns[StringGrid1.ColumnCount-1].Release;
    end
  else //не хватает столбуов - добавляем
    begin
      for I := 1 to abs(delta) do
        StringGrid1.AddObject(TStringColumn.Create(nil));
    end;
  StringGrid1.RowCount:=0;//обнуляем количество строк
try
  StringGrid1.BeginUpdate;
  //заполняем табличку данными
  while not ATable.EOF do
    begin
      {увеличиваем количество строк на 1}
      StringGrid1.RowCount:=StringGrid1.RowCount+1;
      for I := 0 to ATable.FieldCount-1 do
        begin
        {задаем заголовок столбца}
        StringGrid1.Columns[i].Header:=ATable.Fields[i].Name;
        {выписываем в ячейку последнее значение}
        StringGrid1.Cells[i,ATable.Row-1]:=ATable.FieldAsString(i);
        end;
      ATable.Next
    end;
finally
  StringGrid1.EndUpdate;
end;
end;

Здесь мы вначале определяем сколько столбцов надо добавить/удалить из таблицы. Затем приводим таблицу в исходное состояние, т.е. устанавливаем необходимое количество столбцов и строк. И наконец, проходимя по каждой записи в TSQLiteUniTable выводим значения полей в соответствующие ячейки FMX.StringGrid.

Теперь остается только «навесить» необходимое событие на кнопку «Выполнить SQL» и вызвать только что созданный метод. Обработчик OnClick кнопки будет следующим:

if not Assigned(FStmt) then
  FStmt:=TSQLitePreparedStatement.Create(FBase);
FStmt.SQL:=Memo1.Text;//запомнили и подготовили запрос
FTable:=FStmt.ExecQuery();//выполнили запрос и получили объект-таблицу
DrawTable(FTable)//передали таблицу в метод и отрисовали StringGrid

Ну и в заключение вид приложения в котором показана таблица FMX.StringGrid, содержащая ту самую 1000 записей, которые мы добавляли в прошлый раз:

На отрисовку таблички ушло чуть больше 1 секунды.

Скачать исходник: Исходники —> Прочие

Книжная полка

Автор: Дмитрий Осипов
Название:Базы данных и Delphi. Теория и практика
Описание Книга основана на материалах лекций и практических занятий, разработанных автором, и объединяет теоретические основы и практические аспекты разработки реляционных баз данных.
Купить на ЛитРес
Автор: Анатолий Хомоненко, Владимир Гофман
Название:Работа с базами данных в Delphi
Описание: Рассматривается использование средств Delphi для разработки приложений баз данных. Даются понятия баз данных, характеризуются элементы и описываются этапы проектирования реляционных баз данных, изложена технология разработки информационных систем, освещаются приемы работы с данными, создание таблиц и приложений баз данных, подготовка отчетов.
Купить на ЛитРес

 

 

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
10 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
angryvitum
angryvitum
11/10/2011 15:00

Спасибо за материал! WebDelphi — один из лучших блогов, по-моему, по Delphi-тематике!

Теперь по материалу поста: логичнее было бы, если бы была возможность, хотя бы через LiveBinding, «привязать» поле набора данных к столбцу грида. Или в данном конкретном случае это особенность TSQLiteDatabase (TSQLiteTable), не являющихся потомками TDataset?

Георгий
Георгий
11/10/2011 15:52

По поводу мотоциклов. А то что выше TComponent включая TComponent это FMX или VCL ?

Георгий
Георгий
12/10/2011 15:06

Ок, спасибо за ответ

elpik
elpik
13/10/2011 12:08

Спасибо за статью очень понравилась :) У меня стойкое ощущение дежавю, т.к. X-лет назад сам занимался заполнением TStringGrid данными из запросов, воспоминания прям нахлынули :)
А почему не захотели использовать LiveBindin, если не секрет?

yura
yura
04/01/2012 16:56

У меня вопрос, как можно узнать у какой ячейке сейчас фокус в StringGrid текущую колонку я получаю, а вот  строку не могу. Заранее спасибо
 

systopler
systopler
11/03/2012 18:59
При добавлении строки:
      {увеличиваем количество строк на 1}
      StringGrid1.RowCount := StringGrid1.RowCount + 1;
у меня удаляются значения в ячейках.
Как лечить? 
timk
timk
08/08/2012 15:54

yura,
var
CurrCell:string;
begin
CurrCell:=StringGrid.Cell[Stringgrid.ColumnIndex, StringGrid1.Selected];
end;