1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
| unit UDatasetSortingUtility;
interface
uses DB, dbClient, cxGridTableView, cxGridDBTableView;
type TDatasetSortingUtility = class
const prefix : string = 't_';
procedure FillInternalCalcFields(DataSet: TDataSet);
class procedure AddInternalCalcFields(DataSet: TDataSet);
class procedure SortAGrid(GridTableView: TcxGridTableView; AColumn: TcxGridColumn);
class function SortCustomClientDataSet(DataSet: TCustomClientDataSet; FieldName: String): Boolean;
private
FOriginalOnCalcFields : Procedure(Dataset : Tdataset);
constructor Create(Dataset : Tdataset);
end;
implementation
uses TypInfo, Dialogs;
{ TDatasetSortingUtility }
constructor TDatasetSortingUtility.Create(Dataset: Tdataset);
begin
if Assigned(DataSet.OnCalcFields) then
// si le dataset a deja un gestionnaire OnCalcFields assigné, on stocke un pointeur vers ce gestionnaire
FOriginalOnCalcFields := @DataSet.OnCalcFields
else
FOriginalOnCalcFields := nil;
end;
procedure TDatasetSortingUtility.FillInternalCalcFields(DataSet: TDataSet);
var
i:integer;
currentLookupField, correspIntCalcField:TField;
begin
// si le dataset avait deja un gestionnaire OnCalcFields assigné, on commence par executer ce gestionnaire
if Assigned(FOriginalOnCalcFields) then
FOriginalOnCalcFields(Dataset);
// maintenant, pour chaque champ fkLookup du dataset, on actualise son champ homologue de type fkInternalCalc
for i := 0 to Pred(DataSet.Fields.Count) do begin
currentLookupField := DataSet.Fields[i];
if currentLookupField.FieldKind <> fkLookup then continue;
correspIntCalcField := DataSet.FindField(prefix+currentLookupField.FieldName);
if correspIntCalcField <> nil then
correspIntCalcField.Value :=
currentLookupField.LookupDataSet.Lookup(
currentLookupField.LookupKeyFields,
DataSet.FieldByName(currentLookupField.KeyFields).Value,
currentLookupField.LookupResultField);
end;
end;
class procedure TDatasetSortingUtility.AddInternalCalcFields(DataSet: TDataSet);
var
i:integer;
currentField:TField;
internalCalcField:TStringField;
dummy : TDatasetSortingUtility;
begin
// on parcourt chaque champ du dataset, et si l'on trouve un champ de type fkLookup, on lui crée un
// homologue de type fkInternalCalc, et s'appelant prefix + le FieldName original
for i := 0 to Pred(DataSet.Fields.Count) do begin
currentField := DataSet.Fields[i];
if currentField.FieldKind <> fkLookup then continue;
internalCalcField := TStringField.Create(DataSet);
internalCalcField.Name := prefix+currentField.FieldName;
internalCalcField.FieldKind := fkInternalCalc;
internalCalcField.FieldName := prefix+currentField.FieldName;
internalCalcField.DataSet := DataSet;
end;
// maintenant on va créer un objet TDatasetSortingUtility associé au dataset, puis affecter au OnCalcFields
// du dataset la méthode d'instance FillInternalCalcFields
dummy := TDatasetSortingUtility.Create(DataSet);
DataSet.OnCalcFields := dummy.FillInternalCalcFields;
end;
class procedure TDatasetSortingUtility.SortAGrid(GridTableView: TcxGridTableView; AColumn: TcxGridColumn);
Var
DatasetToSort : TClientDataset;
FieldToSort : string;
begin
DatasetToSort := TcxGridDBTableView(GridTableView).Datacontroller.Datasource.dataset as TclientDataset;
FieldToSort := TcxGridDBColumn(AColumn).DataBinding.FieldName;
SortCustomClientDataSet(DatasetToSort,FieldToSort);
end;
// cf. article Understanding ClientDataSet Indexes ( http://dn.codegear.com/article/29056 )
class function TDatasetSortingUtility.SortCustomClientDataSet(DataSet: TCustomClientDataSet;
FieldName: String): Boolean;
var
i: Integer;
IndexDefs: TIndexDefs;
IndexName: String;
IndexOptions: TIndexOptions;
Field: TField;
onCalcFields : TDataSetNotifyEvent;
begin
Result := False;
Field := DataSet.Fields.FindField(FieldName);
//If invalid field name, exit.
if Field = nil then Exit;
//if invalid field type, exit.
if (Field is TObjectField) or (Field is TBlobField) or
(Field is TAggregateField) or (Field is TVariantField)
or (Field is TBinaryField) then Exit;
// si le champ est de type fkLookup il faut effectuer le tri sur un champ homologue de type fkInternalCalc
// ce champ devra avoir été créé au prealable grace a la proc AddInternalCalcFields
if Field.FieldKind = fkLookup then begin
if DataSet.Fields.FindField(prefix+FieldName) = nil then begin
ShowMessage('Sorting is impossible on field "'+FieldName+'"'+chr(13)+chr(13)+
'Developer must call TDatasetSortingUtility.AddInternalCalcFields on dataset '+ DataSet.Owner.Name+'.'+DataSet.name+' first ...');
exit;
end else
FieldName := prefix+FieldName;
end;
//Get IndexDefs and IndexName using RTTI
if IsPublishedProp(DataSet, 'IndexDefs') then
IndexDefs := GetObjectProp(DataSet, 'IndexDefs') as TIndexDefs
else
Exit;
if IsPublishedProp(DataSet, 'IndexName') then
IndexName := GetStrProp(DataSet, 'IndexName')
else
Exit;
//Ensure IndexDefs is up-to-date
IndexDefs.Update;
//If an ascending index is already in use,
//switch to a descending index
if IndexName = FieldName + '__IdxA' then begin
IndexName := FieldName + '__IdxD';
IndexOptions := [ixDescending];
end else begin
IndexName := FieldName + '__IdxA';
IndexOptions := [];
end;
//Look for existing index
for i := 0 to Pred(IndexDefs.Count) do begin
if IndexDefs[i].Name = IndexName then begin
Result := True;
Break
end;
end;
//If existing index not found, create one
if not Result then begin
DataSet.AddIndex(IndexName, FieldName, IndexOptions);
Result := True;
end;
//Set the index
onCalcFields := DataSet.OnCalcFields;
DataSet.OnCalcFields := nil;
SetStrProp(DataSet, 'IndexName', IndexName);
DataSet.OnCalcFields := onCalcFields;
end;
end. |
Partager