Delphi VirtualTreeView Сортировать несколько столбцов? - PullRequest
1 голос
/ 15 декабря 2010

Я слышал много похвал о компоненте VirtualTreeView и рассматривал возможность его использования в процессе переписывания, который мы делаем.В настоящее время мы используем StringGrid.

Я не могу найти способ сортировки нескольких столбцов, хотя сортировка по одному столбцу работает отлично.Есть ли способ сделать что-то похожее на щелчок по столбцу 1> сортировка, Ctrl + щелчок по столбцу 2> сортировка столбца 2 после столбца 1 и т. Д.

В частности, я хочу отсортировать как минимум три столбца: номер заказа на поставку,Позиция, Выпуск.

Заранее спасибо за помощь!

Вот код (слегка упрощенный), с которым я тестирую теорию (не из того же проекта, на который ссылаются выше):

Примечание: После вашего обновления я также отредактировал свой код, чтобы показать, каким он является в данный момент.Ниже я опубликовал результаты сортировки:

type
  PBatchDetails = ^TBatchDetails;
  TBatchDetails = record
    TheBatchKey
    OperationKey,
    PO,
    Line,
    Release,
    Temp,
    Notes : String;
    TransDate : TDateTime;
end;

....
Sorting_Columns: array of TColumnIndex;
....
procedure TForm1.TreeHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo);
var
  I: Integer;
begin
  if not CtrlDown then //function I have to test Ctrl state.
  begin
    setlength(Sorting_Columns,0);
  end;
  SetLength(Sorting_Columns,length(Sorting_Columns)+1);
  Sorting_Columns[Length(Sorting_Columns)-1] := HitInfo.Column;
  tree.SortTree(HitInfo.Column,Sender.SortDirection,True);
  if Sender.SortDirection=sdAscending then
    Sender.SortDirection:=sdDescending
  else
    Sender.SortDirection:=sdAscending
end;


