среда, 27 ноября 2013 г.

Борьба с (WIDEMEMO) в DBGrid

При попытке вывести данные из БД в DBGrid, есть одна проблема. Данные в DBGrid могут отображаться как (WIDEMEMO). Что бы это подправить надо внести изменения в одну из двух процедур отрисовки.

procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;
Field: TField; State: TGridDrawState);
var
  Grid : TStringGrid;
  Texto : String;
  Rectangulo : TRect;
begin
  Rectangulo:=Rect;
  Grid := TStringGrid(Sender);
  if Field.IsBlob then begin
    Grid.Canvas.FillRect(Rect);
    Texto := Field.AsString;
    DrawText( Grid.Canvas.Handle,
                      PChar(Texto),
                      StrLen(PChar(Texto)),
                      Rectangulo,
                      DT_WORDBREAK);
    end;
end;

Или вот другой вариант

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
 if Assigned(Column) then
  begin
   DBGrid1.Canvas.FillRect(Rect);
   DBGrid1.Canvas.TextRect(Rect, Rect.Left, Rect.Top, ' '+Column.Field.AsString);
  end;
end;

[SQLite] Отображение данных SQLite в DBGrid (Способ 1)

После Delphi 7, не смог сразу отобразить данные из базы SQLite в DBGrid. Необходимы дополнительные манипуляции.
В проекте который я пишу, мне нужно просто просматривать данные из базы в виде таблицы. Сделаем это. Есть несколько способов.

Способ 1. Используем провайдер


На форме у нас есть уже

  • DBServer(TSQLConnection),
  • SQLQuery1(TSQLQuery).

Они связаны и настроены (см. Подключаемся к базе данных (Delphi XE3 + SQLite3)).
Кроме этого необходимо задать запрос на отображение всех данных. У свойства SQL компонента SQLQuery1 прописываем следующую строку
SELECT * FROM myBid;
<myBid> - имя таблицы в базе.
Устанавливаем свойство Active в True.

Кидаем на форму DBGrid. Как я уже писал, просто так не получится его прикрутить. Необходимо использовать провайдер. Для этого нам потребуется несколько компонентов:

  • DataSetProvider1(TDataSetProvider),
  • ClientDataSet1(TClientDataSet),
  • DateSource(TDateSource).

У DataSetProvider устанавливаем DataSet = SQLQuery1
У ClientDataSet1 устанавливаем ProviderName = DataSetProvider1, Active=True
У DataSource устанавливаем DataSet = ClientDataSet1
И наконец у DBGrid устанавливаем DataSource = DataSource1

Можно все сделать в дизайнере, сразу увидите результат. Единственное, что при изменении данных в базе, чтоб они изменились в таблице DBGrid (в режиме дизайнера) надо выключить и включить ClientDataSet1.

Связать можно и кодом, например в отдельной процедуре.

procedure LinkDBGridToBase;
begin
  SQLQuery1.SQL.Clear;
  SQLQuery1.SQL.Text :='SELECT * FROM '+TableOfBase;
  SQLQuery1.Open;

  DataSetProvider1.DataSet := SQLQuery1;
  ClientDataSet1.ProviderName := 'DataSetProvider1';
  ClientDataSet1.Active := true;
  DataSource1.DataSet := ClientDataSet1;
  DBGrid1.DataSource := DataSource1;
end;

Обратите внимание, ClientDataSet1.ProviderName является строковым параметром.

P.S. Если в полях отображается (WIDEMEMO), есть пару решений. См. Борьба с (WIDEMEMO) в DBGrid

[SQLite] Подключаемся к базе данных SQLite3 (Delphi XE3 + SQLite3)

Для реализации задач, нам необходима база данных. Желательно бесплатная и простая. И выбрал я SQLite.

