Глава 4. Компоненты разработки клиентской части


Общие сведения

Во фреймворке ONTARIO1 при разработке форм клиентского приложения необходимо использовать следующие компоненты закладки ONTARIO:

Табл.4.1.

Тип отображаемых данных Компонент Delphi Примечание
Строки до 255 символов TDBOMaskEdit Форматы масок отображения совпадают с форматами TMaskEdit
Числа и денежные величины TCurrencyEdit Форматы масок отображения совпадают с форматами TMaskEdit
Дата TDateEdit  
Объект БД TDBOField  
Таблицы, включающие в себя объекты БД TDBODrawGrid,
TDBObjGrid
TDBODrawGrid (только для отображения), TDBObjGrid (отображение и редактирование)

Все компоненты связи с БД являются встроенными процедурами (TStoredProc), настраиваемыми на компонент TDatabase главной формы dbOntario и работающими через это единое соединение. Компоненты TStoredProc должны быть в неактивном состоянии, а их инициализация единообразно описывается в переопределяемом методе InitDataEnvironment (см пример ниже для spTDBODoc_View).

TDBOField

Свойства

Компонент отображает объект БД с названием и пиктограммой. Для инициализации компонента используется ключевое свойство Value, содержащее текущий идентификатор объекта БД (OID). Например:

procedure TfrmDBODocEdit.InitDataEnvironment( DBOInfo: TDBOInfo );
begin
  inherited;
  spTDBODoc_View.Active := false;
  spTDBODoc_View.ParamByName( '@OID' ).Value := DBOInfo.OID;
  spTDBODoc_View.Open;
  edtState.Value := spTDBODoc_View.FieldByName( 'StateID' ).AsInteger;
  edtStateDate.Date := spTDBODoc_View.FieldByName( 'LastStateDate' ).AsDatetime;
  edtUser.Value := spTDBODoc_View.FieldByName( 'CreatorID' ).AsInteger;
  edtStore.Value := spTDBODoc_View.FieldByName( 'StoreID' ).AsInteger;
  edtWhere.Value := spTDBODoc_View.FieldByName( 'WhereID' ).AsInteger;
  edtDate.Date := DBOInfo.CreationDate;
  edtNo.Text := DBOInfo.Ext;
end;

В данном примере, кроме инициализации компонента edtState, показано также применение содержания информационной структуры DBOInfo, присущей каждой форме, унаследованной от TfrmBaseDialog и каждому классу, унаследованному от TDBObject.

CanSelect: boolean

SFolderName: string

Разрешает или запрещает выбор объекта из всплывающего диалогового окна поддерева объектов БД. При этом минимально допустимый класс объекта необходимо занести в свойство DBOClass, а название системной папки, в которую попадает пользователь при выборе из диалогового окна объекта, в SFolderName. Если он не указан, то выбор начинается с корневой вершины системы. Подробнее о системных папках написано в руководстве по разработке модуля.

DBOInfo: TDBOInfo

Структура содержит базовую информацию об объекте, содержащемся в поле в данный момент времени. Структура описана в DBOConst.pas.

Методы

SetFieldByOID( OID: variant )

Аналогичен по действию простому присваиванию идентификатора объекта свойству Value, но может принимать значения типа Variant. Используется во избежание ошибки инициализации, если заранее неизвестен тип инициализирующего значения.

События

OnChangeDBO

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

OnSelectDBO

Происходит при выборе объекта БД, из появляющегося диалогового окна поддерева объектов. Корень поддерева определяется свойством SFolderName. Передает в качестве параметра DBOInfo выбранного объекта.

TDBODrawGrid

Не используется в формах, является суперклассом для TDBObjGrid, реализующим его базовые возможности.

TDBObjGrid

Компонент отображает таблицу, которая может включать в себя одну или более колонок, содержащих объекты БД с пиктограммой. В runtime позволяет по нажатию правой кнопки мыши на ячейке объекта получить список его интерфейсных методов в контекстном всплывающем меню.

TDBObjGrid как потомок TDBGrid, имеет источник данных TDataSource, который, в свою очередь должен быть связан с компонентом TStoredProc, непосредственно заполняющим таблицу данными выборкой из БД. Если Grid предполагает редактирование, необходимо добавить к заполняющей его TStoredProc связанный компонент TSQLUpdate (пустой, без SQL–операторов), а ее свойство CachedUpdates установить в true.

