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

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