понедельник, 6 февраля 2012 г.

Полезняшки: DevExpress.cxVirtualVerticalGrid как средство редактирование и отображения данных таблицы БД

Добрый день!

В этом посте пойдет речь, о небольшой полезняшке позволяющий превратить стандартный DevExpress-овский компонент cxVirtualVerticalGrid в средство отображения и редактирования данных таблицы БД.
Предыстория:Мне очень импонирует представления данных таблиц БД в виде так сказать вертикального списка, разворачивающегося по подразделам. Что бы-было понятно о чем пойдет разговор представлю фрагмент скриншота из реально существующе программы, в нижней части он и представлен:
В общем захотелось мне такой список, да вот беда в стандартной DevExpress поставке такого компонента (который представлял DB-данные одной записи нет). Есть стандартный cxDbVerticalGrid но он не умеет предоставлять только 1 запись на просмотр, а предоставляет весь датасет, что согласитесь для моего случая не совсем то что нужно, т.к. он аналог грида только вертикальный. Мне же нужно было только видеть данные текущей записи (та которая в фокусе). Некий похожий компонент по функциональности компонент был в пакете InfoPower Professional, но ставить его я не хотел. 
И тут мой взгляд упал на  cxVirtualVerticalGrid который позволяет отображать собственные данные из класса наследника TcxCustomDataSource.
В общем пришлось написать посредника между cxVirtualVerticalGrid и datasource БД. Задача стояла так, что бы по колонке делался запрос к БД и выводилось значение в строку  cxVirtualVerticalGrid связанного с полем БД.  Ну и обратный процесс изменения данных в cxVirtualVerticalGrid-е попадал в БД. Этакий бонус - редактор полей БД, хоть и усеченный. 
Вообщем поколдовав денек тродилась такое решение, в виде класса TBrokerDataSource:

unit _Class.BrokerDB;

interface

uses
  Classes, cxCustomData, Variants, DB, DBCtrls;

type
  
  TBrokerField = class(TCollectionItem)
  private
    FFieldName: String;
    procedure SetFieldName(const Value: String);
  public
  published
     Property FieldName : String  read FFieldName write SetFieldName;
  end;
  
  TBrokerFieldList = class(TCollection)
  private
    function  GetItems(Index: Integer): TBrokerField;
    procedure SetItems(Index: Integer; const Value: TBrokerField);
  public
    Function  Add: TBrokerField;
    property  Items[Index: Integer]: TBrokerField read GetItems write SetItems; default;
  end;

  TBrokerDataSource = class(TcxCustomDataSource)
  private
    FModified        : boolean;
    FBrokerFieldList : TBrokerFieldList;
    FDataSource  : TDataSource;
    procedure SetBrokerFieldList(const Value: TBrokerFieldList);

    procedure SetDataSource(const Value: TDataSource);
    function  GetDataSource: TDataSource;
    Procedure DataChange(Sender: TObject; Field: TField);
  protected
    function  GetRecordCount: Integer; override;
    function  AppendRecord: TcxDataRecordHandle; override;
    procedure DeleteRecord(ARecordHandle: TcxDataRecordHandle); override;
    function  GetValue(ARecordHandle: TcxDataRecordHandle;
                       AItemHandle: TcxDataItemHandle): Variant; override;
    function  InsertRecord(ARecordHandle: TcxDataRecordHandle): TcxDataRecordHandle; override;
    procedure SetValue(ARecordHandle: TcxDataRecordHandle;
              AItemHandle: TcxDataItemHandle; const AValue: Variant); override;    
  public
    constructor Create;
    destructor Destroy; override;
    property Modified: boolean read FModified;
  published
    Property DataSource: TDataSource Read GetDataSource Write SetDataSource;
    property Fields    : TBrokerFieldList read FBrokerFieldList write SetBrokerFieldList;
  end;


implementation

{ TCustomDataSource }
constructor TBrokerDataSource.Create;
begin
 FBrokerFieldList := TBrokerFieldList.Create(TBrokerField);
end;

destructor TBrokerDataSource.Destroy;
begin
 FBrokerFieldList.Free;
 inherited;
end;

//------------------------------------------------------------------------------
function TBrokerDataSource.GetRecordCount: Integer;
begin
 Result := 1; 
end;

function TBrokerDataSource.GetValue(ARecordHandle: TcxDataRecordHandle;
                                         AItemHandle: TcxDataItemHandle): Variant;
var
  AColumnId : Integer;
  FN : String;
  I : Integer;
begin
  Result := '<Нет данных>';
  if Assigned(DataSource) then
  begin
     if Assigned(DataSource.DataSet) then
     begin
       I := Integer(AItemHandle);
       if I < FBrokerFieldList.Count then
       begin
         FN := FBrokerFieldList[I].FieldName;
         Result := DataSource.DataSet.FieldByName(FN).AsVariant;
       end;
     end;
  end;
end;

procedure TBrokerDataSource.SetValue(ARecordHandle: TcxDataRecordHandle;
                                          AItemHandle: TcxDataItemHandle;
                                          const AValue: Variant);
var
  FN : String;
  I : Integer;
begin
 I := Integer(AItemHandle);
 if I < FBrokerFieldList.Count then
 begin
    if Assigned(DataSource.DataSet) then
    begin
       FN := FBrokerFieldList[I].FieldName;
       if not DataSource.DataSet.Modified then
          DataSource.DataSet.Edit;
       DataSource.DataSet.FieldByName(FN).AsVariant := AValue;
       DataSource.DataSet.Post;
    end;
 end;
end;


function TBrokerDataSource.AppendRecord: TcxDataRecordHandle;
//var
//  AStatElement: TStatElement;
begin
//  AStatElement := TStatElement.Create;
//  Result := TcxDataRecordHandle(FStatElementList.Add(AStatElement));
//  DataChanged;
//  if not Modified then
//    FModified := True;
end;

