代码之家  ›  专栏  ›  技术社区  ›  STW

.NET数据绑定-文件夹和项目递归树的自定义数据源

  •  1
  • STW  · 技术社区  · 16 年前

    重写:

    集合需要提供一个可数据绑定的, 可编辑 项目树,但有一个小转折:项目需要是两种类型中的一种,每种类型提供稍微不同的特征。这两种类型的项目是Folder和TreeItem。文件夹包含它自己的项目列表(同样,文件夹或TreeItem类型),TreeItem不包含列表。

    我目前的方法相当接近,但感觉粗糙。本质上,我有一个抽象基类TreeItemBase,它(以一种循环的方式)继承自BindableList。然后我有两个具体的派生类型,Folder和TreeItem,它们都继承自抽象基类。明显的缺陷是TreeItem不能包含childitems,仍然从BindingList继承;所以,这是一些丑陋的黑客假装它不是一个集合。

    DataBinding interfaces 听起来他们对数据绑定提供了更高程度的控制,但我还没有找到一个完全正确的。我的 完美的 不过,它提供了一个自定义实现,让我可以控制数据绑定如何遍历集合,并可以检查每个元素的具体类型,以确定它是否包含集合,或者它是否是树中的终点。

    <Items>
      <TreeItem xsi:type="Folder" name="Root">
        <TreeItem xsi:type="Folder" name="Sub1">
          <TreeItem xsi:type="TreeItem" name="Humm"/>
        </TreeItem>
        <TreeItem xsi:type="TreeItem" name="Bleh"/>
        <TreeItem xsi:type="Folder" name="Sub2">
          <TreeItem xsi:type="TreeItem" name="Boo!"/>
        </TreeItem>
      </TreeItem>
    </Items>
    

    我已经在我的方法上做了更多的工作,并且已经接近了我希望使用接口而不是项目的基类来做的事情,但是遇到了一个障碍。我遇到的问题在一本杂志上有报道 seperate question .

    Folder TreeItem 成为复杂类型(不需要手动控制序列化),但这是一个可以忽略的要求。

    4 回复  |  直到 8 年前
        1
  •  3
  •   John B    16 年前

    也许我的知识深度不足以回答这个问题,但你不能这样做:

    有一个接口和两个实现该接口的类。

    interface ITreeItem
    {
        IEnumerable<ITreeItem> GetChildren();
    }
    
    class MyFolder : ITreeItem
    {
        public IEnumerable<ITreeItem> GetChildren()
        {
            // TODO: Return the list of children
        }
    }
    
    class MyITreeItem : ITreeItem
    {
        public IEnumerable<ITreeItem> GetChildren()
        {
            // TODO: Return the list of children
        }
    }
    

    然后,如果您的目标是将集合数据绑定到某个列表,那么您应该能够使用IEnumerable集合执行此操作。在每次调用集合数据绑定时,您应该能够检查项的类型:

    foreach (var node in root.GetChildren())
    {
        if (node is MyFolder)
        {
            var folder = (MyFolder)node;
    
            // Bind fields from the folder object
        }
        else if(node is MyTreeItem)
        {
            var folder = (MyTreeItem)node;
    
            // Bind fields from the tree item object
        }
    }
    

    当我把一个列表嵌套在另一个列表中时,我做了类似的事情。为了显示数据,我设置了嵌套的ListView控件。

        2
  •  2
  •   Johannes    16 年前

    SkippyFire的解决方案似乎比我的更优雅,但我想我会告诉你我是如何解决这个问题的。下面的解决方案显示了我如何构建一个可以绑定到树视图的集合,并且您可以确定选择了哪些项。但它并没有实现任何可绑定列表或任何东西。然而,从你的帖子中还不清楚这是否是你想要的。

     <controls name="Parent" attribute="element">
      <control name="Files" attribute="element">
        <control name="Cashflow" attribute="element">
          <control name="Upload" attribute="leaf"/>
          <control name="Download" attribute="leaf"/>
        </control>
      </control>
      <control name="Quotes" attribute="element">
         <control name="Client Quotes" attribute="leaf"/>
      </control>
    </controls>    
    

    然后我有一个代表每个项目的类。它包含名称、子节点列表(向下1级)、对其父节点的引用以及记录元素属性的字符串。

     public class Items
        {
            public string Name { get; set; }
            public List<Items> SubCategories { get; set; }
            public string IsLeaf { get; set; }
            public string Parent { get; set; }
        }  
    

    从那里,我填充了一个项目列表,如下所示:

    List<Items> categories = new List<Items>();
    
    XDocument categoriesXML = XDocument.Load("TreeviewControls.xml");
    
    categories = this.GetCategories(categoriesXML.Element("controls"));
    

    这将调用GetCategories()方法

    private List<Items> GetCategories(XElement element)
            {
    
                return (from category in element.Elements("control")
                        select new Items()
                        {
                            Parent = element.Attribute("name").Value,
                            Name = category.Attribute("name").Value,
                            SubCategories = this.GetCategories(category),
                            IsLeaf = category.Attribute("attribute").Value
    
                        }).ToList();
            }
    

    填充categories变量后,我只将列表指定为treeview的ItemSource。

    controltree.ItemsSource = categories;
    

    然后,如果树中的选项发生了变化,我会检查该选项是否是叶节点,如果是,我会引发一个事件。

    private void Selection_Changed(object sender, RoutedEventArgs e)
            {
                Items x = controltree.SelectedItem as Items;
    
                if (x.IsLeaf.Equals("leaf"))
                    _parent.RaiseChange(x.Parent+","+x.Name);
            }
    

    此解决方案也适用于树中的任何深度。

        3
  •  1
  •   Joe Caffeine    16 年前

    我用过: treeviewadv 来源于福吉。它有一个非常好的处理树视图类型建模的MVC方法。它是一个windows窗体控件,用于将模型绑定到支持列的treeview样式控件。它们还提供了一些很好的示例代码。

        4
  •  0
  •   user7116    16 年前

    ObservableCollection

    public class TreeItem : INotifyPropertyChanged
    {
        public string Name { get; set; }
        // ...
    }
    
    public class Folder : TreeItem
    {
        public ObservableCollection<TreeItem> Items { get; private set; }
    
        public Folder()
        {
            this.Items = new ObservableCollection<TreeItem>();
        }
        // ...
    }
    

    还有你的数据模板(包括秘方) HierarchicalDataTemplate

    <HierarchicalDataTemplate DataType="{x:Type local:Folder}"
                              ItemsSource="{Binding Path=Items}">
        <TextBlock Text="{Binding Path=Name}"/>
    </HierarchicalDataTemplate>
    <DataTemplate DataType="{x:Type local:TreeItem}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>
    

    public class FolderList : ObservableCollection<TreeItem>
    {
        public FolderList()
        {
            this.Add(new TreeItem { Name = "Hello" });
            this.Add(new TreeItem { Name = "World" });
    
            var folder = new Folder { Name = "Hello World" };
            folder.Items.Add(new TreeItem { Name = "Testing" });
            folder.Items.Add(new TreeItem { Name = "1" });
            folder.Items.Add(new TreeItem { Name = "2" });
            folder.Items.Add(new TreeItem { Name = "3" });
            this.Add(folder);
        }
    }
    

    <Grid>
        <Grid.Resources>
            <local:FolderList x:Key="MyItems" />
            <HierarchicalDataTemplate DataType="{x:Type local:Folder}"
                                      ItemsSource="{Binding Path=Items}">
                <TextBlock Text="{Binding Path=Name}"/>
            </HierarchicalDataTemplate>
            <DataTemplate DataType="{x:Type local:TreeItem}">
                <TextBlock Text="{Binding Path=Name}"/>
            </DataTemplate>
        </Grid.Resources>
        <TreeView>
          <TreeViewItem ItemsSource="{Binding Source={StaticResource MyItems}}"
                        Header="Root" />
        </TreeView>
    </Grid>
    
    推荐文章