Добрый день!
В этом посте пойдет речь, о небольшой полезняшке позволяющий превратить стандартный 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);
И вуаля - готово, можно пользоватся
Комментариев нет:
Отправить комментарий