2012-09-24 43 views
3

我想實現一個可排序的DBgrid(當點擊列標題時對它的行進行排序)。我設法按升序排序,但我無法按降序排列。這裏是我設計的設置:可排序的DBGrid

Query1.DatabaseName:='Test'; 
DataSetProvider1.DataSet:=Query1; 
ClientDataSet1.ProviderName:=DataSetProvider1; 
DataSource1.DataSet:=ClientDataSet1; 
DBGrid1.DatSource:=DataSource1; 

,這裏是我的代碼片段:

procedure TForm2.FormShow(Sender: TObject); 
begin 
    Query1.Open; 
    ClientDataSet1.Data:=DataSetProvider1.Data; 
    ClientDataSet1.AddIndex('objnameDESC','objname',[ixDescending]); 
    ClientDataSet1.AddIndex('SUM(cd.worktime)DESC','SUM(cd.worktime)',[ixDescending]); 
end; 

procedure TForm2.DBGrid1TitleClick(Column: TColumn); 
begin 
    case Column.Index of 
    0: if ClientDataSet1.IndexFieldNames='objname' then 
     ClientDataSet1.IndexFieldNames:='objnameDESC' 
    else 
     ClientDataSet1.IndexFieldNames:='objname'; 
    1: if ClientDataSet1.IndexFieldNames='SUM(cd.worktime)' then 
     ClientDataSet1.IndexFieldNames:='SUM(cd.worktime)DESC' 
    else 
     ClientDataSet1.IndexFieldNames:='SUM(cd.worktime)'; 
    end; 
end; 

當我在列標題,單擊第一次排序是按升序進行 - 這樣了到這裏一切都好。當我點擊第二次我希望以降序排序做,而是我得到的消息:

Project ... raised Exception class EDatabaseError with message 
'ClientDataSet1: Field 'objnameDESC' not found'. 

什麼我做錯了任何想法?

+0

我們的軟件通過每次使用不同'order by'子句重新執行查詢來完成該操作。 –

+0

As @UweRaabe已經指出:不要對'Grid'排序,排序'DataSet',特別是如果它已經是'TClientDataSet'。 –

回答

2

錯誤分配

除了事實,即一個不正確的分配製成,切換回「升」是不可能的。

對於2 Colums,您需要4個索引。

假設'objname'和'SUM(cd.worktime)'是字段。

procedure TForm2.FormShow(Sender: TObject); 
.... 
ClientDataSet1.AddIndex('col0_asc','objname',[]); 
ClientDataSet1.AddIndex('col0_desc','objname',[ixDescending]); 
ClientDataSet1.AddIndex('col1_asc','SUM(cd.worktime)',[]); 
ClientDataSet1.AddIndex('col1_desc','SUM(cd.worktime)',[ixDescending]); 
.... 

使用ClientDataSet1.IndexName

procedure TForm2.DBGrid1TitleClick(Column: TColumn); 
begin 
    case Column.Index of 
    0: if ClientDataSet1.IndexName='col0_asc' then 
     ClientDataSet1.IndexName:='col0_desc' 
    else 
     ClientDataSet1.IndexName:='col0_asc'; 
    1: if ClientDataSet1.IndexName='col1_asc' then 
     ClientDataSet1.IndexName:='col1_desc' 
    else 
     ClientDataSet1.IndexName:='col1_asc'; 
    end; 
.... 

或更短

procedure TForm2.DBGrid1TitleClick(Column: TColumn); 
begin 
    if ClientDataSet1.IndexName='col'+IntToStr(Column.Index)+'_asc' then 
     ClientDataSet1.IndexName:='col'+IntToStr(Column.Index)+'_desc' 
    else 
     ClientDataSet1.IndexName:='col'+IntToStr(Column.Index)+'_asc'; 
.... 

但它是更好的測試是活躍的列數(AddIndex =完成)

