2014-07-07 59 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來替換父組合框的選擇更改事件,因爲這給了我們機會從當前組合框的選擇更改事件有條件地觸發該操作。

我不會詳細介紹實施細節,但如果您好奇,請放下問題。