2016-11-28 9 views
0

У меня есть (странная) проблема сортировки с VirtualTreView (v 6.1.0/Delphi 10 Seattle). Я просмотрел более поздние версии VTV и не упоминается подобное поведение.Сортировка узлов в VirtualTreeView - OnCompareNodes не срабатывает для всех узлов, которые будут сравниваться

я выложу полный исходный код, но позвольте мне объяснить, что я пытаюсь выполнить:

vtv sorting nodes issue

  1. Есть узлы, которые я хотел, чтобы отсортировать
  2. Каждый узел имеет стадию - где этап узла определяет порядок сортировки узла (на этом этапе)
  3. Я хотел бы, чтобы узлы сортировались, когда конкретный этап выбирался по их значению сортировки сцены. Те узлы, которые не включены в сцену, должны быть установлены невидимыми.

Вот запись, я использую:

PRecord = ^TRecord; 
TRecord = record 
    SortOrder : array [0..4] of integer; 
    PositionAdded : integer; 
end; 

Вот как добавлены узлы:

procedure TVTVSortForm.FormCreate(Sender: TObject); 
var 
    vn : PVirtualNode; 
    pr : PRecord; 
begin 
    tree.NodeDataSize := SizeOf(TRecord); 

    vn := tree.AddChild(nil); 
    pr := PRecord(tree.GetNodeData(vn)); 
    pr.PositionAdded := vn.Index; 
    pr.SortOrder[0] := 0; //first in 0 - removed in later 
    pr.SortOrder[1] := -1; 
    pr.SortOrder[2] := -1; 
    pr.SortOrder[3] := -1; 
    pr.SortOrder[4] := 2; //third in 4 

    vn := tree.AddChild(nil); 
    pr := PRecord(tree.GetNodeData(vn)); 
    pr.PositionAdded := vn.Index; 
    pr.SortOrder[0] := 1; //second in 0 
    pr.SortOrder[1] := 0; //first in 1 
    pr.SortOrder[2] := 1; //second in 2 
    pr.SortOrder[3] := 1; //second in 3 
    pr.SortOrder[4] := 0; //first in 4 

    vn := tree.AddChild(nil); 
    pr := PRecord(tree.GetNodeData(vn)); 
    pr.PositionAdded := vn.Index; 
    pr.SortOrder[0] := 2; // third in 0 
    pr.SortOrder[1] := 2; //third in 1 
    pr.SortOrder[2] := 0; //first in 2 
    pr.SortOrder[3] := -1; //removed in 3 
    pr.SortOrder[4] := 1; //second in 4 

    vn := tree.AddChild(nil); 
    pr := PRecord(tree.GetNodeData(vn)); 
    pr.PositionAdded := vn.Index; 
    pr.SortOrder[0] := -1; //not in 0 
    pr.SortOrder[1] := 1; //second in 1 
    pr.SortOrder[2] := 2; //third in 2 
    pr.SortOrder[3] := 0; // first in 3 
    pr.SortOrder[4] := -1; // not in 4 

    tree.ValidateNode(nil, true); 
end; 

"Этапы" являются SortOrder индексы. SortOrder [x]: = y означает, что порядок узла на этапе X должен быть y. Например, SortOrder [2] = 0 означает, что на этапе 2 узел должен быть первым узлом. SortOrder [3] = -1 означает, что узел «не существует» на этапе 3.

Существует радиогруппа, имеющая 5 элементов (0 ... 4), представляющих сцену. При выборе стадии узлы должны быть отсортированы в соответствии с их значение SortOrder на стадии выбранного (я также скрываются узлы, которые не существуют в стадии:

procedure TVTVSortForm.rgFilterAndSortClick(Sender: TObject); 
begin 
    tree.Sort(tree.RootNode, 0, sdAscending); 

    tree.IterateSubtree(
    nil, 
    procedure (Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean) 
    var 
    d : PRecord; 
    begin 
    d := PRecord(tree.GetNodeData(Node)); 
    //tree.IsVisible[Node] := -1 <> d.SortOrder[rgFilterAndSort.ItemIndex]; 
    end, 
    nil); 
end; 

OnCompare выглядит следующим образом:

procedure TVTVSortForm.treeCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer); 
var 
    d1, d2 : PRecord; 
begin 
    d1 := PRecord(Sender.GetNodeData(Node1)); 
    d2 := PRecord(Sender.GetNodeData(Node2)); 

    if (-1 <> d1.SortOrder[rgFilterAndSort.ItemIndex]) AND (-1 <> d2.SortOrder[rgFilterAndSort.ItemIndex]) then 
    result := d1.SortOrder[rgFilterAndSort.ItemIndex] - d2.SortOrder[rgFilterAndSort.ItemIndex]; 