procedure TForm2.DBGrid1TitleClick(Column: TColumn); 
begin 
    if Column.Index < 2 then begin 
    if ClientDataSet1.IndexName='col'+IntToStr(Column.Index)+'_asc' then 
     ClientDataSet1.IndexName:='col'+IntToStr(Column.Index)+'_desc' 
    else 
     ClientDataSet1.IndexName:='col'+IntToStr(Column.Index)+'_asc'; 
    end; 
.... 
6

由於您已經在使用TClientDataSet,因此您可能會使用我爲此目的製作的組件。創建一個實例,設置其網格屬性,它會自動連接到OnTitleClick事件。

type 
    TDBGridSorter = class(TComponent) 
    strict private 
    FSortColumn: TColumn; 
    FGrid: TDBGrid; 
    procedure CreateIndex(const FieldName: string; Descending: Boolean); 
    function GetDataSet: TClientDataSet; 
     function MakeIndexName(const FieldName: string; Descending: Boolean): string; 
    procedure SetSortColumn(const Value: TColumn); 
    procedure SortByField(const FieldName: string; out Descending: Boolean); 
    private 
    procedure SetGrid(const Value: TDBGrid); 
    strict protected 
    procedure GridTitleClick(Column: TColumn); 
    property DataSet: TClientDataSet read GetDataSet; 
    public 
    property Grid: TDBGrid read FGrid write SetGrid; 
    property SortColumn: TColumn read FSortColumn write SetSortColumn; 
    end; 

procedure TDBGridSorter.CreateIndex(const FieldName: string; Descending: Boolean); 
var 
    cds: TClientDataSet; 
    indexDef: TIndexDef; 
    indexName: string; 
begin 
    cds := DataSet; 
    if cds <> nil then begin 
    indexName := MakeIndexName(FieldName, Descending); 
    if cds.IndexDefs.IndexOf(indexName) < 0 then begin 
     indexDef := cds.IndexDefs.AddIndexDef; 
     indexDef.Name := indexName; 
     indexDef.Fields := FieldName; 
     indexDef.CaseInsFields := FieldName; 
     if Descending then 
     indexDef.DescFields := FieldName; 
    end; 
    end; 
end; 

function TDBGridSorter.GetDataSet: TClientDataSet; 
begin 
    if (Grid <> nil) and (Grid.DataSource <> nil) and (Grid.DataSource.DataSet is TClientDataSet) then 
    Result := TClientDataSet(Grid.DataSource.DataSet) 
    else 
    Result := nil; 
end; 

procedure TDBGridSorter.GridTitleClick(Column: TColumn); 
begin 
    SortColumn := Column; 
end; 

function TDBGridSorter.MakeIndexName(const FieldName: string; Descending: Boolean): string; 
const 
    cAscDesc: array[Boolean] of string = ('_ASC', '_DESC'); 
begin 
    Result := FieldName + cAscDesc[Descending]; 
end; 

procedure TDBGridSorter.SetGrid(const Value: TDBGrid); 
begin 
    if FGrid <> Value then begin 
    if FGrid <> nil then begin 
     FGrid.OnTitleClick := nil; 
     FGrid.RemoveFreeNotification(Self); 
    end; 
    FGrid := Value; 
    if FGrid <> nil then begin 
     FGrid.FreeNotification(Self); 
     FGrid.OnTitleClick := GridTitleClick; 
    end; 
    end; 
end; 

procedure TDBGridSorter.SetSortColumn(const Value: TColumn); 
const 
    cOrder: array[Boolean] of string = ('˄', '˅'); 
var 
    descending: Boolean; 
    S: string; 
begin 
    if FSortColumn <> nil then begin 
    S := FSortColumn.Title.Caption; 
    if StartsStr(cOrder[false], S) or StartsStr(cOrder[true], S) then begin 
     Delete(S, 1, 2); 
     FSortColumn.Title.Caption := S; 
    end; 
    end; 
    FSortColumn := Value; 
    if FSortColumn <> nil then begin 
    SortByField(FSortColumn.FieldName, descending); 
    FSortColumn.Title.Caption := Format('%s %s', [cOrder[descending], FSortColumn.Title.Caption]); 
    end; 
