WPF Treeview Binding несколько разных списков - PullRequest
0 голосов
/ 18 февраля 2019

Я хочу связать несколько разных списков с TreeView в WPF.Я искал некоторые другие решения, но не мог найти помощи для моей проблемы. Этот ответ довольно близок, но не совсем то, что я ищу.

Я пробовал связанное решение выше, но оно отображает только два уровня в TreeView. Я не могу понять, как отобразить имя каждого списка в качестве родительского в TreeView.

Мой объект, который я хочу отобразить, выглядит следующим образом:

public class LightDistributor
{
  public string Description { get; set; }
  // ...

  public List<Field> Hardware { get; set; }
  public List<Type> Inputs { get; set; }
  public List<Type> Outputs { get; set; }
}

public class Field
{
  public string Fieldname { get; set; }
  // ...
}

public class Type
{
  public string TypeDescription { get; set; }
  // ...
}

И XAML:

<TreeView ItemsSource="{Binding LightDistributors}">
  <TreeView.ItemTemplate>
    <HierarchicalDataTemplate DataType="{x:Type data:LightDistributor}" ItemsSource="{Binding Hardware}">
       <TextBlock Text="{Binding Description}" />
       <HierarchicalDataTemplate.ItemTemplate>
          <DataTemplate DataType="{x:Type data:Field}">
               <TextBlock Text="{Binding Description}" />
          </DataTemplate>
       </HierarchicalDataTemplate.ItemTemplate>
    </HierarchicalDataTemplate>
  </TreeView.ItemTemplate>   
</TreeView>

То, что я хочу, чтобы мой Treeview выглядел:

LightDistributor - LongFloor
    | Hardware
        - Field1
        - Field2
        - Field3
    | Inputs
        - InputTypeA
        - InputTypeB
    | Outputs
        - OutputTypeY
        - OutputTypeZ

Как это выглядит в данный момент:

LightDistributor - LongFloor
        - Field1
        - Field2
        - Field3

В зависимости от SelectedItem,UserControl отображается с большим количеством параметров.

Ответы [ 2 ]

0 голосов
/ 20 февраля 2019

Добавление еще одного ответа, показывающего, как это сделать в чистом XAML.Это почти полностью основано на первоначальном втором решении canton7, которое было очень близко, но создавало массив TreeViewItems, которые перерабатывались.Обычно установка x:Shared="False" должна исправить это, тот факт, что в его случае это не сработало, кажется мне ошибкой WPF.

В любом случае вместо создания массива элементов управления создайте массив данных.объекты.Введите sys: String будет хорошо работать в этом случае, с дополнительным бонусом, который мы также сможем использовать в качестве текста заголовка TreeViewItem позже:

    <x:Array x:Key="DistributorItems" Type="{x:Type sys:String}">
        <sys:String>Hardware</sys:String>
        <sys:String>Inputs</sys:String>
        <sys:String>Outputs</sys:String>
    </x:Array>

Они представляют дочерние свойства в вашем LightDistributorучебный класс.Второй уровень вашего TreeView получит одну из этих строк, назначенную в качестве его DataContext, поэтому мы создадим стиль для них и будем использовать триггеры для установки ItemsSource соответственно через DataContext родительского TreeViewItem:

    <Style x:Key="TreeViewItemStyle" TargetType="TreeViewItem">
        <Style.Triggers>
            <Trigger Property="DataContext" Value="Hardware">
                <Setter Property="ItemsSource" Value="{Binding DataContext.Hardware, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}, AncestorLevel=1}}" />
            </Trigger>
            <Trigger Property="DataContext" Value="Inputs">
                <Setter Property="ItemsSource" Value="{Binding DataContext.Inputs, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}, AncestorLevel=1}}" />
            </Trigger>
                <Trigger Property="DataContext" Value="Outputs">
                <Setter Property="ItemsSource" Value="{Binding DataContext.Outputs, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}, AncestorLevel=1}}" />
            </Trigger>
        </Style.Triggers>
    </Style>

Остальная часть кода в основном совпадает с исходным кодом canton7, за исключением того, что я устанавливаю для ItemContainerStyle LightDistributor стиль, который я создал выше, чтобы соответственно настроить ItemsSource:

<TreeView ItemsSource="{Binding LightDistributors}">
    <TreeView.Resources>

        <HierarchicalDataTemplate DataType="{x:Type vm:LightDistributor}"
                                  ItemsSource="{Binding Source={StaticResource DistributorItems}}"
                                  ItemContainerStyle="{StaticResource TreeViewItemStyle}">
            <TextBlock Text="{Binding Description}"/>
        </HierarchicalDataTemplate>

        <DataTemplate DataType="{x:Type vm:Field}">
            <TextBlock Text="{Binding Fieldname}"/>
        </DataTemplate>

        <DataTemplate DataType="{x:Type vm:Type}">
            <TextBlock Text="{Binding TypeDescription}"/>
        </DataTemplate>

    </TreeView.Resources>
</TreeView>

Теперь простопоскольку это работает, это не значит, что это хорошее решение, но я по-прежнему придерживаюсь мнения, что первое решение canton7 - лучшее.Просто добавьте это, чтобы показать, что все-таки это можно сделать в чистом XAML.

0 голосов
/ 18 февраля 2019

Добавьте NamedSection, который группирует имя со списком элементов:

public class NamedSection
{
    public string Name { get; set; }
    public IReadOnlyList<object> Items { get; set; }
}

Затем обновите LightDistributor.Обратите внимание, как я установил свойства List<T> только для получения, чтобы NamedSection мог правильно захватить ссылку на конструкцию.

public class LightDistributor
{
    public string Description { get; set; }
    // ...

    public List<Field> Hardware { get; } = new List<Field>();
    public List<Type> Inputs { get; } = new List<Type>();
    public List<Type> Outputs { get; } = new List<Type>();

    public List<NamedSection> Sections { get; }

    public LightDistributor()
    {
        this.Sections = new List<NamedSection>()
        {
            new NamedSection() { Name = "Hardware", Items = this.Hardware },
            new NamedSection() { Name = "Inputs", Items = this.Inputs },
            new NamedSection() { Name = "Outputs", Items = this.Outputs },
        };
    }
}

Тогда ваш XAML:

<TreeView ItemsSource="{Binding LightDistributors}">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:LightDistributor}" ItemsSource="{Binding Sections}">
            <TextBlock Text="{Binding Description}" />
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate DataType="{x:Type local:NamedSection}" ItemsSource="{Binding Items}">
            <TextBlock Text="{Binding Name}"/>
        </HierarchicalDataTemplate>
        <DataTemplate DataType="{x:Type local:Field}">
            <TextBlock Text="{Binding Fieldname}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Type}">
            <TextBlock Text="{Binding TypeDescription}"/>
        </DataTemplate>
    </TreeView.Resources>
</TreeView>

Первоначально я думал, что вы также можете достичь этого, объявив x:Array из TreeViewItem как ресурс (с элементом каждый для Hardware, Inputs, Output), а затем установив его в качестве ItemsSource для HierarchicalTemplate для LightDistributor.Однако это не работает, так как, похоже, нет способа клонировать этот x:Array для каждого LightDistributor, который мы хотим показать.

...