Pergunta

Eu estou tendo alguns problemas graves criando um WPF TreeView com uma ligação de dados de objetos.

A aplicação é Config Editor arquivo. Eu definida uma estrutura de objeto que pode ser serializado para o formato XML correto.

O problema que estou tendo está formatando a instância do objeto na TreeView mostrando a hierarquia correta. O TreeView só irá tornar o nó Channel, e nada mais.

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; }
}

Todas as crianças classes de Channel contêm propriedades Id e Name. A classe Filtros é uma coleção de outros tipos com a mesma definição de propriedade.

Aqui está o 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>

O atrás de código para criar a instância de dados

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;

Eu olhei para inúmeros exemplos, mas eu ainda pode descobrir o que estou fazendo de errado. Obrigado pela ajuda.

Update:

<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>

Sem alterações ao TreeView. Com esta mudança ainda tenho apenas o Channel listado, e nada mais.

Foi útil?

Solução

Expandindo resposta da @ Gimalay, o problema é que o TreeView não sabe onde obter os dados para todos os nós filho. Você informa o TreeView usando um HierarchialDataTemplate, em vez de um 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>

A principal diferença entre os dois é o atributo ItemsSource. Esta é uma expressão de ligação que retorna uma coleção de objetos para usar como nós filho.

O problema é que você tem algumas propriedades para obter as crianças de, não apenas um. Você quer necessidade de combiná-los todos em uma propriedade, ou adicionar outra propriedade que retorna todos os nós filho.

Finalmente, você precisa definir uma DataTemplate para cada tipo de item de criança, de modo que o TreeView sabe como exibi-los (você pode usar um HierarchialDataTemplate para as crianças, bem como, se eles por sua vez, tem nós filhos).

Outras dicas

Eu não acho que você precisa deste modelo, como objectos nunca vêm na imagem em sua TreeView.

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

Você definir isso no XAML, que mostra os canais .. perfeito!

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

Agora você quer o leitor a ser mostrado também. Mas só se pode especificar os objetos do tipo IEnumerable como um ItemsSource , de modo que o modelo abaixo é incorreta que especifica "Reader" como o ItemsSource.

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

Então, se você quiser nome leitor a ser mostrado, bem como, utilizar o modelo como mostrado abaixo.

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

Apenas para o bem dela, modelos de dados hierárquicos são usados ??quando a fonte de itens realmente tem uma hierarquia dentro de si. Ou seja, os próprios objetos têm alguma hierarquia, com os pais objetos manter uma lista de objetos filho.

Como exemplo, pode ser a cada canal tem uma propriedade subcanais como abaixo.

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

Em seguida, poderíamos ter usado um modelo como este.

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

Agora, a estrutura do objeto poderia ser profundo multinível com cada subcanal novamente tendo subcanais.

Além disso, fazer nota que, no exemplo eu usei apenas o mesmo tipo, Channel, para criar uma hierarquia de subcanais. Poderíamos ter usado outro tipo, dizem que cada canal ter uma lista de leitores.

Ok, depois de muita experimentação um erro eu era capaz de chegar a este trabalho como eu queria. Aqui está como eu fiz isso.

Em meu objeto Channel eu adicionei uma nova propriedade que era uma coleção de todas as outras propriedades que eu queria exibir na 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;
                }
            }
        }
    }
}

Então meu XAML é a seguinte para exibir a árvore.

    <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>

Graças Andy por me colocar no caminho certo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top