【问题标题】:WPF TreeView sub-items not displayingWPF TreeView 子项不显示
【发布时间】:2020-01-08 19:18:05
【问题描述】:

我正在尝试通过在代码中创建并添加我的TreeViewItems 来构建TreeView。我有一个Folders 的对象,其中包含一个子文件夹列表和一个Sessions 列表。所有这些都保存在Root 文件夹对象中。当我使用递归创建TreeViewItems 并将它们添加到我的TreeView 时,会出现根文件夹中的Folders 和Sessions,但不会显示属于其集合的TreViewItems。

当我手动为页面写出 xaml 时,它会根据需要显示,但在添加代码时不会显示。我在调试器中查看了TreeView 对象,我可以在Items 集合中看到TreeViewItems 的第二层和第三层,但它们都没有显示。

我的对象

    [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

【问题讨论】:

  • 如果您使用的是 WPF,那么为什么要在后面的代码中创建控件?该控件应创建为 XAML。您构建 TreeView 的方法是完全错误的。

标签: c# wpf treeview


【解决方案1】:

当项不包含至少一个子项时,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)
  {
  }
}

【讨论】:

  • 这对我使用HierarchicalDataTemplate 的方向很有帮助,但我仍然在视图模型上苦苦挣扎。我应该更清楚用户定义的文件夹层次结构和会话。我无法预加载树,因为我没有设置文件夹结构,但不需要延迟加载。每次添加或删除项目时,我是否应该重新创建整个 TreeView?
  • 不,您不需要再次重建整个树。只需将List 替换为ObservableCollection 即可让TreeView 在修改数据结构(添加或删除或移动项目)时自动更新。
  • 我对“HierarchicalDataTemplate”做了更多的研究,而共享继承和 ViewModel 正是我所缺少的。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-07
  • 1970-01-01
  • 2011-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多