Как стать автором
Обновить

Комментарии 35

Ну что вы человека то минусуете? Хоть напишите за что?
Как минимум за нечитаемую простынку из кода на несколько экранов? Нужно выделить самое главное, а остальное — ссылкой :)
Ну, согласна, есть такое дело. Тоже самое, говорила ему.
Статья оформлена ужасно: метод на 7 экранов и, на одну строчку кода, по три пустых строки. Ни один уважающий себя разработчик, такой код даже смотреть не будет, не то что разбираться. Это насколько ж надо не уважать своих читателей…
А, ну и в любом случае, я не минусовал :) Не имею такой привычки. Только плюсики тем статьям, что понравились.
ещё бы было здорово посмотреть на результат…
В статье слишком много Bold текста… Пожалуйста, поправьте как-нибудь ;)
Я правильно понял, что этот метод будет работать только если TreeView сразу отображает всю иерархию? Если да, то такой подход реально будет полезен в довольно ограниченном количестве случаев. Вряд ли Вы будете загружать сразу всю иерархию в память и строить по ней дерево. Да и проход по всем имеющимся ресурсам тоже не лучший вариант, поскольку это приведет к загрузке всех ресурсов (которых может быть сотни).
Но в любом случае, подход интересный и спасибо за статью :)
Поиск происходит по коллекции ItemsSource которую отображает TreeView.
и как бы при поиске все дочерние элементы то же будут загружены.
Как бы там вы поиск не организовывали окажутся в памяти.
При моем подходе не происходит построения визуального дерева пока не надо показать выбранный объект.
А проход по ресурсам которых тысячи ну да замедлит работу на доли секунды. Тут к сожалению без вариантов.
Зато универсально работает без дополнительных усилий разработчиков.

> P.S Автор поста, мой брат SergejSh — все вопросы и замечания к нему.

Ну да, моторолер не мой... Приведите код в порядок.
Отвыкайте использовать для конкатенации строк оператор "+". Пользуйтесь StringBuilder или String.Format
Почему? Чем использование StringBuilder и тем более String.Format в данном коде лучше, чем запись автора:

ItemText + " ";


да ещё настолько, что вы безаппеляционно предписываете использовать именно их?
Ключевое слово «Отвыкайте».
Привыкнув писать, пропустите место, где будет отжираться лвиная доля памяти.
Сегодня он играет джаз, а завтра Родину продаст? Понятно.
полностью согласен отвыкать надо
Обычно использую StringBuilder когда надо собрать большой текст и потери заметны.
а тут по большому счету копейки. Не в этом суть поста

Статья интересна до первых строк кода — дальше читать не смог.

Оформляйте статью не как писатель, а как читатель, и тогда к вам потянутся.
НЛО прилетело и опубликовало эту надпись здесь
Я крайне не рекомендую искать бизнес-объекты в их визуальных представлениях.
По поводу поиска объектов в их представлении
Я ищу объект по его представлению. Или даже по части строки в этом представлении.
В последней версии кода (вечером выложу) сделал возможность разработчику указывать те свойства объекта по которым нужно искать в данном объекте
Мне кажется место таким статьям исключительно на CodeProject и ему подобным.
вот это правильно. Автор уже пытался переместить туда на английском, но прежде решил что реакция сообщества хабра тоже интересна.
1) Я вряд ли смогу так свободно понимать суть критики на английском.
2) Здесь тоже есть люди которым это интересно

Туда помещу как знающий чел проверит мой перевод.
В большинстве случаев, во всяком случае мне, приходится искать не только по тексту отображаемому в дереве но и по Id или некому другому полю. Учитывая это, плюс использование MVVM гораздо логичнее иметь таки в отображаемом объекте свойства IsExpanded и IsSelected, при этом они никоим образом не связаны с бизнес-логикой.
Если не нравится добавление свойств, то можно сделать Attached свойство с поддержкой двустороннего биндинга для TreeView и выставлять/получать SelectedItem через него.
На случай необходимости поиска по другому полю у меня тоже есть такая возможность.
Просто в статью не попало.
Лично я выбрал способ с переносом свойств IsExpanded/IsSelected, а также IsVisible, в модель данных. У меня применяется виртуализация данных, дочерние узлы создаются на лету когда родительский узел развернут, и его контейнер становится видимым. Для БД с сотней тысяч узлов по другому и не прокатывает. Да, можно придраться, что для такого количества данных — дерево это плохой выбор, но там еще есть сортировка, группировка, фильтрация и поиск, поэтому дерево получается вполне себе удобным. Поиск узла сделан через отдельную структуру данных — карту дерева, набор экземпляров реализаций интерфейсов:

