Member 12226991 Ответов: 3

Как естественным образом отсортировать узлы древовидного представления?


напр.

Input:

TreeView
|_Node 1
|_Node 10
|_Node 2
|_Node 3

Output:

TreeView
|_Node 1
|_Node 2
|_Node 3
|_Node 10


Что я уже пробовал:

folderTreeView.TreeViewNodeSorter = new NodeSorter();
folderTreeView.Sort();


В классе NodeSorter должен быть естественный вид узлов.

BillWoodruff

Нужна дополнительная информация: всегда ли это число в конце ? Строки, показанные здесь, представляют собой текстовое свойство WinForm TreeNodes ?

Member 12226991

Цифры могут быть где угодно. В основном мне нужен метод сортировки, похожий на естественную сортировку, используемую в Проводнике Windows. Что касается второго вопроса: да.

3 Ответов

Рейтинг:
2

BillWoodruff

1 Вы получаете эти имена файлов из перечисления каталогов с помощью ?

2 пробовали ли вы использовать метод EnumerateFiles .NET 4 ?

3 пробовали ли вы использовать сортировку Linq с помощью OrderBy/Ascending/Descending ?

Более сложный способ справиться с этим-использовать 'SortedDictionary с пользовательским компаратором: приведенный здесь пример использует тот факт, что числовые последовательности символов не в конце строки сортируются в нужном порядке; только когда числовые символы находятся в начале строки, вам нужно сравнивать в "обратном порядке"."

public class SortFilesComparer : IComparer<string>
{
    public int Compare(string fname1, string fname2)
    {
        // number at the start of file name 1 ?
        var num1 = fname1.TakeWhile(char.IsDigit);

        if (num1.Count() != 0)
        {
            // number at the start of file name 2 ?
            var num2 = fname2.TakeWhile(char.IsDigit);

            if (num2.Count() != 0)
            {
                // we'll need both string and int
                string n1s = new string(num1.ToArray());
                string n2s = new string(num2.ToArray());

                int n1 = Convert.ToInt32(n1s);
                int n2 = Convert.ToInt32(n2s);

                // voodoo !
                if (n1s.Contains(n2s))
                {
                    if (n1 > n2)
                    {
                        return fname2.CompareTo(fname1);
                    }
                }
                else if (n2s.Contains(n1s))
                {
                    if (n1 < n2)
                    {
                        return fname2.CompareTo(fname1);
                    }
                }
            }
        }

        return fname1.CompareTo(fname2);
    }
}
Пример использования:
SortedDictionary<string, string> sd = new SortedDictionary<string, string>(new SortFilesComparer());

sd.Add("234hello", "3");
sd.Add("234567hello", "4");
sd.Add("1234567hello", "4");

sd.Add("hello 1", "2");
sd.Add("hello 10", "1");

sd.Add("100hello", "3");
sd.Add("101hello", "4");

sd.Add("10hello", "3");
sd.Add("1hello", "4");

sd.Add("hell10", "5");
sd.Add("hell1", "6");
Запустите код и изучите элементы 'sd.

Примечание: это было написано быстро, и, хотя все в порядке с этими тестовыми значениями, должно быть тщательно протестировано.

Мне не очень нравится, насколько сложен этот код, и есть один случай, с которым он не справляется ... можете ли вы догадаться, что это такое :)


Рейтинг:
1

Richard MacCutchan

Вам нужно добавить свой собственный метод сортировки, чтобы изменить порядок в зависимости от чисел в именах узлов. Когда числа сортируются как символьные строки, то "10" или "100" будут стоять перед "2". Sso вам нужно преобразовать их в числовые значения и отсортировать соответствующим образом.


Рейтинг:
0

MadMyche

По умолчанию NodeSorter сортирует строки на основе их значений и работает так, как задумано, основываясь на показываемых вами значениях.
Для сортировки с помощью другого метода вам нужно будет реализовать или владеть Compare метод; например, предоставленный MS Docs; последний блок кода в предоставленном примере
TreeView элемент.Свойство TreeViewNodeSorter (System.Окна.Формы) | Microsoft Docs[^]

// Create a node sorter that implements the IComparer interface.
public class NodeSorter : IComparer
{
    // Compare the length of the strings, or the strings
    // themselves, if they are the same length.
    public int Compare(object x, object y)
    {
        TreeNode tx = x as TreeNode;
        TreeNode ty = y as TreeNode;

        // Compare the length of the strings, returning the difference.
        if (tx.Text.Length != ty.Text.Length)
            return tx.Text.Length - ty.Text.Length;

        // If they are the same length, call Compare.
        return string.Compare(tx.Text, ty.Text);
    }
}
Теперь этот пример основан на том, что делается в остальной части приведенного примера, вам нужно будет написать свою собственную специализированную реализацию, основанную на ожидаемых значениях.
Возможно, вы могли бы использовать регулярное выражение или функцию string/Replace, чтобы удалить все нечисловые символы, привести их к целому числу, а затем сделать соответствующий возврат на основе целых чисел


BillWoodruff

+5