WPF TreeView sub-items 未显示

WPF TreeView sub-items not displaying

我正在尝试通过在代码中创建和添加我的 TreeViewItems 来构建 TreeView。我有一个 Folders 的 object,其中包含 sub-folders 的列表和 Sessions 的列表。所有这些都保存在 Root 文件夹 object 中。当我使用递归创建 TreeViewItems 并将它们添加到我的 TreeView 时,根文件夹中的 FoldersSessions 出现但 none显示属于他们 collection 的 24=]。

当我手写页面的 xaml 时,它会按需要显示,但在添加代码时不会显示。我查看了调试器中的 TreeView object,我可以在 Items collection 中看到 TreeViewItems 的第二层和第三层,但是 none 显示。

我的object

    [Serializable]
    public class Session
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public Protocol ConnectionProtocol { get; set; }
        public int Port { get; set; }
    }

[Serializable]
    public class Folder
    {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
        public virtual int ParentID { get; set; }
        public virtual List<Folder> SubFolders { get; set; } = new List<Folder>();
        public virtual List<Session> Sessions { get; set; } = new List<Session>();
    }

    [Serializable]
    public class Root : Folder
    {
        public KeyValuePair<ulong, int> Version
        {
            get
            {
                return new KeyValuePair<ulong, int>(_revisionNumber, GetHashCode());
            }
        }
        private ulong _revisionNumber = 0;

        public override int Id { get => 0; set { } }
        public override int ParentID { get => 0; set {  } }

        private void UpdateVersion()...

        public bool AddSession(Session session)...

        public bool RemoveSession(Session session)...

        public bool AddFolder(Folder folder)...

        public bool RemoveFolder(Folder folder)...
    }

并且在主窗口中

        private static Root root { get; set; }

        private void RefreshTree()
        {
            SessionsTree.Items.Clear();

            foreach (var sub in root.SubFolders)
                SessionsTree.Items.Add(GetNodesFromFolder(sub));

            foreach (var session in root.Sessions)
                SessionsTree.Items.Add(BuildTreeViewItem(session));

            SessionsTree.UpdateLayout();
        }

        private TreeViewItem GetNodesFromFolder(Folder folder)
        {
            TreeViewItem viewItem = BuildTreeViewItem(folder);
            foreach (var sub in folder.SubFolders)
                viewItem.Items.Add(GetNodesFromFolder(sub));

            foreach (var session in folder.Sessions)
                viewItem.Items.Add(BuildTreeViewItem(session));

            return viewItem;
        }

        private TreeViewItem BuildTreeViewItem(Folder folder)
        {
            TreeViewItem viewItem = new TreeViewItem();
            viewItem.Tag = folder;
            viewItem.Height = 14;

            Image image = new Image();
            image.Source = FolderImage;
            image.Height = 14;

            TextBlock textBlock = new TextBlock();
            textBlock.Text = folder.Name;

            StackPanel stackPanel = new StackPanel();
            stackPanel.Orientation = Orientation.Horizontal;
            stackPanel.Children.Add(image);
            stackPanel.Children.Add(textBlock);

            viewItem.Header = stackPanel;

            return viewItem;
        }

        private object BuildTreeViewItem(Session session)
        {
            TreeViewItem viewItem = new TreeViewItem();
            viewItem.Tag = session;

            Image image = new Image();
            image.Source = SwitchBlue;
            image.Height = 14;

            TextBlock textBlock = new TextBlock();
            textBlock.Text = session.Name;
            textBlock.Margin = new Thickness(4, 0, 0, 0);

            StackPanel stackPanel = new StackPanel();
            stackPanel.Orientation = Orientation.Horizontal;
            stackPanel.Children.Add(image);
            stackPanel.Children.Add(textBlock);

            viewItem.Header = stackPanel;

            return viewItem;
        }

我希望一棵树显示为...

v {} Folder 1
  v {} SubFolder 1
      [] Session
      [] Session
  > {} SubFolder 2
      [] Session
  > {} SubFolder 3
> {} Folder 2
> {} Folder 3
  [] Session
  [] Session
  [] Session

但我得到的是

v {} Folder 1
v {} Folder 2
v {} Folder 3
  [] Session
  [] Session
  [] Session

当项不包含至少一个子项时,TreeView 或实际节点将不可展开。如果您需要稍后将子项目添加到树中(例如延迟加载),您需要添加一个虚拟项目以使 TreeView 显示扩展器。由于您使用 List 作为子集合,因此您必须首先创建完整的树数据结构并将其立即添加到 TreeView.ItemSource 或简单地使用 ObservableCollection.

为了简单起见,所有节点类型都应存储在一个共享集合中,该集合需要一个公共基类型。然后你可以简单地使用一个 DataTemplateSelector 来动态创建相应的 TreeViewItem.

MainWindow.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <TreeView ItemsSource="{Binding Folders}"
            ItemTemplateSelector="{DynamicResource DataTemplateSelector}">
    <TreeView.Resources>
      <local:DataTemplateSelector x:Key="DataTemplateSelector" />

      <HierarchicalDataTemplate x:Key="FolderDataTemplate"
                                DataType="Folder" 
                                ItemsSource="{Binding ChildItems}">
        <TextBlock Text="{Binding Name}" />

      </HierarchicalDataTemplate>

      <DataTemplate x:Key="SessionDataTemplate" 
                    DataType="Session">
        <TextBlock Text="{Binding Name}" />
      </DataTemplate>
    </TreeView.Resources>
  </TreeView>
</Window>

DataTemplateSelector.cs

class DataTemplateSelector : System.Windows.Controls.DataTemplateSelector
{
  public override DataTemplate
    SelectTemplate(object item, DependencyObject container)
  {
    FrameworkElement element = container as FrameworkElement;

     return item is Session 
       ? element.FindResource("SessionDataTemplate") as DataTemplate 
       : element.FindResource("FolderDataTemplate") as DataTemplate;
  }
}

ViewModel.cs

class ViewModel
{
  public ObservableCollection<Folder> Folders { get; set; }

  public ViewModel()
  {
    this.Folders = new ObservableCollection<Folder>
    {
      new Folder("Folder_1")
      {
        ChildItems = new ObservableCollection<TreeItem>
        {
          new Folder("Folder_1.1")
          {
            ChildItems = new ObservableCollection<TreeItem>
            {
              new Folder("Folder_1.1.1")
              {
                ChildItems = new ObservableCollection<TreeItem>
                {
                  new Session("SessionA"),
                  new Session("SeesionB")
                }
              },
              new Session("SessionA"),
              new Session("SeesionB")
            }
          },
          new Folder("Folder_1.2")
          {
            ChildItems = new ObservableCollection<TreeItem>
            {
              new Session("SessionA"),
              new Session("SeesionB")
            }
          }
        }
      }
    };
  }
}

数据项层次结构

public abstract class TreeItem
{
  public TreeItem(String name)
  {
    this.Name = name;
  }
  public string Name { get; set; }
}

public class Folder : TreeItem
{
  public Folder(string name) : base(name)
  {
  }
  public ObservableCollection<TreeItem> ChildItems { get; set; } = new ObservableCollection<TreeItem>();
}

public class Session : TreeItem
{
  public Session(string name) : base(name)
  {
  }
}