Question

Je rencontre des difficultés sérieuses lors de la création d'un arbre WPF avec une liaison de données d'objet.

L’application est un éditeur de fichier de configuration. J'ai défini une structure d'objet pouvant être sérialisée au format XML approprié.

Le problème que je rencontre est le formatage de l'instance d'objet dans TreeView montrant la hiérarchie correcte. TreeView ne rendra que le nœud Channel et rien d’autre.

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

Toutes les classes enfants de Channel contiennent les propriétés Id et Nom . La classe Filters est une collection d'autres types ayant la même définition de propriété.

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

Le code derrière pour créer l'instance de données

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;

J'ai examiné d'innombrables exemples, mais je peux toujours comprendre ce que je fais de travers. Merci pour l'aide.

Mise à jour:

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

Aucune modification dans TreeView . Avec ce changement, je n'ai toujours que le canal répertorié, et rien d'autre.

Était-ce utile?

La solution

En réponse à la réponse de @ Gimalay, le problème est que TreeView ne sait pas où obtenir les données pour les nœuds enfants. Vous informez TreeView à l'aide d'un HierarchialDataTemplate , plutôt que d'un 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>

La principale différence entre les deux réside dans l'attribut ItemsSource . Il s'agit d'une expression de liaison qui renvoie une collection d'objets à utiliser en tant que nœuds enfants.

Le problème est que vous disposez de plusieurs propriétés pour obtenir des enfants, et non d'une seule. Vous devez soit les combiner tous dans une propriété, soit en ajouter une autre qui renvoie tous les nœuds enfants.

Enfin, vous devez définir un DataTemplate pour chaque type d'élément enfant, afin que le TreeView sache comment les afficher (vous pouvez utiliser un HierarchialDataTemplate pour les enfants également, s'ils ont à leur tour des nœuds enfants.

Autres conseils

Je ne pense pas que vous ayez besoin de ce modèle, car les objets ne figurent jamais dans l'image de votre TreeView.

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

Vous avez défini cela en XAML, ce qui indique les chaînes .. parfait!

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

Vous souhaitez maintenant que le Reader soit également affiché. Mais il est uniquement possible de spécifier des objets de type IEnumerable en tant que ItemsSource . Par conséquent, le modèle ci-dessous est incorrect et spécifie "Lecteur". comme ItemsSource.

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

Donc, si vous souhaitez que le nom du lecteur soit également affiché, utilisez le modèle comme indiqué ci-dessous.

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

Juste pour le plaisir, les modèles de données hiérarchiques sont utilisés lorsque la source des éléments a réellement une hiérarchie en elle-même. C'est-à-dire que les objets eux-mêmes ont une certaine hiérarchie, les objets parents conservant une liste d'objets enfants.

Par exemple, chaque canal peut avoir une propriété, comme ci-dessous, des sous-canaux.

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

Nous aurions alors pu utiliser un modèle comme celui-ci.

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

Maintenant, la structure de l'objet peut être multiniveau, chaque sous-canal ayant à nouveau des sous-canaux.

Notez également que dans l'exemple, je viens d'utiliser le même type, Channel, pour créer une hiérarchie de sous-canaux. Nous aurions pu utiliser un autre type, disons que chaque chaîne possède une liste de lecteurs.

D'accord, après de nombreuses tentatives d'essai, j'ai réussi à obtenir le résultat souhaité. Voici comment je l'ai fait.

Dans mon objet Channel , j'ai ajouté une nouvelle propriété qui était une collection de toutes les autres propriétés que je voulais afficher dans 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;
                }
            }
        }
    }
}

Ensuite, mon XAML est comme suit pour afficher l'arborescence.

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

Merci Andy de m'avoir mis sur la bonne voie.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top