Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > WPF и Silverlight > TreeListView и ObservableCollection


Автор: Dem0n13 18.5.2011, 00:42
Здравствуйте!

Нашел в интернете контрол, унаследованный от treeview:
http://blogs.windowsclient.net/ricciolocristian/archive/2008/03/22/a-complete-wpf-treelistview-control.aspx

В примере, с которым он поставлялся, использовались xml-данные.
Помогите пожалуйста с привязкой этого компонента к экземпляру вот такого класса:

Код

... где-то при создании окна
 DiffTree DT = DiffTree.Instance;
 DT.Create() ;
.....


public class DiffTree : ObservableCollection<DiffItem>
    {
        #region Реализация синглтона
        protected DiffTree() { }

        private static DiffTree _instance;
        public static DiffTree Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new DiffTree();
                return _instance;
            }
        }
        #endregion

        DiffItem _buffer;

        public void Create()
        {
            ....
            _buffer = new DiffItem();
            Add(_buffer);
            Add(_buffer);
            Add(_buffer);
            Add(_buffer);
            Add(_buffer);
        }
    }

public class DiffItem
    {
        // 2
        public string Name { get; set; }
        public ObservableCollection<DiffItem> Children;
        DiffItem Parent;
    }
       


Сам контрол выглядит так:
Код

не знаю, что здесь писать. В оригинале было ItemsSource="{Binding Source={StaticResource data},XPath=/opml/body/outline/outline}"    ItemTemplate="{StaticResource dt}"   

<DC:TreeListView Name="qq" Width="Auto" Height="300" ItemsSource="{Binding Path=DT}" >  
    <DC:TreeListView.Columns>
        <GridViewColumn Header="Title">
            <GridViewColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Image></Image>
                        <TextBlock Text="{Binding Path=Name}"></TextBlock>
                    </StackPanel>
                </DataTemplate>
            </GridViewColumn.CellTemplate>
        </GridViewColumn>
    </DC:TreeListView.Columns>
</DC:TreeListView>


Задача состоит в том, чтобы отобразить в иерахическом порядке сам объект DT и его потомков Children

Автор: -Mikle- 18.5.2011, 06:16
В ItemsSource прописываете привязку к первоисточнику, то есть к корневым элементам дерева, а в ItemTemplate - шаблон данных. В качестве последнего, для отображения дерева, используется HierarhicalDataTemplae. Подробнее http://msdn.microsoft.com/ru-ru/library/system.windows.hierarchicaldatatemplate.aspx.

Автор: Dem0n13 18.5.2011, 06:53
-Mikle-, в ItemSource уже писал 
ItemsSource="{Binding Source={StaticResource DT}}"
ItemsSource="{Binding DT}"
ItemsSource="{Binding Source=DT}"

где DT - существующий объект класса DiffTree : ObservableCollection<DiffItem>
И все равно, проверяю, что привязывается - либо буквы "DT", либо вообще ничего((

Что я делаю не так?

Автор: -Mikle- 18.5.2011, 08:17
Где у вас свойство DT объявлено? Оно публичное?

Автор: Dem0n13 18.5.2011, 16:26
DT - это экземпляр ObservableCOllection, описан в классе главного окна, где и используется:

Код

    public partial class MainWindow : Window
    {
        public DiffTree DT;
        public MainWindow()
        {
            InitializeComponent();
            //WelcomeWindow WW = new WelcomeWindow();
            //WW.ShowDialog();
            DT = DiffTree.Instance;
......
        }
.........
      }


А еще только что попробовал 

Код

ItemsSource="{Binding Source=DT, Path=Instance}"


но все равно не работает. Проверяю все вышеперечисленные методы так:
MessageBox.Show(qq.ItemsSource.ToString());
выдает исключения, что объекта нет.

Автор: -Mikle- 18.5.2011, 16:40
на выбор:

вариант 1)
Код

        public MainWindow()
        {
            InitializeComponent();
            DT = DiffTree.Instance;
            treeView.ItemsSource = DT;
        }


вариант 2)
Код

        public MainWindow()
        {
            InitializeComponent();
            DT = DiffTree.Instance;

            // ложим себя в контекст данных
            this.DataContext = this;
        }

        // НУЖЕН PROPERTY, а не field
        public DiffTree DT { get; set; }
Теперь это будет работать:
Код

<TreeView ItemsSource="{Binding DT}" ..........


вариант 3)
Код

<Window Name="topRoot" ...............>
.............
<TreeView ItemsSource="{Binding DT, ElementName=topRoot}" ..........


Есть еще много способов как можно задать значение ItemsSource. 

примечание: Во всех трех вариантах не реализовано автообновления TreeView при изменении списка.

Автор: Dem0n13 18.5.2011, 18:11
У меня задача сложнее, мне нужна двунаправленная связь. Но пока что, даже одноправленная не работает  smile 

