2014-07-07 60 views
1

这篇文章的标题可能会使用一些工作,但我无法表达自己的一些细节。如何用父项和子项实现组合框的层次结构?

所以在这里。

我有一个简单的WPF应用程序,它从第三方API检索StateCityComplexBuilding的列表。

public class State 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class City 
{ 
    public int Id { get; set; } 
    public int ParentId { get; set; } // FK to Id in State 
    public string Name { get; set; } 
} 

public class Complex 
{ 
    public int Id { get; set; } 
    public int ParentId { get; set; } // FK to Id in City 
    public string Name { get; set; } 
} 

public class Building 
{ 
    public int Id { get; set; } 
    public int ParentId { get; set; } // FK to Id in Complex 
    public string Name { get; set; } 
} 

当填充一些数据时,这些列表可能看起来像这样;

StateOptions = new ObservableCollection<State> 
{ 
    new State() { Id = 1, Name = "California"}, 
    new State() { Id = 2, Name = "New York"}, 
}; 

CityOptions = new ObservableCollection<City> 
{ 
    new City() { Id = 1, ParentId = 1, Name = "Los Angeles"}, 
    new City() { Id = 2, ParentId = 2, Name = "New York City"} 
}; 

ComplexOptions = new ObservableCollection<Complex> 
{ 
    new Complex() { Id = 1, ParentId = 1, Name = "Los Angeles International Airport"}, 
    new Complex() { Id = 2, ParentId = 2, Name = "John F. Kennedy International Airport"} 
}; 

BuildingOptions = new ObservableCollection<Building> 
{ 
    new Building() { Id = 1, ParentId = 1, Name = "Terminal 1"}, 
    new Building() { Id = 2, ParentId = 1, Name = "Terminal 2"}, 
    new Building() { Id = 3, ParentId = 1, Name = "Terminal 3"}, 
    new Building() { Id = 4, ParentId = 2, Name = "Terminal 1"}, 
    new Building() { Id = 5, ParentId = 2, Name = "Terminal 2"}, 
    new Building() { Id = 6, ParentId = 2, Name = "Terminal 3"}, 
}; 

使用这些列表,我需要创建一个新的对象,Foo,然后回发到API。

public class Foo 
{ 
    public int Id { get; set; } 

    public int StateId { get; set; 
    public int CityId { get; set; 
    public int ComplexId { get; set; 
    public int BuildingId { get; set; 
} 

这本身就是很简单,因为你可以只是把名单四个观察集合,并使用它们作为ItemSource四个ComboBox元素结合。然后,您可以使用SelectedItem绑定属性为每个这些组合框创建Foo对象。

这将是整洁,如果这些是扁平列表,但请注意City,ComplexBuilding模型中的ParentId字段。

这样的四个列表必须处理的层次。实际上,ParentId这个名称很具误导性,因为它们没有引用另一个相同类型的实体。当看到Foo类时,这变得很明显,它们具有上述所有的外键。

因此,我需要筛选City组合框的项目,以仅显示Id等于State组合框的SelectedItem的ParentId的项目。 ComplexBuilding组合框也是这种情况。

为了进一步增加这一点,我宁愿通过创建一个WPF用户控件,因为这四个名单将在未来的许多观点再次出现,并要求相同的功能解决了这个问题。然而我对所有的解决方案都很开放。我还希望在不进行额外访问的情况下执行此操作,因为列表非常大。

为了您的方便,我在https://db.tt/IAyOiq35创造了一个示范项目。该项目通过将这四个列表视为单位来说明问题所在,因此尽管纽约州被选为国家,您仍然可以选择洛杉矶国际机场作为综合体。这显然是错误的,这正是我想阻止用户做错的那种错误。

如果还有其他问题,请留下评论。

+0

当您在一个列表中选择一个项目时,您无法过滤其他列表(例如,基于parentId创建一个子集)? – scheien

+0

我非常确定这样做可以正常工作,但正如我所看到的那样会将大部分逻辑推入视图模型。试图避免这种情况,因为这种完全相同的逻辑将被复​​制到许多未来的视图模型中,并且我喜欢避免复制代码。 –

+1

构建分层数据模型。 – Sheridan

回答

1

一点的讨论,一些试验和错误后,我们发现有一些方法来解决这个问题。

在大多数情况下,第一个解决方案就足够了,而可能是由最值得推荐的,因为它很容易用最少的逻辑视图模型实现的,迫使XAML的层次结构。

不过,我增加了两个解决方案,很多人可能也已经考虑到了这些。事实上,我们最终在这里采用了第三种解决方案,认为第一种解决方案不会解释孤儿,除非您将每个孤儿添加为每个项目的孩子。

方案1(推荐)

由于sugguested通过谢里登你可以建立一个层次数据模型,使您NodeHierarchicalNode。这可能是最简单的解决方案,如果可以的话,我可能会推荐使用这个解决方案。

public class Node 
{ 
    public int Id { get; set; } 
    public int ParentId { get; set; } 
    public string Name { get; set; } 
} 

public class HierarchicalNode 
{ 
    public int Id { get; set; } 
    public int ParentId { get; set; } 
    public List<HierarchicalNode> Children { get; set; } 
} 

从这里,你可以绑定你List<TreeNode>作为第一个组合框的ItemSource。然后,您将第二个组合框的ItemSource设置为第一个组合框的SelectedItemChildren属性。添加尽可能多的组合框,因为您的层次结构中有儿童级别。

解决方案2

另一种解决方案是使用在您的视图模型,其中TKeyParentIdILookup<int, HierarchyEntityBase>。在SelectedItem的绑定属性中,您将使用该项目为该子项组合框创建一个新的ItemSource,方法是使用该项目的ParentId作为查找中的关键字。

这可以工作,但是用这种方式切换组合框的项目源会有几个问题。例如,您可能需要实现一种方法来在切换其项目源之前记住组合框的选定值,然后尝试再次在新的项目列表中找到该项目。

解决方案3

上述解决方案都没有支撑我们的具体使用情况,所以这是我们最终实现了一个。

我们有四个表,其中每行有一个可选的ParentIdParentTableType。换句话说,我们会为某些客户建立实体层次结构,而其他客户则根本没有层次结构。这意味着上述解决方案不会很好,因为它不解释孤儿。

解决方案是创建一个自定义用户控件HierarchicalComboBox,它继承自ComboBox。这些将支持类型为HierarchicalEntityBase的实体,其中其他实体具有Id,ParentIdParentTableType的属性。

这个新的用户控件满足了我所有的要求,但实现起来相当痛苦。

基本的想法是修改您的ItemSource作为父或子组合框的选择更改。在父组合框的选择更改的情况下,子组合框必须更新其子项并相应地添加其孤儿。在某些情况下,用户可能会从层次结构中的最后一个组合框开始,在这种情况下,您必须正确设置父项作为每个父组合框的SelectedItem,一直到顶部。

你最好通过实现依赖项属性绑定到父HierarchicalComboBoxElementName做到这一点,而另一个依赖项属性绑定到应该被用来查找的项目HierarchicalEntityBase类型的实体的完整列表实际的ItemSource

成功的关键是我们意识到我们需要一个自定义Action来替换父组合框的选择更改事件,因为这给了我们机会从当前组合框的选择更改事件有条件地触发该操作。

我不会详细介绍实施细节,但如果您好奇,请放下问题。