每个节点上具有多个数据类型和可能的叶的WPF TreeView

nnsrf1az  于 2023-02-20  发布在  其他
关注(0)|答案(2)|浏览(229)

我在使用WPF时遇到了一个问题。让我们举几个类的例子,它们重现了我试图做的事情:

public class Element
    {
        public string Name { get; set; }
        public IList<Element> SubElements { get; set; } = new List<Element>();
        public IList<Value> Values { get; set; } = new List<Value>();
    }

    public class Value
    {
        public string Name { get; set; }
    }

每个Element示例可以有(或没有)自己的Values列表。
例如,我希望在TreeView上显示以下根目录:

Element root = new Element() { Name = "Root" };
            Element subElement1 = new Element() { Name = "SubElement1" };
            Element subElement1_1 = new Element() { Name = "SubElement1_1" };
            Value valueSubElement1_1 = new Value() { Name = "SubElement1_1_Value" };
            subElement1_1.Values.Add(valueSubElement1_1);
            subElement1.SubElements.Add(subElement1_1);
            root.SubElements.Add(subElement1);

            Element subElement2 = new Element() { Name = "SubElement2" };
            Value valueSubElement2 = new Value() { Name = "SubElement2_Value" };
            subElement2.Values.Add(valueSubElement2);
            root.SubElements.Add(subElement2);

我怎么能那样做呢?我很难找到正确的答案。
下面是我开始使用的xaml:

<TreeView x:Name="TreeView" Grid.Row="0" ItemsSource="{Binding TreeViewElements, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}">
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type local:Element}" ItemsSource="{Binding SubElements}">
                    <TextBlock Text="{Binding Name}"/>
                </HierarchicalDataTemplate>
                <DataTemplate DataType="{x:Type local:Value}">
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </TreeView.Resources>
        </TreeView>

对于TreeViewElementspublic IList<Element> TreeViewElements { get; set; } = new List<Element>();,我在其中添加了根对象。
有了它,我可以完美地显示所有的Element对象,但不能显示值。
我知道为什么当我指定ItemsSource时,我给出了一个Element类型的项,所以它永远看不到里面的值。
我怎么能这么做呢?
提前感谢您的回答,祝您愉快。

ru9i0ody

ru9i0ody1#

您可以根据类型定义任意多个模板,但一个树视图项目只能有一个项目源。
您可以更改元素或创建一个新的具有列表的ElementViewModel

public class Element
{
    public string Name { get; set; }
    public IList<object> Children { get; set; } = new List<object>();
}

然后,您可以向子级添加元素或值。
话虽如此。
元素有名称和子元素,值只有名称。看起来你可以只使用一个元素和一个空的子元素集合来代替值。
只是

public class Element
{
    public string Name { get; set; }
    public IList<Element> SubElements  { get; set; } = new List<Element>();
}
a14dhokn

a14dhokn2#

您必须向模板中添加另一个ItemsControl,才能在同一树级别上显示其他集合。
或者,如果要在同一个树中显示多个类型,则必须更改数据结构,以便将所有子项添加到同一个源集合中。

溶液1

为了能够使用Trigger隐藏嵌套的ToggleButton(在本例中用作节点的扩展器)(在Element.Values源集合为空的情况下),可以引入HasValues属性:

元素.cs

public class Element
{
  public string Name { get; set; }
  public IList<Element> SubElements { get; set; } = new List<Element>();
  public IList<Value> Values { get; set; } = new List<Value>();
  public bool HasValues => this.Values.Count > 0;
}

以下修改后的TreeView示例使用ToggleButton切换嵌套ItemsControlVisibility
或者,要使扩展器的外观与TreeView的扩展器匹配,您可以使用Visual Studio提取TreeView样式,您还将获得TreeView用于设置树节点扩展器样式的ToggleButton样式。然后,您可以将此Style设置为下面的ToggleButton,以切换嵌套ItemsControl的可见性。在这种情况下,必须删除当前设置的ToggleButton.Content值。

<TreeView>
  <TreeView.Resources>
    <HierarchicalDataTemplate DataType="{x:Type local:Element}"
                              ItemsSource="{Binding SubElements}">
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="Auto" />
          <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto" />
          <RowDefinition />
        </Grid.RowDefinitions>

        <ToggleButton x:Name="Expander"
                      Grid.Row="0"
                      Grid.Column="0"
                      Content=">"
                      Margin="-24,0,0,0"
                      Visibility="Collapsed"
                      HorizontalAlignment="Left" />

        <TextBlock x:Name="Header"
                   Grid.Row="0"
                   Grid.Column="1"
                   Text="{Binding Name}" />
        <ItemsControl x:Name="ValueHost"
                      Grid.Row="1"
                      Grid.Column="0"
                      Grid.ColumnSpan="2"
                      Visibility="Collapsed"
                      ItemsSource="{Binding Values}" />
      </Grid>

      <HierarchicalDataTemplate.Triggers>
        <DataTrigger Binding="{Binding HasValues}"
                     Value="True">
          <Setter TargetName="Expander"
                  Property="Visibility"
                  Value="Visible" />
        </DataTrigger>
        <DataTrigger Binding="{Binding ElementName=Expander, Path=IsChecked}"
                     Value="True">
          <Setter TargetName="ValueHost"
                  Property="Visibility"
                  Value="Visible" />
        </DataTrigger>
      </HierarchicalDataTemplate.Triggers>
    </HierarchicalDataTemplate>

    <DataTemplate DataType="{x:Type local:Value}">
      <TextBlock Text="{Binding Name}" />
    </DataTemplate>
  </TreeView.Resources>
</TreeView>

溶液2

在您的情况下,您可以引入一个声明Name属性的公共接口,可以使用此接口或最不常用的类型(object)作为容器集合的类型:

信息节点.cs

interface INode : INotifyPropertyChanged
{
  string Name { get; set; }
}

元素.cs

// TODO::Implement INotifyPropertyChnaged
class Element : INode
{
  public IList<INode> ChildNodes { get; set; }
  public string Name { get; set; }
}

值.cs

// TODO::Implement INotifyPropertyChnaged
class Element : INode
{
  public string Name { get; set; }
}

示例

Element root = new Element() { Name = "Root" };
Element subElement1 = new Element() { Name = "SubElement1" };
Element subElement1_1 = new Element() { Name = "SubElement1_1" };
Value valueSubElement1_1 = new Value() { Name = "SubElement1_1_Value" };

subElement1_1.ChildNodes.Add(valueSubElement1_1);            subElement1.ChildNodes.Add(subElement1_1);
root.ChildNodes.Add(subElement1);

Element subElement2 = new Element() { Name = "SubElement2" };
Value valueSubElement2 = new Value() { Name = "SubElement2_Value" };
subElement2.ChildNodes.Add(valueSubElement2);
root.ChildNodes.Add(subElement2);
<TreeView>
  <TreeView.Resources>
    <HierarchicalDataTemplate DataType="{x:Type local:Element}" 
                              ItemsSource="{Binding ChildNodes}">
      <TextBlock Text="{Binding Name}"/>
    </HierarchicalDataTemplate>

    <DataTemplate DataType="{x:Type local:Value}">
      <TextBlock Text="{Binding Name}"/>
    </DataTemplate>
  </TreeView.Resources>
</TreeView>

相关问题