Свойства

CanDelete: boolean

Разрешает/запрещает удаление строк в Grid по нажатию Ctrl+Del.

CanInsert: boolean

Разрешает/запрещает добавление строк в Grid по нажатию Ctrl+Ins.

CanChoice: boolean

Разрешает/запрещает вызов диалогового окна поддерева выбора объекта после нажатия Ctrl+Ins.

DBOClass: string

Определяет минимально допустимый класс добавляемого по Ctrl+Ins объекта. По умолчанию это TDBObject.

LogActions[ i ]: integer

Индексированное свойство доступа к типу i–го действия над строками DataSet данного Grid, зарегистрированного в журнале.

LogItems

Число записей в журнале действий над строками Grid и записями DataSet соответственно. Записи в журнале нумеруются от 0 до LogItems-1.

LogOIDs[ i ]: integer

Индексированное свойство доступа к OID по i–му действию над строками DataSet данного Grid, зарегистрированному в журнале.

SFolderName: string

Определяет корень поддерева (системную папку) диалогового окна выбора объекта.

ShowClassIcons: boolean

Разрешает/запрещает вывод пиктограммы класса объекта БД.

Методы

AddDBOCellInfo( <OID_Column_Name>, <BaseClass_Column_Name>, <DBOName_Column_Name>, <SFolder_Name> )

Заносит в Grid информацию об именах информативных колонок таблицы выборки (TDataSet), возвращаемой процедурой. Здесь:

<OID_Column_Name> имя поля (колонки), содержащей идентификаторы объектов БД (OID);

<BaseClass_Column_Name> имя поля, содержащего наименование класса объекта БД;

<DBOName_Column_Name> – имя поля, содержащего наименование объекта БД;

<SFolder_Name> – имя системной папки, на которую будет установлено поддерево выбора объекта БД при изменении содержимого ячейки. Заполнение этого параметра автоматически позволит по двойному щелчку мыши на ячейке с объектом БД вызывать диалог выбора объекта.

AddLogAction( <OID>, <Action_Mode> )

Добавляет в журнал операций (действий над содержимым Grid) информацию о произведенном действии.

<OID> – первичный ключ записи в DataSet данного Grid;

<Action_Mode> – вид действий над содержанием Grid. Является одной из констант: DBG_INSERTED, DBG_CHANGED, DBG_CHANGED (при добавлении, модификации и удалении записи соответственно)

ClearLogAction

Очищает содержимое журнала действий.

События

OnDBOCellEdit( Sender: TObject )

Происходит при редактировании ячейки с объектом БД (при двойном щелчке мыши).

OnDeleteDBO( Sender: TObject )

Происходит при удалении строки из DataSet по нажатию Ctrl+Del, если удаление разрешено в CanDelete.

OnHeaderDblClick(Column: TColumn)

Происходит при двойном щелчке мыши на заголовке колонки. Колонка передается в качестве параметра.

OnInsertDBO( Sender: TObject )

Происходит при добавлении строки в DataSet по нажатию Ctrl+Ins, если удаление разрешено в CanInsert.

OnSelectCellDBO( Sender: TObject; DBOInfo: TDBOInfo )

Происходит после выбора объекта из поддерева в диалоговом окне при редактировании ячейки, содержащей объект БД. DBOInfo содержит информацию о выбранном объекте.

OnSelectlDBO( Sender: TObject; DBOInfo: TDBOInfo )

Происходит после выбора объекта из поддерева в диалоговом окне при добавлении строки в DataSet по Ctrl+Ins. DBOInfo содержит информацию о выбранном объекте.

OnSelectObject

Происходит при выборе объекта из окна Проводника. Используется в общесистемных формах.

Пример разработки формы с применением TDBObjGrid

В качестве примера прокомментируем разработку формы редактирования комплекта TfrmDBOSetEdit (файл SetEdit.pas).

unit SetEdit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ThngEdit, DB, DBTables, StdCtrls, Mask, DBOMask, Buttons, DBOBtn, Grids,
  DBGrids, DBODrGrd, DBOGrid, DBOConst, ExtCtrls, DBCtrls;