end; 

Поэтому я только сравнивающие те узлы, которые существуют в выбранном этапе (т.е. SortOrder [стадии] <> -1).

GetText показывает порядок сортировки для выбранного этапа + исходное положение узла в время добавления в дерево.

Теперь, что происходит, когда вы запускаете программу и перейти (выберите радиокнопку/щелчок) на стадии (3), порядок 1,0 вместо 0,1 (я опускаю «-1 "узлы).

Если после выбора этапа (3) вы выбираете (1), то (3) -> этап 3 сортируется в порядке. Но затем перейдите на этап (0), (2), (4), а порядок равен 1,2,0 вместо 0,1,2.

Любые идеи, почему это не работает (как и ожидалось)?

Я заметил, что когда два узла входят в OnCompare: если один из узлов имеет -1 для выбранного этапа, другой узел больше не будет сравниваться с остальными узлами, которые существуют на выбранном этапе. Как заставить другой узел по-прежнему согласовываться с другими узлами той же стадии?

+0

Когда 'SortOrder' равно -1 для одного узла, а не для другого, то' Result' не определено. Сравнение должно определять * полное упорядочение *; если вы не определяете сравнение между * all * возможными парами, то базовый алгоритм сортировки даст неожиданные результаты, такие как остановка до того, как все узлы будут отсортированы. –

+0

@Rob, извините, но результат всегда задан 0 (Node1 Torpedo

+0

Это дает ту же проблему. Результат нуля означает, что узлы равны. Предположим, что у вас есть узлы, а для узла 1 установлено значение -1. Узел 2 сравнится с ним равным, и так будет узел 3, потому что вы пропустили сравнение. По транзитивности это должно означать, что узел 2 равен узлу 3, но если ваша функция сравнения возвращает другой ответ, то у вас нет общего порядка, и алгоритм сортировки может выйти из строя. –

ответ

1

Omg, я пытался выяснить, что здесь не так в течение последних нескольких дней, и, конечно же, как только я разместил свою проблему здесь, я понял это.

OnCompare должен сравнивать «все» узлы - поэтому пропускать некоторые узлы (когда узел «не находится в стадии») - это то, что было не так с моим кодом.

Мне нужно убедиться, что этапный узел по-прежнему по сравнению с другими узлами той же ступени, когда он не сравнивается с согласованным узлом на разных этапах.

Наконец, я переписал OnCompare метод, как это, и на данный момент (у меня есть больше реальное применение мира для тестирования), все, кажется, хорошо:

procedure TVTVSortForm.treeCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer); 
var 
    d1, d2 : PRecord; 
begin 
    d1 := PRecord(Sender.GetNodeData(Node1)); 
    d2 := PRecord(Sender.GetNodeData(Node2)); 

    if (-1 = d1.SortOrder[rgFilterAndSort.ItemIndex]) then result := -1 
    else if (-1 = d2.SortOrder[rgFilterAndSort.ItemIndex]) then result := 1 
    else 
    if (-1 <> d1.SortOrder[rgFilterAndSort.ItemIndex]) AND (-1 <> d2.SortOrder[rgFilterAndSort.ItemIndex]) then 
     result := d1.SortOrder[rgFilterAndSort.ItemIndex] - d2.SortOrder[rgFilterAndSort.ItemIndex] 
    else 
    begin 
    //just testing if this ever happens 
    if (-1 = d1.SortOrder[rgFilterAndSort.ItemIndex]) then Inc(d1.PositionAdded); 
    if (-1 = d2.SortOrder[rgFilterAndSort.ItemIndex]) then Inc(d2.PositionAdded); 
    end; 
end; 

Тем не менее, честно говоря, это не то, что Я бы ожидал от того, как работает сравнение узлов.

+0

Можете ли вы объяснить более подробно то, что вы имеете в виду, _ «это не то, что я ожидаю от того, как работает сравнение узлов». _? Вы видите отличие от других алгоритмов сортировки? –

+0

@JoachimMarder: Думаю, я использовал плохую формулировку здесь. Я хотел сказать, что с первого взгляда я бы не ожидал, что мне все равно придется сравнивать узлы, в которых я «не заинтересован». Как только вы знаете, как работает сортировка - тогда, конечно, все имеет смысл. – Torpedo

 Смежные вопросы

  • Нет связанных вопросов^_^