📄 gridsort.pas
字号:
unit GridSort;
interface
uses Classes,SysUtils,Grids;
//Sorting Stuff
type
TSortStyle = (ssAutomatic, ssAlphabetic, ssNumeric, ssDate);
TSortDirection = (sdAscending, sdDescending);
{**************************************************************}
{*** NOTE: These are the options you can set to affect sorting.}
TSortOptions = record
SortStyle: TSortStyle;
SortDirection: TSortDirection;
SortCaseSensitive: Boolean;
end;
TSortedListEntry = record
Str: String;
RowNum: Longint;
end;
PSortedListEntry = ^TSortedListEntry;
TSortedList = class(TList)
public
function GetItem(const i: Integer): PSortedListEntry;
procedure Reset;
end;
procedure SortGrid(grd:TStringGrid;ACol:integer);
var GlobalSortOptions: TSortOptions;
implementation
var SortedList: TSortedList;
type
TCustGridEnh=class(TCustomGrid)
public
function SelectCell(ACol, ARow: Longint): Boolean;
procedure MoveColRow(ACol, ARow: Longint; MoveAnchor, Show: Boolean);
end;
function TCustGridEnh.SelectCell(ACol, ARow: Longint): Boolean;
begin
inherited SelectCell(ACol, ARow);
end;
procedure TCustGridEnh.MoveColRow(ACol, ARow: Longint; MoveAnchor, Show: Boolean);
begin
inherited MoveColRow(ACol, ARow, MoveAnchor, Show);
end;
//Sorting
function StringCompare(const Str1, Str2: String): Integer;
begin
if Str1 < Str2 then Result:=-1
else if Str2 < Str1 then Result:=1
else Result:=0;
end;
function DateCompare(const Str1, Str2: String): Integer;
var
Val1, Val2: TDateTime;
begin
try
Val1:=StrToDateTime(Str1);
Val2:=StrToDateTime(Str2);
if Val1 < Val2 then
Result:=-1
else
if Val2 < Val1 then
Result:=1
else
Result:=0;
except
on EConvertError do Result:=0;
end;
end;
function NumericCompare(const Str1, Str2: String): Integer;
var
Val1, Val2: Extended;
begin
try
if str1='' then
Val1:=0
else
Val1:=StrToFloat(Str1);
if str2='' then
Val2:=0
else
Val2:=StrToFloat(Str2);
if Val1 < Val2 then
Result:=-1
else
if Val2 < Val1 then
Result:=1
else
Result:=0;
except
on EConvertError do Result:=0;
end;
end;
function Compare(Item1, Item2: Pointer): Integer;
var
Entry1, Entry2: PSortedListEntry;
begin
Entry1:=Item1;
Entry2:=Item2;
//Handle Case-Insensitivity.
if not GlobalSortOptions.SortCaseSensitive then
begin
Entry1^.Str:=Uppercase(Entry1^.Str);
Entry2^.Str:=Uppercase(Entry2^.Str);
end;
//Determine compare type and do the comparison.
case GlobalSortOptions.SortStyle of
ssNumeric: Result:=NumericCompare(Entry1^.Str, Entry2^.Str);
ssDate: Result:=DateCompare(Entry1^.Str, Entry2^.Str);
else
Result:=StringCompare(Entry1^.Str, Entry2^.Str);
end;
//Now, make sure we don't swap the rows if the Keys are equal.
//If they're equal then we sort by row number.
if Result = 0 then
begin
if Entry1^.RowNum < Entry2^.RowNum then
Result:=-1
else
if Entry1^.RowNum > Entry2^.RowNum then
Result:=1
else
Result:=0; //Sometimes an item does get compared to itself.
end
else //Reverse polarity if descending sort.
if GlobalSortOptions.SortDirection = sdDescending then
Result:=-1*Result;
end;
{******************************************************************************}
{** Public Members for TSortedList **}
{******************************************************************************}
function TSortedList.GetItem(const i: Integer): PSortedListEntry;
begin
//Cast the pointer.
Result:=PSortedListEntry(Items[i]);
end;
procedure TSortedList.Reset;
var
i: Integer;
begin
//Dispose of anything in the list first.
for i:=0 to Count-1 do
if Items[i] <> nil then
Dispose(Items[i]);
//Now clear the list.
Clear;
end;
procedure GridMoveTo(Grd:TStringGrid;ACol, ARow: Longint);
begin
with grd do
begin
if ACol < FixedCols then ACol:=FixedCols;
if ARow < FixedRows then ARow:=FixedRows;
if TCustGridEnh(grd).SelectCell(ACol, ARow) then
begin
Col:=ACol;
Row:=ARow;
TCustGridEnh(grd).MoveColRow(ACol, ARow, True, True);
end;
end;
end;
//This function tries to determine the best sort style
//for a column. If all the entries can be converted to
//numbers, a numeric sort is returned. If they can all
//be converted to dates, a date sort is returned.
//Otherwise, an alphabetic sort is returned.
function DetermineSortStyle(grd:TStringGrid;const ACol: Longint): TSortStyle;
var
i: Integer;
DoNumeric, DoDate: Boolean;
begin
with grd do
begin
DoNumeric:=True;
DoDate:=True;
//Note: We only go through the rows once.
//This code depends on the fact that no
//entry can be both a date and number.
//If that fails to hold true, this will
//return a numeric sort.
for i:=FixedRows to RowCount-1 do
begin
if DoNumeric then
begin
try
if Cells[ACol,i]<>'' then StrToFloat(Cells[ACol, i]);
except
on EConvertError do
begin
DoNumeric:=False;
if not DoDate then Break;
end;
end;
end;
if DoDate then
begin
try
StrToDateTime(Cells[ACol, i]);
except
on EConvertError do
begin
DoDate:=False;
if not DoNumeric then Break;
end;
end;
end;
end;
if DoNumeric then
Result := ssNumeric
else
if DoDate then
Result := ssDate
else
Result := ssAlphabetic;
end;
end;
{Grid Sort}
procedure SortGrid(grd:TStringGrid;ACol:integer);
var
i: Integer;
Item: PSortedListEntry;
BufferGrid: TStringGrid;
begin
GlobalSortOptions.SortStyle:=DetermineSortStyle(grd,ACol);
with grd do
begin
try
//Get rid of any old entries in the sorted list.
SortedList.Reset;
//Set the sort options for the list.
//"Compare" can only look at GlobalSortOptions.
//GlobalSortOptions:=SortOptions;
//Insert the Row Number and Key (Str) into
for i:=FixedRows to RowCount-1 do
begin
New(Item);
Item^.RowNum:=i;
Item^.Str:=Cells[ACol, i];
SortedList.Add(Item);
end;
//Quick Sort the list by key string.
//Then the row numbers will indicate where
//each row should be placed.
//E.g. If list item 0 contains a RowNum of 4 then
//row 4 should be the first row (position 0).
SortedList.Sort(Compare);
//Now rearrange the rows of the grid in sorted order.
//This is a fast but space inefficient way to do it.
//First, create a buffer grid and size it correctly.
BufferGrid:=TStringGrid.Create(nil);
try
BufferGrid.ColCount:=ColCount;
BufferGrid.RowCount:=RowCount;
//Copy the rows to the buffer grid in sorted order.
for i:=0 to SortedList.Count-1 do
begin
Item:=SortedList.GetItem(i);
BufferGrid.Rows[i+FixedRows].Assign(Rows[Item^.RowNum]);
end;
//Now put the rows back into the original grid.
for i:=FixedRows to RowCount-1 do
begin
Rows[i].Assign(BufferGrid.Rows[i]);
end;
finally
BufferGrid.Free;
end;
//Now put the selection back on the right row.
for i:=0 to SortedList.Count-1 do
begin
Item:=SortedList.GetItem(i);
if Item^.RowNum = Row then
begin
GridMoveTo(grd,Col, i+FixedRows);
Break;
end;
end;
finally
end;
end;
end;
initialization
begin
SortedList:=TSortedList.create;
with GlobalSortOptions do
begin
SortDirection:=sdAscending;
SortCaseSensitive:=false;
end;
end;
finalization
begin
SortedList.free;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -