Frage

Ich habe eine Sammlung von Datenbank Objekte, die jeweils Sammlungen von Schema Objekte und Benutzer Objekte.Ich wollen, um Sie zu binden, um ein TreeView -, aber das hinzufügen von zusätzlichen statischen Ebenen in der Hierarchie, so dass die resultierende TreeView sieht mehr oder weniger wie diese:

<TreeView>
    <TreeViewItem Header="All the databases:">
        <TreeViewItem Header="Db1">
            <TreeViewItem Header="Here's all the schemas:">
                <TreeViewItem Header="Schema1"/>
                <TreeViewItem Header="Schema2"/>
            </TreeViewItem>
            <TreeViewItem Header="Here's all the users:">
                <TreeViewItem Header="User1"/>
                <TreeViewItem Header="User2"/>
            </TreeViewItem>
        </TreeViewItem>
        <TreeViewItem Header="Db2">
            <TreeViewItem Header="Here's all the schemas:">
                <TreeViewItem Header="Schema1"/>
                <TreeViewItem Header="Schema2"/>
            </TreeViewItem>
            <TreeViewItem Header="Here's all the users:">
                <TreeViewItem Header="User1"/>
                <TreeViewItem Header="User2"/>
            </TreeViewItem>
        </TreeViewItem>
    </TreeViewItem>
</TreeView>

Ich war in der Lage, ziemlich nahe an dem, was ich will mithilfe der folgenden Vorlagen:

<Window.Resources>
    <HierarchicalDataTemplate DataType="{x:Type smo:Database}">
        <TreeViewItem Header="{Binding Path=Name}">
            <TreeViewItem Header="Here's all the schemas:" ItemsSource="{Binding Path=Schemas}"/>
            <TreeViewItem Header="Here's all the users:" ItemsSource="{Binding Path=Users}"/>
        </TreeViewItem>
    </HierarchicalDataTemplate>
    <DataTemplate DataType="{x:Type smo:Schema}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type smo:User}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>
</Window.Resources>

Dann in dem code, den ich verbindlich fest, wie diese:

TreeViewItem treeViewItem = new TreeViewItem();
treeViewItem.Header = "All the databases:";
treeViewItem.ItemsSource = server.Databases;
treeView.Items.Add(treeViewItem);

Die resultierende TreeView sieht wie ich es mir vorstelle, aber es ist nicht möglich, wählen Sie ein bestimmtes schema oder Benutzer.Offenbar WPF sieht, ist der gesamte Teilbaum verwurzelt in einer Datenbank-Knoten als ein einziges Element, und es wählt nur die ganze Sache.Ich muss in der Lage sein zu wählen, ein bestimmtes schema, Benutzer oder Datenbank.Wie legen Sie die Vorlagen und Bindungen, so dass es funktioniert auf die Weise, die ich brauche?

War es hilfreich?

Lösung

Oh man das ist eine unglaublich frustrierende Aufgabe. Ich habe versucht, es selbst oft tun. Ich hatte eine sehr ähnliche Anforderung, wo ich so etwas wie ein Customer-Klasse haben, die sowohl eine Locations Sammlung und eine Orders Sammlung. Ich wollte Standorte und Aufträge „Ordner“ in der Baumansicht sein. Wie Sie entdeckt haben, werden alle TreeView Beispiele, die zeigen Ihnen, wie Sie sich selbst verweisende Arten zu binden, sind ziemlich nutzlos.

Zuerst griff ich manuell einen Baum von FolderItemNode und ItemNode Objekten bauen, die ich in dem Ansichtsmodell erzeugen würde, aber diese besiegt den Zweck der Bindung, weil sie auf die zugrunde liegende Sammlung Veränderungen nicht reagieren würde.

Dann kam ich mit einem Ansatz auf, der ziemlich gut zu funktionieren scheint.

  • In dem obigen Objektmodell beschrieben, habe ich Klassen LocationCollection und OrderCollection. Sie beide erben von ObservableCollection und überschreiben ToString () zurückzukehren „Orte“ und „Aufträge“ sind.
  • Ich erstelle ein MultiCollectionConverter Klasse, die IMultiValueConverter
  • implementiert
  • Ich habe eine FolderNode Klasse, die einen Namen und eine Eigenschaft Items hat. Dies ist das Platzhalter-Objekt, das Ihre „Ordner“ in der Baumansicht darstellen.
  • definieren HierarchicalDataTemplate ist, dass die Verwendung überall mehrbindigen, dass Sie mehrere untergeordnete Sammlungen in Ordnern.
  • gruppieren möchten

Das resultierende XAML ähnelt dem folgenden Code und Sie können eine ZIP-Datei packen, die alle Klassen und XAML in einem Arbeitsbeispiel hat.

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Local="clr-namespace:WpfApplication2"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">

    <Window.Resources>

        <!-- THIS IS YOUR FOLDER NODE -->
        <HierarchicalDataTemplate DataType="{x:Type Local:FolderNode}" ItemsSource="{Binding Items}">
            <Label FontWeight="Bold" Content="{Binding Name}" />
        </HierarchicalDataTemplate>

        <!-- THIS CUSTOMER HAS TWO FOLDERS, LOCATIONS AND ORDERS -->
        <HierarchicalDataTemplate DataType="{x:Type Local:Customer}">
            <HierarchicalDataTemplate.ItemsSource>
                <MultiBinding>
                    <MultiBinding.Converter>
                        <Local:MultiCollectionConverter />
                    </MultiBinding.Converter>
                    <Binding Path="Locations" />
                    <Binding Path="Orders" />
                </MultiBinding>
            </HierarchicalDataTemplate.ItemsSource>
            <Label Content="{Binding Name}" />
        </HierarchicalDataTemplate>

        <!-- OPTIONAL, YOU DON'T NEED SPECIFIC DATA TEMPLATES FOR THESE CLASSES -->
        <DataTemplate DataType="{x:Type Local:Location}">
            <Label Content="{Binding Title}" />
        </DataTemplate>
        <DataTemplate DataType="{x:Type Local:Order}">
            <Label Content="{Binding Title}" />
        </DataTemplate>

    </Window.Resources>

    <DockPanel>
        <TreeView Name="tree" Width="200" DockPanel.Dock="Left" />
        <Grid />
    </DockPanel>

</Window>

Ordner in TreeView

Andere Tipps

Das Problem ist, dass ein TreeView nicht sehr gut geeignet ist, was Sie wollen acomplish: Es erwartet alle untergeordneten Knoten des gleichen Typs sein. Als Datenbankknoten einen Knoten vom Typ Collection<Schemas> und vom Typ Collection<Users hat> können Sie keine HierarchicalDataTemplate verwenden. Ein besserer Ansatz ist verschachtelte Expandern zu verwenden, die Listboxen enthalten.

Der folgende Code tut, was Sie wollen, ich denke, während so nah wie möglich an Ihre ursprüngliche Absicht zu sein:

<Window x:Class="TreeViewSelection.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:smo="clr-namespace:TreeViewSelection"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <Style TargetType="ListBox">
            <Setter Property="BorderThickness" Value="0"/>
        </Style>
        <DataTemplate DataType="{x:Type smo:Database}">
                <TreeViewItem Header="{Binding Name}">
                    <TreeViewItem Header="Schemas">
                        <ListBox ItemsSource="{Binding Schemas}"/>
                    </TreeViewItem>
                    <TreeViewItem Header="Users">
                    <ListBox ItemsSource="{Binding Users}"/>
                </TreeViewItem>
                </TreeViewItem> 
        </DataTemplate>
        <DataTemplate DataType="{x:Type smo:User}" >
            <TextBlock Text="{Binding Name}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type smo:Schema}">
            <TextBlock Text="{Binding Name}"/>
        </DataTemplate>
    </Window.Resources>
    <StackPanel>
        <TreeViewItem ItemsSource="{Binding DataBases}" Header="All DataBases">
        </TreeViewItem>
    </StackPanel>
</Window>

using System.Collections.ObjectModel;
using System.Windows;

namespace TreeViewSelection
{
    public partial class Window1 : Window
    {
        public ObservableCollection<Database> DataBases { get; set; }
        public Window1()
        {
            InitializeComponent();
            DataBases = new ObservableCollection<Database>
                            {
                                new Database("Db1"),
                                new Database("Db2")
                            };
            DataContext = this;
        }
    }

    public class Database:DependencyObject
    {
        public string Name { get; set; }
        public ObservableCollection<Schema> Schemas { get; set; }
        public ObservableCollection<User> Users { get; set; }

        public Database(string name)
        {
            Name = name;
            Schemas=new ObservableCollection<Schema>
                        {
                            new Schema("Schema1"),
                            new Schema("Schema2")
                        };
            Users=new ObservableCollection<User>
                      {
                          new User("User1"),
                          new User("User2")
                      };
        }
    }

    public class Schema:DependencyObject
    {
        public string Name { get; set; }
        public Schema(string name)
        {
            Name = name;   
        }
    }

    public class User:DependencyObject
    {
        public string Name { get; set; }
        public User(string name)
        {
            Name = name;
        }
    }
}

Sie benötigen, füllen die Eigenschaften, die Sie sind mit in Ihre Bindung mit Daten aus Ihrer Datenbank.Derzeit verwenden Sie eine neue TreeViewItem, und verwenden Sie als Datenquelle, so, was Sie sagen, über Sie zu sehen, alles als einen einzelnen Knoten macht Sinn, denn Sie haben platziert Sie in einer einzigen Knoten.

Sie brauchen, um zu laden Sie Ihre Datenbank Daten ein und befestigen Sie es an den Eigenschaften, die Sie in Ihrer WPF-Vorlage als verbindliche Elemente.

Hier ist eine Modifikation von Joshs Lösung mit SMO (meiner ursprünglichen Problemstellung) zu arbeiten:

<Window.Resources>
    <HierarchicalDataTemplate DataType="{x:Type local:FolderNode}" ItemsSource="{Binding Items}">
        <TextBlock Text="{Binding Name}"/>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate DataType="{x:Type smo:Database}">
        <HierarchicalDataTemplate.ItemsSource>
            <MultiBinding>
                <MultiBinding.Converter>
                    <local:MultiCollectionConverter />
                </MultiBinding.Converter>
                <Binding Path="Schemas" />
                <Binding Path="Users" />
            </MultiBinding>
        </HierarchicalDataTemplate.ItemsSource>
        <TextBlock Text="{Binding Name}"/>
    </HierarchicalDataTemplate>
    <DataTemplate DataType="{x:Type smo:User}" >
        <TextBlock Text="{Binding Name}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type smo:Schema}">
        <TextBlock Text="{Binding Name}"/>
    </DataTemplate>
</Window.Resources>

und der modifizierte Konverter:

public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    FolderNode[] result = new FolderNode[values.Length];
    for (int i = 0; i < values.Length; ++i)
    {
        result[i].Items = (IEnumerable)values[i];
        result[i].Name = values[i] is UserCollection ? "Users" : "Schemas";
    }
    return result;
}
  

Namensnennung Hinweis: Content kopiert von Endlösung der OP, als geschrieben < a href = "https://stackoverflow.com/revisions/1815056/3"> bearbeiten auf die Frage , anstatt als Antwort

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top