type
  TfrmDBOSetEdit = class(TfrmDBOThingEdit)
    dsrcGoods: TDataSource;
    spTDBOSet_ViewContents: TStoredProc;
    UpdateSQL1: TUpdateSQL;
    spTDBOSet_InclObj: TStoredProc;
    dbgGoods: TDBObjGrid;
    spTDBOSet_ExclObj: TStoredProc;
    spTDBOGood_View: TStoredProc;
    spTDBObject_View: TStoredProc;
    procedure spTDBOSet_ViewContentsAfterPost(DataSet: TDataSet);
    procedure dbgGoodsSelectDBO(Sender: TObject; DBOInfo: TDBOInfo);
    procedure dbgGoodsDeleteDBO(Sender: TObject);
    procedure dbgGoodsSelectCellDBO(Sender: TObject; DBOInfo: TDBOInfo);
    private
   { Private declarations }
  public
  { Public declarations }
    procedure InitDataEnvironment( DBOInfo: TDBOInfo ); override;
    function ValidCheck: boolean; override;
    procedure SaveDataEnvironment; override;
end;

var
  frmDBOSetEdit: TfrmDBOSetEdit;

implementation

{$R *.DFM}

procedure TfrmDBOSetEdit.InitDataEnvironment( DBOInfo: TDBOInfo );
var
  i: integer;
  Part : integer;
begin
  inherited;

  dbgGoods.AddDBOCellInfo( 'OID', 'BaseClass', 'Name', '' );
  dbgGoods.AddDBOCellInfo( 'MeasureID', 'MeasureClass', 'MeasureName', 'MeasureSec' );

  spTDBOSet_ViewContents.Active := false;
  spTDBOSet_ViewContents.ParamByName( '@OID' ).AsInteger := DBOInfo.OID;
  spTDBOSet_ViewContents.Open;

  with dbgGoods do begin
    ReadOnly := false;
    Columns.State := csCustomized;
    Part := round( Width / 15 );
    for i := Columns.Count - 1 downto 0 do begin

     if ( Columns.Items[ i ].FieldName = 'Name' ) then begin
        Columns.Items[ i ].Title.Caption := 'Позиция';
        Columns.Items[ i ].Width := Part * 5;
        Columns.Items[ i ].ReadOnly := true;
      end
      else
        if ( Columns.Items[ i ].FieldName = 'Amount' ) then begin
          Columns.Items[ i ].Title.Caption := 'Количество';
          Columns.Items[ i ].Width := Part * 3;
          Columns.Items[ i ].Alignment := taLeftJustify;
          Columns.Items[ i ].ReadOnly := false;
          ( Columns.Items[ i ].Field as TFloatField ).DisplayFormat := '###,###,###.######';
          ( Columns.Items[ i ].Field as TFloatField ).EditFormat :=

            DisplayToEditFormat( ( Columns.Items[ i ].Field as TFloatField ).DisplayFormat );
        end
        else
          if ( Columns.Items[ i ].FieldName = 'MeasureName' ) then begin
            Columns.Items[ i ].Title.Caption := 'Измерение';
            Columns.Items[ i ].Width := Part * 4;
            Columns.Items[ i ].ReadOnly := true;
          end
          else
            if ( Columns.Items[ i ].FieldName = 'No' ) then begin
              Columns.Items[ i ].Title.Caption := 'N п/п';
              Columns.Items[ i ].Width := Part * 2;
              Columns.Items[ i ].ReadOnly := false;
            end
            else
              Columns.Items[ i ].Destroy;
    end;
  end;
end;

function TfrmDBOSetEdit.ValidCheck: boolean;
begin
  Result := inherited ValidCheck;
  if spTDBOSet_ViewContents.RecordCount <= 0 then
    raise Exception.Create( 'Необходимо внести хотя бы одну позицию в комплект' );
end;

procedure TfrmDBOSetEdit.SaveDataEnvironment;
var
  i: integer;
