Hej,
Jeg har to "hovedklasser" som alle mine underklasser arver fra. Den ene
er en dataklasse(arver fra TPersistent), det kunne eksempel være en
vare, en kunde osv. Den anden hovedklasse er en listeklasse(arver fra
TObjectList), denne indeholder så en liste af dataklasser, f.eks. en
varelist, en kundeliste osv.
Da jeg bruger RaveReports til rapportgenerering vil jeg gerne kunne
angive, fra Rave-rapporten, hvilken property i data-klassen som en given
liste skal kunne sorteres efter. Da jeg gerne vil undgå at lave sorting
for alle klasser og alle properties har jeg forsøgt at benytte mig af
RTTI.
Jeg har lavet en metode i min listeklasse(TNPParentList.PrinterSetSort),
denne metode kaldes fra Rave Reports når brugeren har sat en
SortKey-property på et databand som henter data fra listen. Dette
fungere fint, men idag spurgte en test-brugere så om: "Jeg vil gerne
sortere efter FeltA _OG_ FeltB...i den rækkefølge...hvordan gør jeg
det?"....øh...jeg forsøgte så at omskrive sortingsfunktionen til at
understøtte dette, men det vil bare ikke som jeg vil. Nu har jeg så
efterhånden stiret mig blind på det og mistænker nu Quicksort for ikke
at kunne bruges til det jeg forsøger. Er der nogen der kan gennemskue om
det er korrekt eller set hvor jeg har lavet fejlen?
Først en kildekoden:
**********
//Method to enable users to sort lists from a Rave-report
procedure TNPParentList.PrinterSetSort(Connection: TRvCustomConnection);
//Compare-method for the Sort-function
function SortCompare(Item1, Item2: Pointer): Integer;
var
Count, Loop: Integer;
List: PPropList;
objProp: TObject;
SortLoop: integer;
begin
//Make sure we are reset
SortLoop:=0;
Result:=0;
//Allocate space for properties of the data-class
//Note: item1 and item2 will always be of the same class, so just
use item1!
Count := GetPropList(TNPParent(item1).ClassInfo, tkProperties, nil)
;
GetMem(List, Count * SizeOf(PPropInfo)) ;
GetPropList(TNPParent(item1).ClassInfo, tkProperties, List);
try
//Loop as long as we does not have a difference in the sorting
fields (...or aslong as we have fields to sort by!)
while (result=0) and (SortLoop<TNPParent(Item1).SortingList.Count)
do
begin
//Loop through the property-list of the data-class...
for Loop := 0 to Pred(Count) do
begin
//Is this the right property to sort by?
If TNPParent(Item1).SortingList[SortLoop] = List[loop]^.Name
then
begin
//Make sure we sort the right way...only Integers, Strings
and Floats at the moment!
case List[Loop]^.PropType^.Kind of
tkInteger:
Result:=CompareValue(GetOrdProp(TNPParent(Item1),List[loop]^.Name),GetOrdProp(TNPParent(Item2),List[loop]^.Name));
tkString,
tkLString,
tkWString:
Result:=CompareText(GetStrProp(TNPParent(Item1),List[loop]^.Name),GetStrProp(TNPParent(Item2),List[loop]^.Name));
tkFloat:
Result:=CompareValue(GetFloatProp(TNPParent(Item1),List[loop]^.Name),GetFloatProp(TNPParent(Item2),List[loop]^.Name));
else raise exception.create(Format(_('Cannot sort on field
"%s", the format of the field is not supported! Please redefine
sortfield!'),[TNPParent(Item1).SortingList[SortLoop]]));
end;
//No need to search any further! We found the right
property!
break;
end;
//showmessage(List[loop]^.Name+chr(13)+inttostr(loop)+chr(13)+inttostr(Pred(Count)));
//Did we not find the property? If we are sorting on a
sub-class we wont find it! e.g. "dataclass.dataclass.property"
if loop=Pred(Count) then
begin
raise exception.create(Format(_('Cannot sort on field "%s",
maybe a sub-field? Please redefine
sortfield!'),[TNPParent(Item1).SortingList[SortLoop]]));
end;
end;
//Goto the next sort-field...
Inc(SortLoop);
end;
finally
FreeMem(List, Count * SizeOf(PPropInfo))
end;
end;
var
a: integer;
TheSortList: TStrings;
SortByFieldName: string;
begin
//Get the name of the field which the list should be sorted...
SortByFieldName:=Connection.ReadStr;
//Create a list of strings(Since the "SortByFieldName" can be of the
format "Field1 & Field2 & Field3..."
TheSortList:=TStringlist.create;
try
//Loop through the string and find each field-name
while pos('&',SortByFieldName)> 0 do
begin
TheSortList.Add(Trim(copy(SortByFieldName,1,pos('&',SortByFieldName)-2)));
System.Delete(SortByFieldName,1,pos('&',SortByFieldName));
end;
//...remember to add the last field in the list(...and potential the
only one!)
TheSortList.Add(Trim(SortByFieldName));
//Assign all the searchfields to each item (to be used in the
SortCompare-method)
for a:=0 to Self.Count-1 do
begin
Items[a].SortingList.Text:=TheSortList.Text;
end;
finally
TheSortList.free;
end;
//Use the TList.Sort function to sort the list...
Sort(@SortCompare);
end;
**********
Så problemet:
Say jeg søger på FeltA(en streng-type) og FeltB(en float-type), f.eks.:
1: "A" og 5,5
2: "B" og 1,0
3: "A" og 4,5
Så ville jeg forvente at den sorterede listen som 3-1-2:
3: "A" og 4,5
1: "A" og 5,5
2: "B" og 1,0
MEN dette er ikke tilfælde, den sortere ikke efter felter ud over den
første:
1: "A" og 5,5
3: "A" og 4,5
2: "B" og 1,0
....min tanke er så: Jeg har mistænkt min do-while løkke for at være
årsagen til problemet: altså det med at jeg kun kigger på de
efterfølgende felter hvis det aktuelle felt er ens for item1 og item2.
Men jeg kan ikke rigtigt gennemskue hvorfor det IKKE skulle virke. Jeg
håbede derfor at der var nogle "søgehajer" herinde som kunne sige: "hør
lige her unge mand, linje 7 er der helt i skoven!"...eller noget i den
stil
PS: Jeg har brugt samme princip til at fortælle Rave hvilke felter der
findes i data-klasserne og til at hente data ind i Rave fra
listerne...Jeg synes selv det er suuuuper smart, men jeg kunne godt
tænke mig at høre hvordan andre gør? Om jeg bare har opfundet den dybe
tallerken igen....igen....?
MVH
Thomas