procedure TForm1.TreeCompareNodes(Sender: TBaseVirtualTree; Node1,
  Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
var
  BatchRec1 : PBatchDetails;
  BatchRec2: PBatchDetails;
  I: Integer;
begin
  if length(Sorting_Columns) > 0 then
  begin
    BatchRec1 := Tree.GetNodeData(Node1);
    BatchRec2 := Tree.GetNodeData(Node2);
    if (not Assigned(BatchRec1)) or (not Assigned(BatchRec2)) then
      Result:=0
    else
    begin
      for I := High(Sorting_Columns) downto 0 do
      begin
        case Sorting_Columns[i] of
          0,1: Result := Result + CompareDate(BatchRec1.TransDate,BatchRec2.TransDate); // col 0 is Date and col 1 is Time.
          2: Result := Result + CompareText(BatchRec1.OperationKey,BatchRec2.OperationKey);
          3: Result := Result + CompareText(BatchRec1.PO,BatchRec2.PO);
          4: Result := Result + CompareText(BatchRec1.Line,BatchRec2.Line);
          5: Result := Result + CompareText(BatchRec1.Release,BatchRec2.Release);
          6: Result := Result + CompareText(BatchRec1.Temp, BatchRec2.Temp);
          7: Result := Result + CompareText(BatchRec1.Notes,BatchRec2.Notes);
        end; //end case;
        if Result <> 0 then
          Break;
      end;
    end;
  end;
end;

Это дало следующие результаты (я показываю только три столбца, которые я пытаюсь отсортировать здесь):

Когда изначальнозагружено:
PO Line Release
153 7 2
153 7 1
153 1 1
153 1 2
153 4 1
153 6 2
153 6 1
120 3 2
120 3 1
153 2 1
153 4 2
120 2 1
153 4 1
120 1 1
153 31
153 2 1
111 2 1
111 1 5
111 1 1
111 4 2
111 3 1
111 4 1
111 1 3
111 1 2
111 1 4

После первого щелчка
Отключение PO-линии
111 2 1
111 1 5
111 1 1
111 4 2
111 3 1
111 4 1
111 1 3
111 1 2
111 1 4
120 3 2
120 3 1
120 2 1
120 1 1
153 7 2
153 7 1
153 1 1
153 1 2
153 4 1
153 6 2
1536 1
153 2 1
153 4 2
153 4 1
153 3 1
153 2 1

После второго щелчка
Отпуск линии PO
153 7 2
153 7 1
153 6 2
153 6 1
153 4 1
153 4 2
153 4 1
111 4 2
111 4 1
153 3 1
120 3 2
120 3 1
111 3 1
1532 1
153 2 1
120 2 1
111 2 1
153 1 1
153 1 2
120 1 1
111 1 5
111 1 1
111 1 3
111 1 2
111 1 4

После третьего нажатия
Отключение PO-линии
111 1 1
120 11
153 1 1
111 2 1
120 2 1
153 2 1
153 2 1
111 3 1
120 3 1
153 3 1
111 4 1
153 4 1
153 4 1
153 6 1
153 7 1
111 1 2
153 1 2
120 3 2
111 4 2
153 4 2
153 6 2
153 7 2
111 1 3
111 1 4
111 1 5

Спасибо за ваше время!

Ответы [ 2 ]

2 голосов
/ 15 декабря 2010

Отключить все параметры автосортировки в целом. Затем вам нужно реализовать OnCompareNodes вместе с событиями OnHeaderClick.

Вот, надеюсь, рабочий код (я только что сделал быстрый тест:)

Цель - сохранить столбцы сортировки в некоторой переменной (Sorting_Columns). Эту переменную можно передать в событии OnHeaderClick.
В событии OnCompareNodes, которое будет вызвано после вызова функции SortTree, переберите переменную из последнего добавленного столбца в первый добавленный и в параметр Result передайте первое ненулевое сравнение результат. Теперь по-человечески - вам следует пройтись по столбцам в обратном порядке, поскольку они были «выбраны» и проверить, совпадают ли они, если да, перейти к ранее выбранному, если не разорвать цикл и передать результат. Обратите внимание, что вы сравниваете два узла (строки) в одном обращении к событию, в чем причина итерации и последующего сравнения столбцов сортировки.

type
  PRecord = ^TRecord;
  TRecord = record
    ID: integer;
    Text_1: string;
    Text_2: string;
    Text_3: string;
    Date: TDateTime;
  end;

...

var Sorting_Columns: array of TColumnIndex;

...

procedure TForm1.VirtualStringTree1CompareNodes(Sender: TBaseVirtualTree;
  Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
var Actual_Index: integer;
    Data_1: PRecord;
    Data_2: PRecord;

begin
  if Length(Sorting_Columns) > 0 then
    begin
      Data_1 := VirtualStringTree1.GetNodeData(Node1);
      Data_2 := VirtualStringTree1.GetNodeData(Node2);

      if Assigned(Data_1) and Assigned(Data_2) then
        for Actual_Index := High(Sorting_Columns) downto 0 do
          case Sorting_Columns[Actual_Index] of
            0: Result := Result + Data_1^.ID - Data_2^.ID;
            1: Result := Result + CompareStr(Data_1^.Text_1, Data_2^.Text_1);
            2: Result := Result + CompareStr(Data_1^.Text_2, Data_2^.Text_2);
            3: Result := Result + CompareStr(Data_1^.Text_3, Data_2^.Text_3);
            4: Result := Result + CompareDateTime(Data_1^.Date, Data_2^.Date);
          end;

      if Result <> 0 then
        Break;
    end;
end;
0 голосов
/ 07 марта 2019

Слегка измененный код от @ user532231 для получения рабочего решения

type
  PRecord = ^TRecord;
  TRecord = record
    ID: integer;
    Text_1: string;
    Text_2: string;
    Text_3: string;
    Date: TDateTime;
  end;

...

var Sorting_Columns: array of TColumnIndex;

...

procedure TForm1.VirtualStringTree1CompareNodes(Sender: TBaseVirtualTree;
  Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
var Actual_Index: integer;
    Data_1: PRecord;
    Data_2: PRecord;
    Matrix : array of integer;
    I: Integer;
begin
  if Length(Sorting_Columns) > 0 then
    begin
      Data_1 := VirtualStringTree1.GetNodeData(Node1);
      Data_2 := VirtualStringTree1.GetNodeData(Node2);

      if Assigned(Data_1) and Assigned(Data_2) then
        begin
          SetLength(Matrix,Length(Sorting_Columns));
          for Actual_Index := 0 to High(Sorting_Columns) do
            begin
              case Sorting_Columns[Actual_Index] of
                0: Matrix[Actual_Index] := Data_1^.ID - Data_2^.ID;
                1: Matrix[Actual_Index] := CompareStr(Data_1^.Text_1, Data_2^.Text_1);
                2: Matrix[Actual_Index] := CompareStr(Data_1^.Text_2, Data_2^.Text_2);
                3: Matrix[Actual_Index] := CompareStr(Data_1^.Text_3, Data_2^.Text_3);
                4: Matrix[Actual_Index] := CompareDateTime(Data_1^.Date, Data_2^.Date);
              end;
            end;
          for I := 0 to Length(Matrix) - 1 do
            if (Matrix[i] <> 0) then
              begin
                Result:=Matrix[i];
                break;
              end;
          SetLength(Matrix,0);
        end;      
    end;
end;

Разница в том, что вам нужно запомнить результат сравнения каждого столбца, а затем вернуть первое наиболее значимое ненулевое значение (наиболее значимым является столбец, который был добавлен к сортировке первым). Вам не нужно зацикливаться от верхнего к низшему столбцу. Этот код нуждается в процедуре ОП TreeHeaderClick для добавления / удаления столбцов в Sorting_Columns.

Здесь направление сортировки всегда одинаково для всех столбцов. Должно быть довольно легко реализовать направление сортировки, путем изменения результата сравнения каждого столбца в соответствии с его направлением сортировки, по возрастанию или по убыванию. Я не пробовал это.

...