Плюсы:

  1. Бесплатный и открытый исходный код
  2. Не нуждается в дополнительной настройке на клиентской машине, надо только одну DLL
  3. Очень быстрый
  4. Возможность работать в режиме только чтение и гостевого аккаунта
  5. Не записывает ни чего в реестр, и другие файлы
  6. Поддерживается стандартного SQL ( http://www.sqlite.org/lang.html )

Как я упомянул выше, для разработки и работы программы с базой данных нам нужна одна единственная sqlite3.dll. Она должна находиться в папке с программой, а так же в директории XE3 (RAD Studio\10.0\bin). Я еще добавил ее в Windows\system32.

Из компонентов Delphi нам понадобятся:
ActionList1(TActionList)
DBServer (TSQLConnection)
SQLQuery1(TSQLQuery)

Настройку компонентов можно сделать из дизайнера, а можно вручную.

Создадим процедуру инициализации соединения с БД через DBServer.

procedure InitialConnection(ASQLConnection: TSQLConnection; AFileName: string);
begin
  ASQLConnection.ConnectionName := 'SQLITECONNECTION';
  ASQLConnection.DriverName:='Sqlite';
  ASQLConnection.LoginPrompt:= false;
  ASQLConnection.Params.Values['Host']:='localhost';
  ASQLConnection.Params.Values['FailIfMissing']:='False';
  ASQLConnection.Params.Values['ColumnMetaDataSupported']:='False';
  ASQLConnection.Params.Values['Database']:=AFileName;
  ASQLConnection.Open;
end;

ASQLConnection.Open открывает базу, если файла нет, то создает. Если же в указанную папку нельзя записать, возникнет ошибка. Так как это не прямая функция, то обрабатывать ошибки будет вызывающая процедура (acDBServerExecute).

Для передачи SQL запросов используем SQLQuery1. Но перед использованием свяжем его с нашим соединением DBServer.
SQLQuery1.SQLConnection := DBServer;
Все, можно отправлять запросы. Так например нам надо создать таблицу в базе, если ее нет (первый раз открываем базу). Давно в исходниках INIFiles подсмотрел такой вид процедур, которые начинаются на MayBeХххх(). Как я понимаю, это процедуры которые может быть что-то делают. Так вот я использую такую процедуру для создания таблицы в безе. если ее нет.

procedure MayBeCreateTables(AQuery: TSQLQuery);
begin
  AQuery.SQL.Clear;
  AQuery.SQL.Text :='CREATE TABLE if not exists MyBids ( '+
                    'id INTEGER NOT NULL PRIMARY KEY,'+
                    'WellNo CHAR(10),'+
                    'Field CHAR(20),'+
                    'WellZaboy CHAR(10),'+
                    'WellDiam CHAR(10),'+
                    'WellRastvor CHAR(20),'+
                    'DateDone DATE,'+
                    'TimeDone TIME,'+
                    'DateConfirm DATE,'+
                    'TimeConfirm TIME,'+
                    'Task TEXT,'+
                    'Zakazhik TEXT,'+
                    'Predstavitel TEXT,'+
                    'Partia TEXT,'+
                    'Comment TEXT,'+
                    'DateRecieve DATE,'+
                    'TimeRecieve TIME,'+
                    'Status TEXT,'+
                    'DateChange DATE,'+
                    'TimeChange TIME'+
                    ');';
  AQuery.ExecSQL;
end;

Ключевым словом здесь является  if not exists в SQL запросе. Таким образом SQLite сам проверяет есть ли такая таблица и если нет, то создает. Спасибо ему за это.

Осталось собрать все в кучу и обработать возможные ошибки.

В ActionList добавим новое действие в категорию DB и назовем его acDBServerStart. В нем мы будем запускать наш сервер БД.

procedure TForm1.acDBServerStartExecute(Sender: TObject);
begin
  try
   InitialConnection(DBServer, 'C:\DB\bids.db');
   SQLQuery1.SQLConnection := DBServer;
   MayBeCreateTables(SQLQuery1);
 except
    on E: Exception do
    begin
      ShowMessage( StringReplace(E.Message, #13, ' ', [rfReplaceAll]);
      //raise;
    end;
  end;
end;

//raise - закоментировал, что бы не отображались ошибки. Их будем записывать в лог.

Таким образом мы настроили соединение, соединились и на случай отсутствия таблицы создали ее. Можно добавлять данные в таблицу.