DevExpress.cx VirtualVerticalGrid как средство редактирование и отображения данных таблицы БД

Источник: deeptown12
Кузан Дмитрий

Добрый день!

В этом посте пойдет речь, о небольшой полезняшке позволяющий превратить стандартный DevExpress-овский компонент cxVirtualVerticalGrid в средство отображения и редактирования данных таблицы БД.
Предыстория:Мне очень импонирует представления данных таблиц БД в виде так сказать вертикального списка, разворачивающегося по подразделам. Что бы-было понятно о чем пойдет разговор представлю фрагмент скриншота из реально существующе программы, в нижней части он и представлен:

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

001  unit _Class.BrokerDB;  
002    
003  interface 
004    
005  uses 
006    Classes, cxCustomData, Variants, DB, DBCtrls;  
007    
008  type 
009      
010    TBrokerField = class(TCollectionItem)  
011    private 
012      FFieldName: String;  
013      procedure SetFieldName(const Value: String);  
014    public 
015    published 
016       Property FieldName : String  read FFieldName write SetFieldName;  
017    end;  
018      
019    TBrokerFieldList = class(TCollection)  
020    private 
021      function  GetItems(Index: Integer): TBrokerField;  
022      procedure SetItems(Index: Integer; const Value: TBrokerField);  
023    public 
024      Function  Add: TBrokerField;  
025      property  Items[Index: Integer]: TBrokerField read GetItems write SetItems; default;  
026    end;  
027    
028    TBrokerDataSource = class(TcxCustomDataSource)  
029    private 
030      FModified        : boolean;  
031      FBrokerFieldList : TBrokerFieldList;  
032      FDataSource  : TDataSource;  
033      procedure SetBrokerFieldList(const Value: TBrokerFieldList);  
034    
035      procedure SetDataSource(const Value: TDataSource);  
036      function  GetDataSource: TDataSource;  
037      Procedure DataChange(Sender: TObject; Field: TField);  
038    protected 
039      function  GetRecordCount: Integer; override;  
040      function  AppendRecord: TcxDataRecordHandle; override;  
041      procedure DeleteRecord(ARecordHandle: TcxDataRecordHandle); override;  
042      function  GetValue(ARecordHandle: TcxDataRecordHandle;  
043                         AItemHandle: TcxDataItemHandle): Variant; override;  
044      function  InsertRecord(ARecordHandle: TcxDataRecordHandle): TcxDataRecordHandle; override;  
045      procedure SetValue(ARecordHandle: TcxDataRecordHandle;  
046                AItemHandle: TcxDataItemHandle; const AValue: Variant); override;      
047    public 
048      constructor Create;  
049      destructor Destroy; override;  
050      property Modified: boolean read FModified;  
051    published 
052      Property DataSource: TDataSource Read GetDataSource Write SetDataSource;  
053      property Fields    : TBrokerFieldList read FBrokerFieldList write SetBrokerFieldList;  
054    end;  
055    
056    
057  implementation 
058    
059  { TCustomDataSource } 
060  constructor TBrokerDataSource.Create;  
061  begin 
062   FBrokerFieldList := TBrokerFieldList.Create(TBrokerField);  
063  end;  
064    
065  destructor TBrokerDataSource.Destroy;  
066  begin 
067   FBrokerFieldList.Free;  
068   inherited;  
069  end;  
070    
071  //------------------------------------------------------------------------------  
072  function TBrokerDataSource.GetRecordCount: Integer;  
073  begin 
074   Result := 1 
075  end;  
076    
077  function TBrokerDataSource.GetValue(ARecordHandle: TcxDataRecordHandle;  
078                                           AItemHandle: TcxDataItemHandle): Variant;  
079  var 
080    AColumnId : Integer;  
081    FN : String;  
082    I : Integer;  
083  begin 
084    Result := '<Нет данных>';  
085    if Assigned(DataSource) then 
086    begin 
087       if Assigned(DataSource.DataSet) then 
088       begin 
089         I := Integer(AItemHandle);  
090         if I < FBrokerFieldList.Count then 
091         begin 
092           FN := FBrokerFieldList[I].FieldName;  
093           Result := DataSource.DataSet.FieldByName(FN).AsVariant;  
094         end;  
095       end;  
096    end;  
097  end;  
098    
099  procedure TBrokerDataSource.SetValue(ARecordHandle: TcxDataRecordHandle;  
100                                            AItemHandle: TcxDataItemHandle;  
101                                            const AValue: Variant);  
102  var 
103    FN : String;  
104    I : Integer;  
105  begin 
106   I := Integer(AItemHandle);  
107   if I < FBrokerFieldList.Count then 
108   begin 
109      if Assigned(DataSource.DataSet) then 
110      begin 
111         FN := FBrokerFieldList[I].FieldName;  
112         if not DataSource.DataSet.Modified then 
113            DataSource.DataSet.Edit;  
114         DataSource.DataSet.FieldByName(FN).AsVariant := AValue;  
115         DataSource.DataSet.Post;  
116      end;  
117   end;  
118  end;  
119    
120    
121  function TBrokerDataSource.AppendRecord: TcxDataRecordHandle;  
122  //var  
123  //  AStatElement: TStatElement;  
124  begin 
125  //  AStatElement := TStatElement.Create;  
126  //  Result := TcxDataRecordHandle(FStatElementList.Add(AStatElement));  
127  //  DataChanged;  
128  //  if not Modified then  
129  //    FModified := True;  
130  end;  
131    
132  function TBrokerDataSource.InsertRecord(ARecordHandle: TcxDataRecordHandle): TcxDataRecordHandle;  
133  //var  
134  //  AStatElement: TStatElement;  
135  begin 
136  //  AStatElement := TStatElement.Create;  
137  //  FStatElementList.Insert(Integer(ARecordHandle), AStatElement);  
138  //  Result := TcxDataRecordHandle(ARecordHandle);  
139  //  DataChanged;  
140  //  if not Modified then  
141  //     FModified := True;  
142  end;  
143    
144  procedure TBrokerDataSource.DataChange(Sender: TObject; Field: TField);  
145  begin 
146   DataChanged;  
147  end;  
148    
149  procedure TBrokerDataSource.DeleteRecord(  
150    ARecordHandle: TcxDataRecordHandle);  
151  begin 
152    DataChanged;  
153    if not Modified then 
154       FModified := True;  
155  end;  
156    
157  procedure TBrokerDataSource.SetBrokerFieldList(const Value: TBrokerFieldList);  
158  begin 
159    FBrokerFieldList := Value;  
160  end;  
161    
162  function TBrokerDataSource.GetDataSource: TDataSource;  
163  begin 
164   Result := FDataSource;  
165  end;  
166    
167  procedure TBrokerDataSource.SetDataSource(const Value: TDataSource);  
168  begin 
169    FDataSource := Value;  
170    FDataSource.OnDataChange := DataChange;  
171  end;  
172    
173  { TBrokerField} 
174  procedure TBrokerField.SetFieldName(const Value: String);  
175  begin 
176    FFieldName := Value;  
177  end;  
178    
179  { TBrokerFieldList } 
180  function TBrokerFieldList.Add: TBrokerField;  
181  begin 
182    Result := TBrokerField(inherited Add);  
183  end;  
184    
185  function TBrokerFieldList.GetItems(Index: Integer): TBrokerField;  
186  begin 
187    Result := TBrokerField(inherited GetItem(Index))  
188  end;  
189    
190  procedure TBrokerFieldList.SetItems(Index: Integer; const Value: TBrokerField);  
191  begin 
192   inherited SetItem(Index, Value);  
193  end;  
194    
195  end. 
Использовать его просто
объявляете переменну класса в нужном месте 
1  Broker : TBrokerDataSource; 
далее связываете  cxVirtualVerticalGrid c данным брокером и БД
1  Broker.DataSource := <DataSource талицы БД>;  
2  cxVirtualVerticalGrid.DataController.CustomDataSource := Broker; 
после чего заполняете отображаемые колонки в cxVirtualVerticalGrid-е
с помощью функции  AddFieldBroker, в которую передаете поле БД для отображения 
сама процедура
01  procedure AddFieldBroker(var VirtualVerticalGrid: TcxVirtualVerticalGrid;  
02                                  var DataSourceBroker : TBrokerDataSource;  
03                                  RowParent : TcxCustomRow;  
04                                  Field: TField);  
05  var 
06    BrField : TBrokerField;  
07  begin 
08    BrField:= DataSourceBroker.Fields.Add;  
09    BrField.FieldName := Field.FieldName;  
10    with VirtualVerticalGrid do 
11    begin 
12       with AddChild(RowParent, TcxEditorRow) as TcxEditorRow do 
13       begin 
14        Properties.Caption := Field.DisplayName;  
15        Properties.HeaderAlignmentHorz := taLeftJustify;  
16        case Field.DataType of 
17             ftBoolean              :  
18               Properties.DataBinding.ValueTypeClass := TcxBooleanValueType;  
19             ftDate, ftDateTime, ftTime  
20                                    :  
21               Properties.DataBinding.ValueTypeClass := TcxDateTimeValueType;  
22             ftFloat,ftExtended,ftSingle  :  
23               Properties.DataBinding.ValueTypeClass := TcxFloatValueType;  
24             ftFMTBcd, ftBCD        :  
25               Properties.DataBinding.ValueTypeClass := TcxFMTBcdValueType;  
26             ftInteger, ftAutoInc, ftShortint  
27                                    :  
28               Properties.DataBinding.ValueTypeClass := TcxIntegerValueType;  
29             ftLargeint,ftLongWord  :  
30               Properties.DataBinding.ValueTypeClass := TcxLargeIntValueType;  
31             ftObject               :  
32               Properties.DataBinding.ValueTypeClass := TcxObjectValueType;  
33             ftSmallint,ftByte      :  
34               Properties.DataBinding.ValueTypeClass := TcxSmallintValueType;  
35             ftTimeStamp, ftOraTimeStamp :  
36               Properties.DataBinding.ValueTypeClass := TcxSQLTimeStampValueType;  
37             ftVariant              :  
38               Properties.DataBinding.ValueTypeClass := TcxVariantValueType;  
39             ftWideString           :  
40               Properties.DataBinding.ValueTypeClass := TcxWideStringValueType;  
41             ftString, ftFixedChar  :  
42               Properties.DataBinding.ValueTypeClass := TcxStringValueType;  
43             ftCurrency             :  
44               Properties.DataBinding.ValueTypeClass := TcxCurrencyValueType;  
45             ftWord                 :  
46               Properties.DataBinding.ValueTypeClass := TcxWordValueType;  
47        end;  
48       end;  
49    end;  
50  end; 
Пример заполнения представлен ниже (взято с реального проекта)
01  var 
02  Row : TcxCustomRow;  
03  begin 
04     
05  Broker := TBrokerDataSource.Create;  
06  Broker.DataSource := DsContract;  
07  cxVirtualVerticalGrid.DataController.CustomDataSource := Broker;  
08    
09  Row := nil;  
10  with cxVirtualVerticalGrid do 
11  begin 
12   Row := Add(TcxCategoryRow);  
13   TcxCategoryRow(Row).Properties.Caption := 'Данные регистрации';  
14  end;<  
15    
16  // поля - строки вcxVirtualVerticalGrid   
17  AddFieldBroker(cxVirtualVerticalGrid, Broker, Row, ContractNUM_REGISTRATION);  
18  AddFieldBroker(cxVirtualVerticalGrid, Broker, Row, ContractDATE_REGISTRATION);  
19  AddFieldBroker(cxVirtualVerticalGrid, Broker, Row, ContractUSER_NAME); 
И вуаля - готово, можно пользоватся

Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=28109