end; 

procedure TDBGridSorter.SortByField(const FieldName: string; out Descending: 
    Boolean); 
var 
    cds: TClientDataSet; 
    curIndex: TIndexDef; 
    N: Integer; 
begin 
    cds := DataSet; 
    if cds <> nil then begin 
    descending := false; 
    N := cds.IndexDefs.IndexOf(cds.IndexName); 
    if N >= 0 then begin 
     curIndex := cds.IndexDefs[N]; 
     if SameText(FieldName, curIndex.Fields) then 
     descending := not (ixDescending in curIndex.Options) 
    end; 
    { make sure the index exists } 
    CreateIndex(FieldName, descending); 
    cds.IndexName := MakeIndexName(FieldName, descending); 
    end; 
end; 
+0

+1,感謝DelphiTage.de的精彩演講! –

+0

@JeroenWiertPluimers,我的榮幸! –

1

您應該設置IndexName而不是IndexFieldNamesIndexFieldNames接受字段名稱並即時創建索引。

procedure TForm2.DBGrid1TitleClick(Column: TColumn); 
begin 
    case Column.Index of 
    0: if ClientDataSet1.IndexFieldNames='objname' then 
     ClientDataSet1.IndexName:='objnameDESC' 
    else 
     ClientDataSet1.IndexFieldNames:='objname'; 
    1: if ClientDataSet1.IndexFieldNames='SUM(cd.worktime)' then 
     ClientDataSet1.IndexFieldNames:='SUM(cd.worktime) DESC' 
    else 
     ClientDataSet1.IndexFieldNames:='SUM(cd.worktime)'; 
    end; 
end; 
0

我的許多程序都需要這個,所以我寫了它建立兩個指數的每個字段數據集中

Procedure BuildIndices (cds: TClientDataSet); 
var 
i, j: integer; 
alist: tstrings; 

begin 
with cds do 
    begin 
    open; 
    for i:= 0 to FieldCount - 1 do 
    if fields[i].fieldkind <> fkCalculated then 
    begin 
    j:= i * 2; 
    addindex ('idx' + inttostr (j), fieldlist.strings[i], [], '', '', 0); 
    addindex ('idx' + inttostr (j+1), fieldlist.strings[i], [ixDescending], '', '',0); 
    end; 
    alist:= tstringlist.create; 
    getindexnames (alist); 
    alist.free; 
    close; 
    end; 
end; 

在這個階段的一般步驟,我已經索引IDX0和IDX1爲先字段,第二個字段的idx2和idx3等。

然後,在其中顯示的DBGrid(這裏的活性查詢稱爲qShowFees)

procedure TShowFees.DBGrid1TitleClick(Column: TColumn); 
var 
n, ex: word; 

begin 
n:= column.Index; 
try 
    dbGrid1.columns[prevcol].title.font.color:= clNavy 
except 
end; 

dbGrid1.columns[n].title.font.color:= clRed; 
prevcol:= n; 
directions[n]:= not directions[n]; 

ex:= n * 2; 
if directions[n] then inc (ex); 
with qShowFees do 
    try 
    disablecontrols; 
    close; 
    indexname:= 'idx' + inttostr (ex); 
    open 
    finally 
    enablecontrols 
    end; 
end; 

「路線」的形式是布爾值的形式陣列「記住」其方式每一列當前排序(升序或降序),所以第二次單擊dbgrid的標題欄將導致網格以與之前排序相反的方式排序。 'Prevcol'是一個表單變量,用於存儲當前選中的列;這是在調用之間保存的,所以下次用戶打開表單時,它將按照以前離開時的相同方式進行排序。