none
WPF как правильно построить TreeView RRS feed

  • Вопрос

  • День добрый,

    Имеется список объектов класса Project.

    public class Project { public string Name { get; set; } public Types types { get; set; } public Instances instances { get; set; }

    ... } public class Types { public string Name { get; set; } public List<ProjectType> projectType { get; set; }

    ... } public class Instances { public string Name { get; set; } public List<ProjectInstance> projectInstance { get; set; }

    ... }

    Требуется построить визуальное дерево в TreeView, следующего вида:

    Project1
        Types
            Type1
            Type2
            ...
        Instances
            Instance1
            Instance2
            ...
    
    Project2
        Types
            Type1
            Type2
            ...
        Instances
            Instance1
            Instance2
            ...
    ...
    

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

    Спасибо


Ответы

Все ответы

  • Забыл упомянуть, что класс Project генерируется из схемы, и модифицировать его ручками не есть хороший вариант.

    Частично решить вопрос удалось применением шаблона ниже,  

    <Grid.Resources>
        <HierarchicalDataTemplate 
            x:Key="projectsTemplate">
            <TreeViewItem Header="{Binding Path=Name}" 
                          IsExpanded="True">
                <TreeViewItem.Items>
                    <TreeViewItem Header="Types" 
                                  ItemsSource="{Binding Path=types}"
                                  IsExpanded="True">
                    </TreeViewItem>
                    <TreeViewItem Header="Instances" 
                                  ItemsSource="{Binding Path=instances}"
                                  IsExpanded="True">
                    </TreeViewItem>
                </TreeViewItem.Items>
            </TreeViewItem>
        </HierarchicalDataTemplate>
    </Grid.Resources>
    
    <TreeView ItemsSource="{Binding Path=projects}"
              ItemTemplate="{StaticResource projectsTemplate}"
    />
    

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

    Интересно узнать ваше мнение! Спасибо

  • Вы привязываетесь к классу, а не к коллекции внутри этого класс. То есть:

    1. Привязку осуществляетй так - ItemsSource="{Binding Path=types.projectType}"

    2. Сделайте в вашем классе реализацию интерфейса INotifyPropertyChanged, что бы ваш TreeView мог обновлятся в случае изменения исходных классов.


    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    Отвечающий
  • Дополнение к ответу LXGDARK, по пункту два.

    Если в сами классы дописывать ничего нельзя, то сделайте реализацию интерфейса во второй части класса, которую можно поучить сделав класс partial.

    Отвечающий
  • LXGDARK, да все верно, по первому пункту - в разметке ошибка (торопился писал).

    Однако вопрос не в этом. С этим шаблоном, который я написал, невозможно выделить мышкой вложенный узел (например Types) - выделяется вся иерархия дерева до Project. Скрин экрана ниже. Тестовый проект тоже.


    Не знаю как тут прикрепить архив проекта, если не затруднит, скачайте отсюда

    TreeViewTest.zip

    Спасибо

  • Подождите еще ответа Алексея, но я подозреваю что при нынешнем строении классов просто задачу не решить. Могу вбросить идею, пока поковыряйте ее. Итак проблема в том, что внутри Project нет коллекции, а просто 2 класса внутри которых уже коллекция имеется. Вы решили эту проблему созданием шаблона, где намеренно разделили классы Instances и Types по ветвям, но такой подход не позволяет использовать автоматические механизмы TreeView. В результате дерево построено правильно, но Project выделяется целиком, так как считается одним объектом. Если классы изменять нельзя, то в качестве временного решения можно так:

    <HierarchicalDataTemplate 
        DataType="{x:Type TreeViewTest:Project}" ItemsSource="{тут мультиконвертер, который объединяет классы Instances и Types в коллекцию, например Object List}">
        <TextBlock Text="{Binding ...}"/>
    </HierarchicalDataTemplate>
    Ну и сделать еще отдельные HierarchicalDataTemplate и DataTemplate для остальных классов участвующих в построении дерева. Указание x:Type позволяет применять стиль автоматически, поэтому в TreeView ItemTemplate можно не задавать.

    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!


    Отвечающий
  • Да, просто не будет.

    Я использовал декораторы.

    Отвечающий
  • LXGDARK, да именно в этом дело, что Project считается одним объектом. Интересно было бы посмотреть пример реализации мультиконвертора, который объединяет классы Instances и Types в коллекцию. По моим 5 Points наверное Вы поняли, что опыта в WPF у меня столько, сколько можно получить из книжки "Троелсен Э. - Язык программирования C# 2010 и платформа .NET 4 - 2010"

    Алексей, я обещаю покурить в сторону декораторов... но надеюсь на Ваш обещанный пример. 

    Спасибо

    У вас получилось скачать проект по ссылке? 

    • Изменено nvkokorin 16 мая 2012 г. 7:58
  • К сожалению могу подсказать только идею. На C# не пишу, поэтому кода дело доходит до тонкостей ООП в С# я бессилен.

    Думаю вы вместе с Алексеем решите задачу наиболее удобным способом.


    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    Отвечающий
  • Пример скачался.

    отлично, Алексей, информации по той ссылке на декораторы хватило, чтобы понять суть.

    Я так понимаю все дороги ведут к тому, чтобы получить в результате коллекцию содержащую другие коллекции. ТОлько в этом случае встроенные средства TreeView корректно построят дерево.

    Ну или ручками в коде добавлять Item'ы в какую нибудь коллекцию и привязать её к TreeView (мне кажется это не хороший вариант).

    В любом случае подожду до вечера )

  • Что то ответ получился здоровый. Давайте, сюда я демо проект выложу.

    А описание вот здесь.

    • Помечено в качестве ответа Abolmasov Dmitry 23 мая 2012 г. 5:55
    Отвечающий
  • Офтоп :)

    Алексей нравится ваш стиль и явная любовь к тому, что в делает. Предлагаю наладить контакт, за кулисами форума с целью обмена опытом (хотя у вас его явно больше). Если что мой ящик lxgdark@mail.ru

    Да и кстати меня тоже зовут Алексей :)


    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    Отвечающий
  • круто, сегодня конечно гляну... но разбираться буду завтра )

    Спасибо

  • круто, сегодня конечно гляну... но разбираться буду завтра )

    Спасибо

    Пример работает, это факт.

    Сейчас попробовал применить этот способ к реальному классу Project (для примера я его значительно упростил). Так вот, в действительности наименования полей в коллекциях не совпадает (а-ля Name), и поле Name { get... пришлось дополнить проверками на тип и в зависимости от типа Item возвращать то или иное поле. Довольно громоздко должен заметить...

            public string Name
            {
                get
                {
                    Type t = Item.GetType();
                    if (Item is Project)
                    {
                        return (Item as Project).Name;
                    }
                    if (Item is Types)
                    {
                        return (Item as Types).Desc;
                    }
                    ...
                    if (Item is ProjectType)
                    {
                        return (Item as ProjectType).Name;
                    }
                    ...
                    return string.Empty;
                }
            }
    но идея хорошая, надо попробовать шаблоны прикрутить, чтобы избавится от этого...

  • Так я же в статье и указал, что примените DataTemplateSelector, который будет определять тип лежащий в Item и в зависимости от него применять разные шаблоны, как, например, сделано на первой показанной мной в этом обсуждении картинке.

    Отвечающий
  • Так я же в статье и указал, что примените DataTemplateSelector, который будет определять тип лежащий в Item и в зависимости от него применять разные шаблоны, как, например, сделано на первой показанной мной в этом обсуждении картинке.

    Алексей, а идея с мультиконвертером (я предлагал выше) в данной конкретной ситуации не подойдет? Просто я не могу написать пример реализации на C#. К тому же DataTemplateSelector, все равно потребует создание еще одного класса. В итоге создается ощущение, что этом конкретном случае с мультиконвертером даже проще.

    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    Отвечающий
  • Можно вечером попробовать. Вроде принципиальных проблем нет. Просто если структура классов исходная, то проще wrapper, если отображаемые поля классов отлиаются, надо добавлять картинки и прочие штуки, т.е. все равно применять DateTemplateSelector, то можно и с конверторами позаморачиваться.
    Отвечающий
  • Да пробую сейчас применить DateTemplateSelector, по идее с ним вообще не нужно будет поле Name.