TreeView не отображает иерархию объектов - PullRequest
2 голосов
/ 13 сентября 2009

У меня возникли серьезные проблемы при создании WPF TreeView с привязкой данных объекта.

Приложение является редактором конфигурационных файлов. Я определил структуру объекта, которую можно сериализовать в правильный формат XML.

У меня проблема с форматированием экземпляра объекта в TreeView, показывающего правильную иерархию. TreeView будет отображать только узел канала, и ничего больше.

public class Objects
{
    public List<Channel> Channels { get; set; }
}

public class Channel 
{
    public string Id { get; set; }
    public string Name { get; set; }
    public Reader Reader { get; set; }
    public Filters Filters { get; set; }
    public Router Router { get; set; }
    public Persister Persister { get; set; }
}

public class Filters : ArrayList
{
    public string StopOnFailure { get; set; }
}

public class Reader
{
    public string Id { get; set; }
    public string Name { get; set; }
}

Все дочерние классы Channel содержат свойства Id и Name. Класс Filters - это коллекция других типов с тем же определением свойства.

Вот XAML

 <Window.Resources>
    <ObjectDataProvider x:Key="data"/>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Channel}">
        <WrapPanel>
            <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
            <TextBlock Text=" [" />
            <TextBlock  Text="{Binding Path=Id}" />
            <TextBlock Text="]" />
        </WrapPanel>
    </DataTemplate>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Reader}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>
</Window.Resources>
<Grid>
    <TreeView Margin="12,12,12,96" Name="treeView1" ItemsSource="{Binding Source={StaticResource data}, Path=Channels}">
    </TreeView>
</Grid>

Код для создания экземпляра данных

Objects config;
var serializer = new XmlSerializer(typeof(Objects));
using (var stream = new FileStream(@"C:\test.xml", FileMode.Open))
{
    config = (Objects)serializer.Deserialize(stream);
}

var dp = (ObjectDataProvider)FindResource("data");
dp.ObjectInstance = config;

Я посмотрел на бесчисленные примеры, но я все еще могу понять, что я делаю неправильно. Спасибо за помощь.

Обновление:

<HierarchicalDataTemplate DataType="{x:Type ConfigurationEditor:Objects}" ItemsSource="{Binding Path=Channels}"/>
<HierarchicalDataTemplate DataType="{x:Type ConfigurationEditor:Channel}" ItemsSource="Binding Path=Reader}">
    <TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>

<DataTemplate DataType="{x:Type ConfigurationEditor:Reader}">
    <TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>

Без изменений TreeView. С этим изменением у меня все еще есть только Channel и ничего больше.

Ответы [ 4 ]

2 голосов
/ 13 сентября 2009

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

<HierarchicalDataTemplate
    DataType="{x:Type ConfigurationEditor:Objects}" 
    ItemsSource="{Binding Path=Channels}"/>

Вы установили это в XAML, который показывает каналы ... идеально!

<TreeView 
    Margin="12,12,12,96" Name="treeView1" 
    ItemsSource="{Binding Source={StaticResource data}, Path=Channels}">
</TreeView>

Теперь вы хотите, чтобы читатель также отображался. Но в качестве ItemsSource можно указывать только объекты типа IEnumerable, поэтому приведенный ниже шаблон неверен, и в качестве ItemSource указан «Reader».

<HierarchicalDataTemplate 
    DataType="{x:Type ConfigurationEditor:Channel}" 
    ItemsSource="{Binding Path=Reader}">
    <TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>

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

<DataTemplate 
    DataType="{x:Type ConfigurationEditor:Channel}" >
    <StackPanel>
        <TextBlock Text="{Binding Path=Name}"/>
        <TextBlock Text="{Binding Path=Reader.Name}"/>
    <StackPanel>
</DataTemplate>
2 голосов
/ 13 сентября 2009

Если продолжить ответ @ Gimalay, проблема в том, что TreeView не знает, где взять данные для любых дочерних узлов. Вы сообщаете TreeView, используя HierarchialDataTemplate вместо DataTemplate:

<HierarchialDataTemplate DataType="{x:Type ConfigurationEditor:Channel}"
    ItemsSource="...">
    <WrapPanel>
        <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
        <TextBlock Text=" [" />
        <TextBlock  Text="{Binding Path=Id}" />
        <TextBlock Text="]" />
    </WrapPanel>
</HierarchialDataTemplate>

Основное различие между ними заключается в атрибуте ItemsSource. Это выражение привязки, которое возвращает коллекцию объектов для использования в качестве дочерних узлов.

Проблема в том, что у вас есть несколько свойств, чтобы получить детей, а не только одно. Вам нужно либо объединить их все в одно свойство, либо добавить другое свойство, которое возвращает все дочерние узлы.

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

0 голосов
/ 15 сентября 2009

Хорошо, после долгих пробных ошибок я смог заставить это работать так, как я хотел. Вот как я это сделал.

В моем объекте Channel я добавил новое свойство, представляющее собой коллекцию всех других свойств, которые я хотел отобразить в TreeView

public ArrayList Components
{
    get { return new ArrayList { Reader, Filters, Router, Persister  }; } 
    set
    {
        ArrayList list = value;
        if (list != null)
        {
            foreach (var item in list)
            {
                if (item is Reader)
                {
                    Reader = (Reader)item;
                }
                else if (item is Router)
                {
                    Router = (Router) item;
                }
                else if (item is Persister)
                {
                    Persister = (Persister) item;
                }
                else if (item is Filters)
                {
                    Filters = (Filters) item;
                }
            }
        }
    }
}

Тогда мой XAML выглядит следующим образом:

    <HierarchicalDataTemplate DataType="{x:Type ConfigurationEditor:Channel}" ItemsSource="{Binding Path=Components}">
        <WrapPanel>
            <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
            <TextBlock Text=" [" />
            <TextBlock  Text="{Binding Path=Id}" />
            <TextBlock Text="]" />
        </WrapPanel>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate DataType="{x:Type ConfigurationEditor:Filters}" ItemsSource="{Binding Path=.}">
        <TextBlock Text="Filters"/>
        <HierarchicalDataTemplate.ItemTemplate>
            <DataTemplate >
                <TextBlock Text="{Binding Path=Name}"/>
            </DataTemplate>
        </HierarchicalDataTemplate.ItemTemplate>
    </HierarchicalDataTemplate>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Reader}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Router}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Persister}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>

Спасибо Энди за то, что поставил меня на правильный путь.

0 голосов
/ 13 сентября 2009

Просто ради этого шаблоны иерархических данных используются, когда источник элементов фактически имеет иерархию внутри себя. То есть сами объекты имеют некоторую иерархию, причем родительские объекты хранят список дочерних объектов.

В качестве примера, может быть, каждый канал имеет свойство SubChannels, как показано ниже.

public class Channel 
{
    public string Id { get; set; }
    public string Name { get; set; }
    public ObservableCollection<Channel> SubChannels { get; }
}

Тогда мы могли бы использовать такой шаблон.

<HierarchicalDataTemplate 
    DataType="{x:Type ConfigurationEditor:Channel}" 
    ItemsSource="{Binding Path=SubChannels}">
    <TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>

Теперь структура объекта может быть многоуровневой, и каждый подканал снова будет иметь подканалы.

Также обратите внимание, что в примере я просто использовал тот же тип, Channel, для создания иерархии подканалов. Мы могли бы использовать другой тип, скажем, каждый канал имеет список читателей.

...