3

並べ替え可能な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;

初めて列のタイトルをクリックすると、昇順で並べ替えが行われるため、ここまではすべて問題ありません。2回目にクリックすると、降順で並べ替えが実行されることを期待していますが、代わりに次のメッセージが表示されます。

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

私が間違っていることについて何か考えはありますか?

4

4 に答える 4

6

すでに TClientDataSet を使用しているので、まさにその目的のために私が作成したコンポーネントを利用することができます。インスタンスを作成し、その Grid プロパティを設定すると、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;
于 2012-09-24T15:08:03.477 に答える
2

間違った割り当て

誤った割り当てが行われたという事実は別として、「昇順」に戻すことはできません。

2 列の場合、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 = done)

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;
....
于 2012-09-25T03:38:44.550 に答える
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;
于 2012-09-24T19:33:32.770 に答える
0

私のプログラムの多くはこれを必要としているので、データセットの各フィールドに2つのインデックスを作成する一般的な手順を作成しました

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、2番目のフィールドに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;

「Directions」はブール値のフォーム配列であり、各列が現在どのようにソートされているか(昇順または降順)を「記憶」しているため、dbgridのタイトルバーをもう一度クリックすると、グリッドはソートされたのとは逆の方法でソートされます。前。'Prevcol'は、現在選択されている列を格納するフォーム変数です。これは呼び出しの間に保存されるため、ユーザーが次にフォームを開いたときに、前に残したのと同じ方法で並べ替えられます。

于 2012-09-25T05:19:54.170 に答える