Добрый день!
В этом посте пойдет речь, о небольшой полезняшке позволяющий превратить стандартный DevExpress-овский компонент cxVirtualVerticalGrid в средство отображения и редактирования данных таблицы БД.
Предыстория:Мне очень импонирует представления данных таблиц БД в виде так сказать вертикального списка, разворачивающегося по подразделам. Что бы-было понятно о чем пойдет разговор представлю фрагмент скриншота из реально существующе программы, в нижней части он и представлен:
В общем захотелось мне такой список, да вот беда в стандартной DevExpress поставке такого компонента (который представлял DB-данные одной записи нет). Есть стандартный cxDbVerticalGrid но он не умеет предоставлять только 1 запись на просмотр, а предоставляет весь датасет, что согласитесь для моего случая не совсем то что нужно, т.к. он аналог грида только вертикальный. Мне же нужно было только видеть данные текущей записи (та которая в фокусе). Некий похожий компонент по функциональности компонент был в пакете InfoPower Professional, но ставить его я не хотел.
И тут мой взгляд упал на cxVirtualVerticalGrid который позволяет отображать собственные данные из класса наследника TcxCustomDataSource.
В общем пришлось написать посредника между cxVirtualVerticalGrid и datasource БД. Задача стояла так, что бы по колонке делался запрос к БД и выводилось значение в строку cxVirtualVerticalGrid связанного с полем БД. Ну и обратный процесс изменения данных в cxVirtualVerticalGrid-е попадал в БД. Этакий бонус - редактор полей БД, хоть и усеченный.
Вообщем поколдовав денек тродилась такое решение, в виде класса TBrokerDataSource:
001 |
unit _Class . BrokerDB; |
006 |
Classes, cxCustomData, Variants, DB, DBCtrls; |
010 |
TBrokerField = class (TCollectionItem) |
013 |
procedure SetFieldName( const Value: String ); |
016 |
Property FieldName : String read FFieldName write SetFieldName; |
019 |
TBrokerFieldList = class (TCollection) |
021 |
function GetItems(Index: Integer ): TBrokerField; |
022 |
procedure SetItems(Index: Integer ; const Value: TBrokerField); |
024 |
Function Add: TBrokerField; |
025 |
property Items[Index: Integer ]: TBrokerField read GetItems write SetItems; default; |
028 |
TBrokerDataSource = class (TcxCustomDataSource) |
031 |
FBrokerFieldList : TBrokerFieldList; |
032 |
FDataSource : TDataSource; |
033 |
procedure SetBrokerFieldList( const Value: TBrokerFieldList); |
035 |
procedure SetDataSource( const Value: TDataSource); |
036 |
function GetDataSource: TDataSource; |
037 |
Procedure DataChange(Sender: TObject; Field: TField); |
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; |
049 |
destructor Destroy; override; |
050 |
property Modified: boolean read FModified; |
052 |
Property DataSource: TDataSource Read GetDataSource Write SetDataSource; |
053 |
property Fields : TBrokerFieldList read FBrokerFieldList write SetBrokerFieldList; |
059 |
{ TCustomDataSource } |
060 |
constructor TBrokerDataSource . Create; |
062 |
FBrokerFieldList := TBrokerFieldList . Create(TBrokerField); |
065 |
destructor TBrokerDataSource . Destroy; |
067 |
FBrokerFieldList . Free; |
071 |
//------------------------------------------------------------------------------ |
072 |
function TBrokerDataSource . GetRecordCount: Integer ; |
077 |
function TBrokerDataSource . GetValue(ARecordHandle: TcxDataRecordHandle; |
078 |
AItemHandle: TcxDataItemHandle): Variant; |
084 |
Result := '<Нет данных>' ; |
085 |
if Assigned(DataSource) then |
087 |
if Assigned(DataSource . DataSet) then |
089 |
I := Integer (AItemHandle); |
090 |
if I < FBrokerFieldList . Count then |
092 |
FN := FBrokerFieldList[I].FieldName; |
093 |
Result := DataSource . DataSet . FieldByName(FN).AsVariant; |
099 |
procedure TBrokerDataSource . SetValue(ARecordHandle: TcxDataRecordHandle; |
100 |
AItemHandle: TcxDataItemHandle; |
101 |
const AValue: Variant); |
106 |
I := Integer (AItemHandle); |
107 |
if I < FBrokerFieldList . Count then |
109 |
if Assigned(DataSource . DataSet) then |
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; |
121 |
function TBrokerDataSource . AppendRecord: TcxDataRecordHandle; |
123 |
// AStatElement: TStatElement; |
125 |
// AStatElement := TStatElement.Create; |
126 |
// Result := TcxDataRecordHandle(FStatElementList.Add(AStatElement)); |
128 |
// if not Modified then |
129 |
// FModified := True; |
132 |
function TBrokerDataSource . InsertRecord(ARecordHandle: TcxDataRecordHandle): TcxDataRecordHandle; |
134 |
// AStatElement: TStatElement; |
136 |
// AStatElement := TStatElement.Create; |
137 |
// FStatElementList.Insert(Integer(ARecordHandle), AStatElement); |
138 |
// Result := TcxDataRecordHandle(ARecordHandle); |
140 |
// if not Modified then |
141 |
// FModified := True; |
144 |
procedure TBrokerDataSource . DataChange(Sender: TObject; Field: TField); |
149 |
procedure TBrokerDataSource . DeleteRecord( |
150 |
ARecordHandle: TcxDataRecordHandle); |
157 |
procedure TBrokerDataSource . SetBrokerFieldList( const Value: TBrokerFieldList); |
159 |
FBrokerFieldList := Value; |
162 |
function TBrokerDataSource . GetDataSource: TDataSource; |
164 |
Result := FDataSource; |
167 |
procedure TBrokerDataSource . SetDataSource( const Value: TDataSource); |
169 |
FDataSource := Value; |
170 |
FDataSource . OnDataChange := DataChange; |
174 |
procedure TBrokerField . SetFieldName( const Value: String ); |
180 |
function TBrokerFieldList . Add: TBrokerField; |
182 |
Result := TBrokerField( inherited Add); |
185 |
function TBrokerFieldList . GetItems(Index: Integer ): TBrokerField; |
187 |
Result := TBrokerField( inherited GetItem(Index)) |
190 |
procedure TBrokerFieldList . SetItems(Index: Integer ; const Value: TBrokerField); |
192 |
inherited SetItem(Index, Value); |
Использовать его просто
объявляете переменну класса в нужном месте
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; |
06 |
BrField : TBrokerField; |
08 |
BrField:= DataSourceBroker . Fields . Add; |
09 |
BrField . FieldName := Field . FieldName; |
10 |
with VirtualVerticalGrid do |
12 |
with AddChild(RowParent, TcxEditorRow) as TcxEditorRow do |
14 |
Properties . Caption := Field . DisplayName; |
15 |
Properties . HeaderAlignmentHorz := taLeftJustify; |
16 |
case Field . DataType of |
18 |
Properties . DataBinding . ValueTypeClass := TcxBooleanValueType; |
19 |
ftDate, ftDateTime, ftTime |
21 |
Properties . DataBinding . ValueTypeClass := TcxDateTimeValueType; |
22 |
ftFloat,ftExtended,ftSingle : |
23 |
Properties . DataBinding . ValueTypeClass := TcxFloatValueType; |
25 |
Properties . DataBinding . ValueTypeClass := TcxFMTBcdValueType; |
26 |
ftInteger, ftAutoInc, ftShortint |
28 |
Properties . DataBinding . ValueTypeClass := TcxIntegerValueType; |
29 |
ftLargeint,ftLongWord : |
30 |
Properties . DataBinding . ValueTypeClass := TcxLargeIntValueType; |
32 |
Properties . DataBinding . ValueTypeClass := TcxObjectValueType; |
34 |
Properties . DataBinding . ValueTypeClass := TcxSmallintValueType; |
35 |
ftTimeStamp, ftOraTimeStamp : |
36 |
Properties . DataBinding . ValueTypeClass := TcxSQLTimeStampValueType; |
38 |
Properties . DataBinding . ValueTypeClass := TcxVariantValueType; |
40 |
Properties . DataBinding . ValueTypeClass := TcxWideStringValueType; |
41 |
ftString, ftFixedChar : |
42 |
Properties . DataBinding . ValueTypeClass := TcxStringValueType; |
44 |
Properties . DataBinding . ValueTypeClass := TcxCurrencyValueType; |
46 |
Properties . DataBinding . ValueTypeClass := TcxWordValueType; |
Пример заполнения представлен ниже (взято с реального проекта)
05 |
Broker := TBrokerDataSource . Create; |
06 |
Broker . DataSource := DsContract; |
07 |
cxVirtualVerticalGrid . DataController . CustomDataSource := Broker; |
10 |
with cxVirtualVerticalGrid do |
12 |
Row := Add(TcxCategoryRow); |
13 |
TcxCategoryRow(Row).Properties . Caption := 'Данные регистрации' ; |
16 |
// поля - строки вcxVirtualVerticalGrid |
17 |
AddFieldBroker(cxVirtualVerticalGrid, Broker, Row, ContractNUM_REGISTRATION); |
18 |
AddFieldBroker(cxVirtualVerticalGrid, Broker, Row, ContractDATE_REGISTRATION); |
19 |
AddFieldBroker(cxVirtualVerticalGrid, Broker, Row, ContractUSER_NAME); |
И вуаля - готово, можно пользоватся