function TBrokerDataSource.InsertRecord(ARecordHandle: TcxDataRecordHandle): TcxDataRecordHandle;
//var
//  AStatElement: TStatElement;
begin
//  AStatElement := TStatElement.Create;
//  FStatElementList.Insert(Integer(ARecordHandle), AStatElement);
//  Result := TcxDataRecordHandle(ARecordHandle);
//  DataChanged;
//  if not Modified then
//     FModified := True;
end;

procedure TBrokerDataSource.DataChange(Sender: TObject; Field: TField);
begin
 DataChanged;
end;

procedure TBrokerDataSource.DeleteRecord(
  ARecordHandle: TcxDataRecordHandle);
begin
  DataChanged;
  if not Modified then
     FModified := True;
end;

procedure TBrokerDataSource.SetBrokerFieldList(const Value: TBrokerFieldList);
begin
  FBrokerFieldList := Value;
end;

function TBrokerDataSource.GetDataSource: TDataSource;
begin
 Result := FDataSource;
end;

procedure TBrokerDataSource.SetDataSource(const Value: TDataSource);
begin
  FDataSource := Value;
  FDataSource.OnDataChange := DataChange;
end;

{ TBrokerField}
procedure TBrokerField.SetFieldName(const Value: String);
begin
  FFieldName := Value;
end;

{ TBrokerFieldList }
function TBrokerFieldList.Add: TBrokerField;
begin
  Result := TBrokerField(inherited Add);
end;

function TBrokerFieldList.GetItems(Index: Integer): TBrokerField;
begin
  Result := TBrokerField(inherited GetItem(Index))
end;

procedure TBrokerFieldList.SetItems(Index: Integer; const Value: TBrokerField);
begin
 inherited SetItem(Index, Value);
end;

end.
Использовать его просто
объявляете переменну класса в нужном месте 
Broker : TBrokerDataSource;
далее связываете  cxVirtualVerticalGrid c данным брокером и БД
Broker.DataSource := <DataSource талицы БД>;
cxVirtualVerticalGrid.DataController.CustomDataSource := Broker;
после чего заполняете отображаемые колонки в cxVirtualVerticalGrid-е
с помощью функции  AddFieldBroker, в которую передаете поле БД для отображения 
сама процедура
procedure AddFieldBroker(var VirtualVerticalGrid: TcxVirtualVerticalGrid;
                                var DataSourceBroker : TBrokerDataSource;
                                RowParent : TcxCustomRow;
                                Field: TField);
var
  BrField : TBrokerField;
begin
  BrField:= DataSourceBroker.Fields.Add;
  BrField.FieldName := Field.FieldName;
  with VirtualVerticalGrid do
  begin
     with AddChild(RowParent, TcxEditorRow) as TcxEditorRow do
     begin
      Properties.Caption := Field.DisplayName;
      Properties.HeaderAlignmentHorz := taLeftJustify;
      case Field.DataType of
           ftBoolean              :
             Properties.DataBinding.ValueTypeClass := TcxBooleanValueType;
           ftDate, ftDateTime, ftTime
                                  :
             Properties.DataBinding.ValueTypeClass := TcxDateTimeValueType;
           ftFloat,ftExtended,ftSingle  :
             Properties.DataBinding.ValueTypeClass := TcxFloatValueType;
           ftFMTBcd, ftBCD        :
             Properties.DataBinding.ValueTypeClass := TcxFMTBcdValueType;
           ftInteger, ftAutoInc, ftShortint
                                  :
             Properties.DataBinding.ValueTypeClass := TcxIntegerValueType;
           ftLargeint,ftLongWord  :
             Properties.DataBinding.ValueTypeClass := TcxLargeIntValueType;
           ftObject               :
             Properties.DataBinding.ValueTypeClass := TcxObjectValueType;
           ftSmallint,ftByte      :
             Properties.DataBinding.ValueTypeClass := TcxSmallintValueType;
           ftTimeStamp, ftOraTimeStamp :
             Properties.DataBinding.ValueTypeClass := TcxSQLTimeStampValueType;
           ftVariant              :
             Properties.DataBinding.ValueTypeClass := TcxVariantValueType;
           ftWideString           :
             Properties.DataBinding.ValueTypeClass := TcxWideStringValueType;
           ftString, ftFixedChar  :
             Properties.DataBinding.ValueTypeClass := TcxStringValueType;
           ftCurrency             :
             Properties.DataBinding.ValueTypeClass := TcxCurrencyValueType;
           ftWord                 :
             Properties.DataBinding.ValueTypeClass := TcxWordValueType;
      end;
     end;
  end;
end;
Пример заполнения представлен ниже (взято с реального проекта)
var
Row : TcxCustomRow;
begin
 
Broker := TBrokerDataSource.Create;
Broker.DataSource := DsContract;
cxVirtualVerticalGrid.DataController.CustomDataSource := Broker;

Row := nil;
with cxVirtualVerticalGrid do
begin
 Row := Add(TcxCategoryRow);
 TcxCategoryRow(Row).Properties.Caption := 'Данные регистрации';
end;<

// поля - строки вcxVirtualVerticalGrid 
AddFieldBroker(cxVirtualVerticalGrid, Broker, Row, ContractNUM_REGISTRATION);
AddFieldBroker(cxVirtualVerticalGrid, Broker, Row, ContractDATE_REGISTRATION);
AddFieldBroker(cxVirtualVerticalGrid, Broker, Row, ContractUSER_NAME);
И вуаля - готово, можно пользоватся

Комментариев нет:

Отправить комментарий