2009-01-24 31 views
2

我正在開發一個業務應用程序,使用Silverlight作爲UI,WCF使用Web服務作爲後端。在數據庫中我有許多查找表。當WCF服務返回一個業務對象時,其中一個屬性包含查找表中的整個行而不僅僅是外鍵,因此在UI中我可以顯示諸如查找表中的描述之類的東西,而無需再次調用服務。我現在要做的是提供一個綁定到整個查找值列表的組合框,並使其正確更新。我在本例中處理的業務對象稱爲Session,查找稱爲SessionType。數據綁定Silverlight組合框與對象列表 - 工作但很難看

下面是組合框的定義。 DataContext被設置爲Session的一個實例。我正在設置ItemTemplate,因爲組合框顯示的不僅僅是一串字符串。

<ComboBox 
x:Name="SessionTypesComboBox" 
ItemTemplate="{StaticResource SessionTypeDataTemplate}" 
ItemsSource="{Binding Source={StaticResource AllSessionTypes}}" 
SelectedItem="{Binding Path=SessionType, Mode=TwoWay}" 
/> 

業務對象和查找表都通過Web服務異步加載。如果我什麼都不做,組合框列表將填充SessionTypes,但它不會顯示來自Session的初始SessionType值。然而,如果組合框選擇被改變,會話將使用正確的SessionType進行更新。

似乎在發生的事情是,SelectedItem綁定不能將Session中的SessionType與SessionType列表中的等價物相匹配。對象值是相同的,但引用不是。

我找到的解決方法是加載會話和SessionTypes列表,然後使用SesstionTypes列表中相應的會話更新Session的當前SessionType。如果我這樣做,那麼組合框顯示正確。但對我來說這有一個不好的代碼味道。因爲一切都異步加載,所以我必須確定什麼時候一切都可用。以下是我正在做的是:

在我的Silverlight用戶控件的代碼隱藏:

// incremented every time we get data back during initial form load. 
private volatile int m_LoadSequence = 0; 

... 

// Loaded event, called when the form is er... loaded. 
private void UserControl_Loaded(object sender, RoutedEventArgs e) 
{ 
    // load session types 
    var sessionTypes = this.Resources["AllSessionTypes"] as Lookups.AllSessionTypes; 
    if (sessionTypes != null) 
    { 
     sessionTypes.DataLoadCompleted += (s, ea) => 
     { 
      IncrementLoadSequence(); 
     }; 
     sessionTypes.LoadAsync(); 
    } 

    // start loading another lookup table, same as above 
    // omitted for clarity 

    // set our DataContect to our business object (passed in when form was created) 
    this.LayoutRoot.DataContext = this.m_Session; 
    IncrementLoadSequence(); 
} 

// This is the smelly part. This gets called by OnBlahCompleted events as web service calls return. 
private void IncrementLoadSequence() 
{ 
    // check to see if we're expecting any more service calls to complete. 
    if (++m_LoadSequence < 3) 
     return; 

    // set lookup values on m_Session to the correct one in SessionType list. 

    // Get SessionType list from page resources 
    var sessionTypes = this.Resources["AllSessionTypes"] as Lookups.AllSessionTypes; 

    // Find the matching SessionType based on ID 
    this.m_Session.SessionType = sessionTypes.Where((st) => { return st.SessionTypeID == this.m_Session.SessionType.SessionTypeID; }).First(); 

    // (other lookup table omitted for clarity) 
} 

所以基本上我有一個被我每次得到的數據從WebService後面加一個計數器。由於我期待3件事情(核心業務對象+ 2查找表),當該計數器達到3時,我會匹配引用。

對我來說,這看起來很詭異。我寧願看到組合框指定一個ValueMemberPath和SelectedValue,以將所選項目與列表中的一個匹配。

任何人都可以看到一個更乾淨的方式嗎?這種情況在商業應用程序中非常常見,所以我確信必須有一種很好的方式來做到這一點。

回答

1

我不確定我是否完全理解了這個問題(這是早期的:))但是不能只在一次調用中轉移所需的所有項目嗎? (即使你必須在新的DTO類中包裝3),那麼你可以使用完整的事件來更新當前的會話類型。這還不完美,但至少你不必保留任何櫃檯。

我也希望移居所有邏輯一個ViewModel和公正綁定到,不過這只是我:)

1

你會更好,然後使用一些其他的代碼綁定到一個ObservableCollection(視圖模型MVVM的一部分不是一個不錯的選擇)在後臺更新它。這樣你就可以與用戶界面分離,並且隨着用戶界面的綁定,更容易處理更新。

2

傑夫,

要確認我理解您的問題:數據綁定的基礎設施似乎並沒有認識到,兩個對象考慮「平等」 實際上等於 - 因此最初SelectedItem未設置正確,因爲數據綁定在您的StaticResource集合中找不到與Session.SessionType匹配的引用等於對象。 您可以通過「扁平化」的引用(即你Session.SessionType是在Where((st)...First()碼參考等於解決這個問題。

我們有一個similar problem

確實還挺因爲知道它們代表了相同的數據,所以Silverlight不會自動'等同'來自差異'來源'的兩個對象。就像你說的「對象值相同但參考不是」。你怎麼能讓數據庫ng等同於他們?我們認爲

事情/試過:

  • 實現(在你的情況SessionType).Equals()在類

  • 實現==操作符的類(SessionType你的情況)

  • 實現的類IEquatableSessionType你的情況)

  • 使得集合只String S和結合字符串屬性

但最終我們放棄了,並使用相同的方法,因爲你 - 「提取」從「收集正確的引用,等於對象'(在所有內容加載完畢後),並將其撥入SelectedItem -bound屬性。

我同意你關於代碼嗅覺,並懷疑必須是一個更好的解決方案。到目前爲止,我們在屬性訪問器和no-op IValueConverter的所有調試都沒有找到解決方案 - 但是如果我們這樣做,我也會在這裏發佈它。

0

感謝您的回答,以上所有內容都非常有用!我正在朝着MVVM的方向發展,並將多個服務調用合併爲一個服務調用(也減少了往返開銷)。看起來我會繼續堅持查找重新引用 - 如果我找到更好的方法,我會發布它。

0

geofftnz, 你有沒有找到任何漂亮的解決方案呢?

CraigD, 我懷疑重寫Equals等是一個很好的解決方案。首先,這是在生成的代理類SessionType內完成的,因此這些更改將在每個服務引用更新中丟失。其次,在SessionType設置器(這裏SessionType是相同的生成的客戶端代理類)中的通知使用ReferenceEquals調用...所以這是另一個觸摸生成代碼的地方!好的,第一件事可以通過手工製作的部分類SessionType來完成(所以更新後不會丟失),但第二件事當然不能以同樣的方式完成。

+0

試圖記住原來的問題 - 我從那時起就改變了工作:) – geofftnz 2010-03-18 21:23:24

相關問題