Медленно в TreeView в C#
У меня есть устаревшее приложение, написанное на C#, и оно отображает очень сложный treeview с 10 до 20 тысяч элементов.
в прошлом я столкнулся с аналогичной проблемой (но в C++), которую я решил с помощью возможности OWNERDATA, предлагаемой Win32 API.
есть ли аналогичный механизм в C#?
EDIT: план состоит в том, чтобы оптимизировать время создания, а также время просмотра. Метод, доступный через Win32 API, превосходен в обоих случаях поскольку это уменьшает время инициализации до нуля, а количество запросов на элементы ограничено только теми, которые видны в любой момент времени. Joshl: мы на самом деле делает именно то, что вы предлагаете, уже, но нам все еще нужно больше эффективности.
6 ответов
Я не верю, что .NET TreeView поддерживает то, что вы хотите, хотя этот тип модели поддерживается DATAGRIDVIEW .NET (см. VirtualMode свойства). TreeView позволит вам рисовать собственные узлы, но не позволит вам заполнять их из некоторого виртуального магазина.
Если возможно, вы можете рассмотреть возможность использования DataGridView для вашего приложения. Если нет, управление узлами вручную (Как упоминалось выше в joshl) может работать, если вы можете получить вокруг некоторых проблем с обновлением экрана должным образом, когда узлы расширяются. Помимо этого, вы можете проверить некоторых сторонних поставщиков, таких как этот (Divelements SandGrid), что может (акцент на might) поддерживать желаемый режим работы.
Примечание: SandGrid не поддерживается Divelements по состоянию на конец июля 2013 года.
один из методов повышения производительности-загрузка TreeNodes по мере расширения пользователем treeview. Обычно пользователю не требуется, чтобы на экране одновременно открывалось 20 000 узлов. Загрузите только уровень, который должен видеть пользователь, а также любую дочернюю информацию, необходимую для правильного отображения доступности пользователю (разверните значок, если существуют дети, подсчеты, значки и т. д.). Когда пользователь расширяет узлы, загружайте дочерние элементы как раз вовремя.
полезный совет от Кита: с winforms TreeView вам нужно иметь хотя бы один дочерний узел, или он не будет показывать expand [ + ], но затем вы обрабатываете событие TreeNodeExpanded, чтобы удалить этот фиктивный узел и заполнить дочерние элементы.
в нашем главном приложении WinForm у нас есть treeview, загруженный все в одном кадре:
- BeginUpdate()
- загрузить 20.000 узлов
- EndUpdate()
и до сих пор все хорошо. На самом деле это один из немногих компонентов, которые мы не заменяем сторонними.
производительность TreeView, по моему опыту, замедляется при загрузке узлов (одним выстрелом или по требованию) без вызова Begin/EndUpdate (), особенно если ваши узлы отсортированы, но если вы вызываете Begin/EndUpdate() правильно, вы не должны действительно получать проблемы с производительностью, связанные с самим компонентом.
Примечание: этот ответ недействителен при редактировании спрашивающим, говоря, что он уже делает такие вещи, но я решил все же опубликовать его для дальнейшего использования другими, ищущими по этой теме
когда я делал подобные вещи в прошлом, я имел тенденцию выбирать наивный стиль ленивой загрузки.
- использовать
TreeNode.Tag
свойство для хранения ссылки, которую можно использовать для поиска детей - использовать
TreeView.BeforeExpand
событие для заполнения дочерние узлы - дополнительно использовать
TreeView.AfterCollapse
событие для их удаления. - чтобы получить [ + ] / [ - ] коробки, чтобы появиться, лучший способ, который я нашел, это создать одноэлементный манекен
TreeNode
который добавляется как дочерний ко всем незаселенным узлам, и вы проверяете его существование перед заполнениемBeforeExpand
.
есть один способ сделать TreeView работать намного лучше, и это создать все подузлы и подключить их вместе а то добавьте узлы в TreeView. Если речь идет о графической производительности.
TreeView tree = new TreeView();
TreeNode root = new TreeNode("Root");
PopulateRootNode(root); // Get all your data
tree.Nodes.Add(root);
в противном случае загрузите их узел за узлом, используя OnTreeNodeExpanded.
для больших данных в программировании Windows c#, будь то в WPF или WinForms, я традиционно добавлял узлы динамически. Я загружаю начальный корень дерева + дети + внуки глубоко. Когда любой узел развернут, я загружаю узлы дерева, которые будут представлять внуки расширяющегося узла, если таковые имеются.
эта модель также хорошо работает с поиском данных. Если вы действительно загружаете данные из источника тысяч или миллионов записей, вы, вероятно, не хотите загружать все это впереди. Ни один пользователь не хочет ждать загрузки, и нет причин загружать данные, которые никогда не могут быть просмотрены.
У меня обычно загружается внуки или правнуки узел данных в фоновом потоке, а затем маршал эти данные обратно в поток пользовательского интерфейса и создавать и добавлять узлы. Это оставляет пользовательский интерфейс отзывчивым. Вы можете визуально украсить узлы дерева, чтобы указать, что они все еще загружаются для случая, когда пользователь опережает ваш IO хранилище данных.