改写为:
我可以使用其他一些想法中的一些输入,建议和样本来构建数据表示集合。
该集合需要提供一个databindable,可编辑项目树,但有一点点扭曲:项目需要是两种类型之一,每种类型提供略有不同的特征。这两种类型的项目是Folder和TreeItem。文件夹包含它自己的项目列表(同样,Folder或TreeItem类型)和TreeItem不包含列表。
我目前的方法相当接近,但感觉很狡猾。基本上我有一个抽象的基类,TreeItemBase,它(以循环方式)继承自BindableList。然后我有两个具体的派生类型,Folder和TreeItem,它们都继承自抽象基类。明显的缺陷是TreeItem,它不能包含子项,仍然继承自BindingList;因此,假装它不是一个集合,这取决于一些丑陋的hackery。
是BindingList<>这个系列的基础不佳?一些DataBinding interfaces听起来像是对数据绑定提供了更高程度的控制,但我没有找到一个非常正确的。我的理想虽然提供了一个自定义实现,让我可以控制数据绑定如何遍历集合,并可以检查每个元素的具体类型以确定它是否包含集合,或者它是否是树中的终点
这是XML的快速剪切,以帮助可视化我想要表达的内容;我很容易在XSD中干净地编写结构和规则 - 但我只是很难将它转换为.NET并支持数据绑定。
<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。
理想情况下,我想使用抽象基类方法,以便生成的XML将Folder
和TreeItem
视为complexTypes(无需手动控制序列化),但它可以忽略不计要求。
答案 0 :(得分:3)
也许我对这个问题的知识深度还不够大,但你不能这样做:
有一个接口,以及两个实现该接口的类。
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控件。
很抱歉,如果这不是你想要的,但希望它有所帮助!
答案 1 :(得分:2)
SkippyFire的解决方案似乎比我的更优雅,但我想我会告诉你我是如何解决这个问题的。以下解决方案显示了我为构建可绑定到树视图的集合所做的操作,并且您可以确定已选择了哪些项目。它没有实现任何可绑定列表或任何东西。但是,从您的帖子中不清楚这是否是您想要的。
这是我的XML文件的一个示例:
<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();
}
填充了类别变量后,我只是将列表指定为树视图的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);
}
此解决方案也适用于树中的任何深度。
答案 2 :(得分:1)
我使用了来自source forge的treeviewadv。它有一个非常好的MVC方式来处理树视图类型建模。它是一个Windows窗体控件,它将模型绑定到支持列的树视图样式控件。他们还提供了一些不错的示例代码。
答案 3 :(得分:0)
ObservableCollection可能是最好的选择,加上两个DataTemplates。
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>();
}
// ...
}
你的DataTemplate(包括秘密酱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);
}
}
XAML:
<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>