interface ITreeNavigationHint
{
// Получить идентификатор родительского узла.
// nodeId — идентификатор дочернего узла.
// Если узел корень — возвращается null, в случае ошибки — исключение.
object GetParentId(object nodeId);
}

+

Dictionary<Type, ITreeNavigationHint> m_TreeMap;

Через карту восстанавливается путь от листа к корню, а потом проходится в прямом направлении. Так же в каждом узле есть не только коллекция дочерних элементов, но и словарь в котором дочерний узел ассоциирован со своим идентификатором.

А прокрутка к контейнеру элемента выглядит так:

private static readonly Action m_BringIngexIntoView =
(Action)Delegate.CreateDelegate(
typeof(Action),
typeof(VirtualizingStackPanel).GetMethod(«BringIndexIntoView»,
BindingFlags.Instance | BindingFlags.NonPublic));

public void BringItemIntoView(TreeNode node)
{
Contract.Requires(null != node);

// Развертка родительской ветки узлов IsExpanded = true.
node.ExpandParentBranch(false);

List path = new List(10);
while (null != node)
{
path.Add(node);
node = node.Parent;
}

ItemsControl current = this;
for (int i = path.Count — 1; i >= 0; i--)
{
var container = GetTreeViewItem(current, path[i]);
if (null == container) continue;
current = container;
}

current.BringIntoView();
}

private TreeViewItem GetTreeViewItem(ItemsControl container, object item)
{
if (null == container) return null;

var control = container as Control;
var template = container.Template;
if (null == template) return null;

container.ApplyTemplate();
var presenter = template.FindName(«ItemsHost», container) as ItemsPresenter;

if (null == presenter)
presenter = container.FindVisualChild();
if (null == presenter) return null;

presenter.ApplyTemplate();

var itemsHostPanel = presenter.FindVisualChild();
if (null == itemsHostPanel || !itemsHostPanel.IsItemsHost)
return null;

var virtualizingPanel = itemsHostPanel as VirtualizingStackPanel;

if (null == virtualizingPanel ||
!VirtualizingStackPanel.GetIsVirtualizing(virtualizingPanel))
{
var result = container.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (null != result) result.BringIntoView();
return result;
}

// Очень плохо, но другого пути выяснить положение
// элемента с учетом сортировки, группировки и т.д. — нет.
int index = container.Items.IndexOf(item);
if (index < 0) return null;

var children = virtualizingPanel.Children;

m_BringIngexIntoView(virtualizingPanel, index);
return container.ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
}
Этот блог я даже не видел, все что аналогично в нем FindVisualChild но таких множество в инете.
Идея похожа но… весьма отдаленно.
Собственно ключевая строчка: m_BringIngexIntoView(virtualizingPanel, index);
Этот код надежно создаёт контейнер для элемента дерева, без необходимости вызова UpdateLayout. UpdateLayout — очень опасная штука при работе с виртуализированным деревом. Вызов может запросто «выкинуть» невидимые контейнеры, в числе которых могут оказаться только что созданные.
Спасибо.
Обязательно буду разбираться так как часть BringIngexIntoView самая проблемная.

У нас существует достаточно много работающего кода в котором в treeView отображается иерархическая коллекция бизнес объектов и в которых нет и не должно быть свойств IsSelected, IsExpanded.
некоторые из них весьма большие.
И у всех из них одномоментно появился работающий поиск без всякого усилия со стороны разработчиков(кроме меня).
Можно конечно каждый раз при получении ItemSource автоматически создавать вьюмодельную коллекцию оболочку, но извините а сколько времени это займет когда коллекция имеет 1000 узлов. Ох долго.
В общем это не мой путь.
А за пример кода спасибо посмотрю разберусь что нибудь очень возможно и скопипащу.
А можно пример строчки кода как вы используете выделение нужного элемента. Т.е. есть заведомо известный объект который надо выделить в дереве. В моем случае это будет как SelectedObject = objects.First(); а у вас?
Вообще вызов из OnSelectedObjectChanged там я проверяю, на случай если объект уже найден и путь на него есть ну и если нет то вызываю вот эту функцию.
 public void SelectItem()
        {
            List<int> path = new List<int>();
            FindedPath = FindPathByObject(this.ItemsSource, path);//найти путь для объекта
            if (FindedPath != null)
            {
               
                failCount = 0;//сбросили счетчик падений
                SelectNodeByPath(this, FindedPath);
            }
        }
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории