문제

나는 모음이있다 데이터 베이스 각각의 컬렉션을 포함하는 물체 개요 물체와 사용자 사물. TreeView에 바인딩하고 싶지만 계층 구조에 추가 정적 레벨을 추가하여 결과 TreeView가 다음과 같이 보이도록합니다.

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

다음 템플릿을 사용하여 원하는 것에 거의 가까워 질 수있었습니다.

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

그런 다음 코드에서 다음과 같이 바인딩을 설정했습니다.

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

결과 TreeView는 원하는 것처럼 보이지만 특정 스키마 또는 사용자를 선택할 수는 없습니다. 분명히 WPF는 전체 하위 트리를 데이터베이스 노드에 뿌리 내린 것을 단일 항목으로보고 있으며 모든 것을 선택합니다. 특정 스키마, 사용자 또는 데이터베이스를 선택할 수 있어야합니다. 템플릿과 바인딩이 필요한 방식으로 작동하도록하려면 어떻게해야합니까?

도움이 되었습니까?

해결책

오, 이건 엄청나게 실망스러운 일입니다. 나는 그것을 여러 번 스스로 시도했다. 위치 컬렉션과 주문 컬렉션이 모두있는 고객 클래스와 같은 것이있는 곳에서 매우 비슷한 요구 사항이있었습니다. 나는 트리 뷰에서 위치와 주문이 "폴더"가되기를 원했다. 발견 한 바와 같이, 자기 참조 유형에 결합하는 방법을 보여주는 모든 TreeView 예제는 거의 쓸모가 없습니다.

먼저 ViewModel에서 생성 할 FolderItemnode 및 ItemNode 객체의 트리를 수동으로 구축하는 데 의지했지만 기본 컬렉션 변경에 응답하지 않기 때문에 바인딩의 목적을 물리 쳤습니다.

그런 다음 나는 잘 작동하는 것처럼 보이는 접근 방식을 생각해 냈습니다.

  • 위에서 설명한 객체 모델에서 클래스 LocationCollection 및 OrderCollection을 만들었습니다. 이들은 모두 관측형 수집에서 물려 받고 ToString ()을 무시하여 "위치"및 "주문"을 각각 반환합니다.
  • imultivalueconverter를 구현하는 MulticollectionConverter 클래스를 만듭니다
  • 이름과 항목 속성이있는 폴더 노드 클래스를 만들었습니다. 이것은 트리 뷰에서 "폴더"를 나타내는 자리 표시 자 개체입니다.
  • 여러 하위 컬렉션을 폴더로 그룹화하려는 어느 곳에서나 멀티 바인딩을 사용하는 계층 적 DataTemplate을 정의하십시오.

결과 XAML은 아래 코드와 비슷해 보이며 작업 예제에 모든 클래스와 XAML이있는 지퍼 파일을 잡습니다..

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

Folders in TreeView

다른 팁

문제는 TreeView가 당신이 당신이 압도하려는 것에 적합하지 않다는 것입니다. 그것은 모든 서브 노드가 같은 유형의 유형이 될 것으로 기대합니다. 데이터베이스 노드에 유형 수집 노드가 있으므로<Schemas> 및 유형 수집<Users> 계층 적 DataTemplate을 사용할 수 없습니다. 더 나은 접근 방식은 ListBox를 포함하는 중첩 확장기를 사용하는 것입니다.

아래 코드는 원래 의도에 최대한 가깝게 생각하는 반면, 원하는 것을 수행합니다.

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

데이터베이스의 데이터로 바인딩하는 속성을 채워야합니다. 현재 새로운 것을 사용하고 있습니다 TreeViewItem, 그리고 그것을 데이터 소스로 사용하므로 단일 노드에 배치 할 때 모든 것을 단일 노드로 보는 것에 대해 말하는 내용은 의미가 있습니다.

데이터베이스 데이터를로드하여 WPF 템플릿에서 사용한 속성에 바인딩 항목으로 첨부해야합니다.

다음은 SMO 작업을위한 Josh의 솔루션을 수정했습니다 (원래 문제 설명) :

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

수정 된 변환기 :

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

속성 참고 : 컨텐츠에서 복사했습니다 OP 최종 솔루션, 질문을 편집하십시오, 대답보다는

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top