Может все дело в компоненте TreeListView? Но вроде унаследован он от TreeList, а все равно, примеры выше не работаю, хотя и все сдвинулось с мертвой точки. Теперь MessageBox.Show(qq.ItemsSource.ToString()); показывает, что привязка есть, а вот шаблон иерархических данных не работает. Что я делаю не так?

Может поможет исходник разобраться? Помогите, пожалуйста, уже 2 дня бьюсь с привязками

Добавлено:
И еще... Чем отличается 
this.DataContext = this; в c#
от
DataContext="{Binding RelativeSource={RelativeSource Self}}" в xaml?

У меня привязывается только с первым вариантом

Автор: -Mikle- 20.5.2011, 06:45
Щас посмотрю проект.

Автор: -Mikle- 20.5.2011, 08:54
1) У тебя бардак в настройках солюшена, не знаю как так у тебя получилось. Открой Configuration Manager солюшена. У всех проектов должна стоять галочка Build и обязательно, чтоб стоял одинаковый Platform у всех.
2) Binding работает только со свойствами. Children в твоем коде это поле класса, а не свойство.
3) Я эксперимент проводил на обычном TreeView, твой не заработал и я не разбирался с ним. Придется смотреть тебе мануал на этот компонент.
Код

<HierarchicalDataTemplate x:Key="DT_HTemplate" ItemsSource="{Binding Children}">
    <TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>

<TreeView ItemsSource="{Binding DT}" ItemTemplate="{StaticResource DT_HTemplate}" />


Вот уже на этом этапе все заработало.

Совет: когда имеешь дело с биндингом, возьми за правило реализовывать интерфейс INotifyPropertyChanged в моделях данных и DependencyProperty для типов наследованных от DependencyObject (все UI-элементы и еще много других от него наследованы). Для списков по возможности ObsevableCollection<?> (ты и так ее использовал тут) или любую другую реализующую интерфейс INotifyCollectionChanged.

PS: на всякий случай, фраза "{Binding Source={StaticResource DT_HTemplate}}" безсмыслена и полностью эквивалентна "{StaticResource DT_HTemplate}"

Автор: Dem0n13 20.5.2011, 16:36
Спасибо за потраченное время! В общем, с TreeView я разобрался, и правда все работает. Сделал так:
Код

public MainWindow()
{
    InitializeComponent();
    // присваиваю как раз-таки свойство
    TLW.DataContext = DiffTree.Instance; // TLW - это treeview
    // создаю тестовое заполнение
    DiffTree.Instance.Create();
}

Теперь насчет TreeListView. Разбираться с тем, что было не стал, нашел другой пример с msdn. Объявляю следующим образом:
Код

<l:TreeListView x:Name="TLW" ItemsSource="{Binding}" MouseDown="TreeListView_MouseDown"/>

Шаблон практически без изменений выдает только первый уровень дерева.
Я понимаю, что теперь нужно где-то сослаться на наш HierarchicalDataTemplate, но не знаю где...
Начал разбирать шаблон от TreeListView, увидел следующие вещи:
1. Из реализации шаблона первой ячейки (которая со иконкой +):
Код

<DataTemplate.Triggers>
    <DataTrigger Binding="{Binding Path=HasItems, RelativeSource={RelativeSource AncestorType={x:Type l:TreeListViewItem}}}" Value="False">
        <Setter TargetName="Expander" Property="Visibility" Value="Hidden"/>
    </DataTrigger>
</DataTemplate.Triggers>

Вот этот триггер срабатывает (все элементы стоят без плюсов). Естественно я нигде не определил, что свойство Children это и есть Items

2. Далее из шаблона TreeListViewItem:
Код

<ControlTemplate TargetType="{x:Type l:TreeListViewItem}">
    <StackPanel>
        <Border Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
            <GridViewRowPresenter x:Name="PART_Header" Content="{TemplateBinding Header}" Columns="{StaticResource gvcc}" />
        </Border>
        <ItemsPresenter x:Name="ItemsHost"/>
    </StackPanel>
    ....
</ControlTemplate>

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

Используемый контрол, на этот раз, унаследован уже от ListView. В оригинале заполнялся так:
Код

<l:TreeListView>
    <l:TreeListViewItem>
        <l:TreeListViewItem.Header>
        <x:Type TypeName="DependencyObject"/>
        </l:TreeListViewItem.Header>
        <l:TreeListViewItem>
            <l:TreeListViewItem.Header>
              <x:Type TypeName="Visual"/>
            </l:TreeListViewItem.Header>
        <l:TreeListViewItem/>
    <l:TreeListViewItem/>
<l:TreeListView/>

Как мне добить уже это дерево?  smile 

P.S.: обновленный исходник

Автор: Dem0n13 23.5.2011, 22:40
Ура! Решение данной проблемы нашел!
http://social.msdn.microsoft.com/Forums/en/wpf/thread/a9281c6d-d27a-4887-9990-dd3cf3e9350b

Краткое описание: в ресурсы вставил
Код

<HierarchicalDataTemplate ItemsSource="{Binding Children}" DataType="{x:Type lib:DiffItem}"/>

Всем спасибо!

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)