Domanda

Sto riscontrando seri problemi nella creazione di un TreeView WPF con un database di oggetti.

L'applicazione è l'editor di file di configurazione. Ho definito una struttura di oggetti che può essere serializzata nel formato XML corretto.

Il problema che sto riscontrando è la formattazione dell'istanza dell'oggetto in TreeView che mostra la gerarchia corretta. TreeView eseguirà il rendering del nodo Canale e nient'altro.

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

Tutte le classi secondarie di Channel contengono le proprietà Id e Name . La classe Filters è una raccolta di altri tipi con la stessa definizione di proprietà.

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

Il codice dietro per creare l'istanza di dati

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;

Ho esaminato innumerevoli esempi ma riesco ancora a capire cosa sto facendo di sbagliato. Grazie per l'aiuto.

Aggiornamento:

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

Nessuna modifica al TreeView . Con questa modifica ho ancora elencato solo il Channel e nient'altro.

È stato utile?

Soluzione

Espandendosi sulla risposta di @ Gimalay, il problema è che TreeView non sa dove ottenere i dati per nessun nodo figlio. Informi il TreeView utilizzando un HierarchialDataTemplate , anziché 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 differenza tra i due è l'attributo ItemsSource . Questa è un'espressione vincolante che restituisce una raccolta di oggetti da utilizzare come nodi figlio.

Il problema è che hai alcune proprietà da cui ottenere i bambini, non solo una. È necessario combinarli tutti in una proprietà o aggiungere un'altra proprietà che restituisca tutti i nodi figlio.

Infine, dovrai definire un DataTemplate per ogni tipo di elemento figlio, in modo che TreeView sappia come visualizzarli (puoi usare un HierarchialDataTemplate anche per i bambini, se a loro volta hanno nodi figlio).

Altri suggerimenti

Non penso che tu abbia bisogno di questo modello, in quanto gli oggetti non vengono mai visualizzati nell'immagine in TreeView.

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

L'hai impostato in XAML, che mostra i canali ... perfetto!

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

Ora vuoi mostrare anche Reader. Ma è possibile specificare solo oggetti di tipo IEnumerable come ItemsSource , quindi il modello seguente non è corretto e specifica " Reader " come ItemsSource.

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

Quindi, se si desidera visualizzare anche il nome del lettore, utilizzare il modello come mostrato di seguito.

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

Solo per il gusto di farlo, i modelli di dati gerarchici vengono utilizzati quando l'origine degli elementi ha effettivamente una gerarchia al suo interno. Cioè, gli oggetti stessi hanno una certa gerarchia, con gli oggetti padre che mantengono un elenco di oggetti figlio.

Ad esempio, può essere che ogni canale abbia una proprietà Subcanali come di seguito.

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

Quindi avremmo potuto usare un modello come questo.

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

Ora la struttura degli oggetti potrebbe essere a più livelli con ogni sottochannel di nuovo con sottochannel.

Inoltre, tieni presente che nell'esempio ho appena usato lo stesso tipo, Canale, per creare una gerarchia di canali secondari. Avremmo potuto usare un altro tipo, ad esempio ogni canale con un elenco di lettori.

Okay, dopo molte prove un errore sono stato in grado di farlo funzionare come volevo. Ecco come l'ho fatto.

Nel mio oggetto Channel ho aggiunto una nuova proprietà che era una raccolta di tutte le altre proprietà che volevo visualizzare in 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;
                }
            }
        }
    }
}

Quindi il mio XAML è il seguente per visualizzare il treeview.

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

Grazie Andy per avermi messo sulla strada giusta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top