begin
  inherited;
  // включение/исключение из комплекта
  for i := 0 to dbgGoods.LogItems - 1 do begin
    case dbgGoods.LogActions[ i ] of
      DBG_INSERTED,
      DBG_CHANGED:
      begin
        spTDBOSet_InclObj.Active := false;
        spTDBOSet_InclObj.ParamByName( '@SetID' ).AsInteger := DBOInfo.OID;
        spTDBOSet_InclObj.ParamByName( '@OID' ).AsInteger := dbgGoods.LogOIDs[ i ];
        spTDBOSet_InclObj.ParamByName( '@No' ).AsFloat :=
        spTDBOSet_ViewContents.Lookup( 'OID', dbgGoods.LogOIDs[ i ], 'No' );
        spTDBOSet_InclObj.ParamByName( '@Amount' ).AsFloat :=
        spTDBOSet_ViewContents.Lookup( 'OID', dbgGoods.LogOIDs[ i ], 'Amount' );
        spTDBOSet_InclObj.ParamByName( '@MeasureID' ).AsInteger :=
        spTDBOSet_ViewContents.Lookup( 'OID', dbgGoods.LogOIDs[ i ], 'MeasureID' );
        spTDBOSet_InclObj.ExecProc;
      end;
      DBG_DELETED:
      begin
        spTDBOSet_ExclObj.Active := false;
        spTDBOSet_ExclObj.ParamByName( '@SetID' ).AsInteger := DBOInfo.OID;
        spTDBOSet_ExclObj.ParamByName( '@OID' ).AsInteger := dbgGoods.LogOIDs[ i ];
        spTDBOSet_ExclObj.ExecProc;
      end;
      else ;
    end;
  end;
  dbgGoods.ClearLogAction;
end;

procedure TfrmDBOSetEdit.spTDBOSet_ViewContentsAfterPost(DataSet: TDataSet);
begin
  inherited;
  dbgGoods.AddLogAction( DataSet.FieldByName( 'OID' ).AsInteger, DBG_CHANGED );
end;

procedure TfrmDBOSetEdit.dbgGoodsSelectDBO(Sender: TObject; DBOInfo: TDBOInfo);
var
  MaxNo, MeasureID: integer;
begin
  inherited;
  if spTDBOSet_ViewContents.Locate( 'OID', DBOInfo.OID, [] ) then begin
    MessageDlg( 'Позиция уже включена в список:' + #13 +
                '<' + DBOInfo.Name + '>',
                mtError, [ mbOK ], 0 );
  end
  else begin
    spTDBOGood_View.Active := false;
    spTDBOGood_View.ParamByName( '@OID' ).Value := DBOInfo.OID;
    spTDBOGood_View.Open;
    if spTDBOGood_View.RecordCount > 0 then begin
      MeasureID := spTDBOGood_View.FieldByName( 'MeasureID' ).AsInteger;
    end
    else begin
      MeasureID := 0;
    end;

    spTDBObject_View.Active := false;
    spTDBObject_View.ParamByName( '@OID' ).Value := MeasureID;
    spTDBObject_View.Open;

    // определение максимального номера
    MaxNo := 1;
    spTDBOSet_ViewContents.First;
    while not spTDBOSet_ViewContents.EOF do begin
      if spTDBOSet_ViewContents.FieldByName( 'No' ).AsInteger >= MaxNo then
      MaxNo := spTDBOSet_ViewContents.FieldByName( 'No' ).AsInteger + 1;
      spTDBOSet_ViewContents.Next;
    end;

    spTDBOSet_ViewContents.Append;
    spTDBOSet_ViewContents.FieldByName( 'No' ).AsInteger := MaxNo;
    spTDBOSet_ViewContents.FieldByName( 'OID' ).AsInteger := DBOInfo.OID;
    spTDBOSet_ViewContents.FieldByName( 'Name' ).AsString := DBOInfo.Name;
    spTDBOSet_ViewContents.FieldByName( 'BaseClass' ).AsString := DBOInfo.BaseClass;
    spTDBOSet_ViewContents.FieldByName( 'Amount' ).Value := 1;
    spTDBOSet_ViewContents.FieldByName( 'MeasureID' ).Value := MeasureID;
    spTDBOSet_ViewContents.FieldByName( 'MeasureName' ).Value :=
    spTDBObject_View.FieldByName( 'Name' ).Value;
    spTDBOSet_ViewContents.FieldByName( 'MeasureClass' ).Value :=
    spTDBObject_View.FieldByName( 'BaseClass' ).Value;
    spTDBOSet_ViewContents.Post;
    dbgGoods.AddLogAction( DBOInfo.OID, DBG_INSERTED );
  end;
end;

procedure TfrmDBOSetEdit.dbgGoodsDeleteDBO(Sender: TObject);
begin
  inherited;
  if spTDBOSet_ViewContents.RecordCount > 0 then begin
    dbgGoods.AddLogAction( spTDBOSet_ViewContents.FieldByName( 'OID' ).AsInteger, DBG_DELETED );
    spTDBOSet_ViewContents.Delete;
    DataChanged := true;
  end
end;

procedure TfrmDBOSetEdit.dbgGoodsSelectCellDBO(Sender: TObject; DBOInfo: TDBOInfo);
begin
  inherited;
  spTDBOSet_ViewContents.Edit;
  spTDBOSet_ViewContents.FieldByName( 'MeasureID' ).Value := DBOInfo.OID;
  spTDBOSet_ViewContents.FieldByName( 'MeasureName' ).Value := DBOInfo.Name;
  spTDBOSet_ViewContents.FieldByName( 'MeasureClass' ).Value := DBOInfo.BaseClass;
  spTDBOSet_ViewContents.Post;
end;

end.

В разделе объявления объектов формы определен компонент dbgGoods  класса TDBObjGrid отображающий позиции, входящие в комплект. Через DataSource его заполняет spTDBOSet_GetGoods, которая является редактируемым DataSet и связана с компонентом SQLUpdate1. Напомним, что свойство CachedUpdates установлено в true. Тексты серверных процедур можно просмотреть в файле \SERVER\Things\Set.sql. Приведем процедуру просмотра позиций, входящих в комплект:

CREATE PROCEDURE TDBOSet_ViewContents
  @OID int
as
  SELECT Set2Obj.No,
         OID = Set2Obj.OID,
         Name = Objs.Name,
         BaseClass = Objs.BaseClass,
         Set2Obj.Amount,
         Set2Obj.MeasureID,
         MeasureName = ( SELECT Name FROM Objs WHERE OID = Set2Obj.MeasureID ),
         MeasureClass = ( SELECT BaseClass FROM Objs WHERE OID = Set2Obj.MeasureID )
    FROM Set2Obj, Objs ( NOLOCK )
    WHERE Objs.OID = Set2Obj.OID AND
          Set2Obj.SetID = @OID
    ORDER BY Set2Obj.No
GO

В методе инициализации формы InitDataEnvironment определяются ячейки для отображения объектов БД в Grid

dbgGoods.AddDBOCellInfo( 'GoodID', 'BaseClass', 'Name', '' );
dbgGoods.AddDBOCellInfo( 'MeasureID', 'MeasureClass', 'MeasureName', 'Measure' );

Первая колонка ячеек содержит объекты класса товаров, входящих в комплект, а вторая ячейка отображает единицы измерения и является редактируемой, поскольку имеет информацию о системной папке “Measure”, предположительно содержащей объекты класса единиц измерения.

После открытия DataSet процедуры TDBOSet_GetGoods описывается инициализация самого Grid с детализацией информации по колонкам. Таким образом отображаются только нужные колонки, которым при необходимости прописываются свойства возможности редактирования, выравнивания и ширины. Ширина подбирается простым образом: вся ширина Grid делится на N частей из которых N-1 распределяется между колонками.

В методе проверки ValidCheck происходит стандартная проверка на наличие хотя бы одной позиции в комплекте.

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

OnAfterPost – для регистрации модификации записи (DBG_CHANGED);

OnSelectDBO – для занесения новой записи в DataSet, заполнения ее атрибутами выбранного объекта (товара) и регистрации действия (DBG_INSERTED);

OnDeleteDBO – для удаления записи из DataSet и регистрации действия (DBG_DELETED);

OnSelectCellDBO – для заполнения информации о единице измерения по товару и регистрации изменения в журнале.


Содержание Глоссарий Предыдущая Следующая