UIにSilverlightを使用し、バックエンドにWCF Webサービスを使用して、ビジネスアプリケーションを開発しています。データベースには、いくつかのルックアップテーブルがあります。WCFサービスがビジネスオブジェクトを返す場合、プロパティの1つに、外部キーだけでなくルックアップテーブルの行全体が含まれるため、UIで、ルックアップテーブルの説明などを別の呼び出しを行わずに表示できます。サービス。現在私がやろうとしているのは、ルックアップ値のリスト全体にバインドされたコンボボックスを提供し、それを適切に更新することです。この例で扱っているビジネスオブジェクトはSessionと呼ばれ、ルックアップはSessionTypeと呼ばれます。
以下は、コンボボックスの定義です。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値は表示されません。ただし、コンボボックスの選択が変更された場合、Sessionは正しいSessionTypeで更新されます。
起こっているように見えるのは、SelectedItemバインディングがSessionのSessionTypeをSessionTypeリストの同等のものと一致させることができないということです。オブジェクトの値は同じですが、参照は異なります。
私が見つけた回避策は、SessionとSessionTypesリストをロードしてから、Sessionの現在のSessionTypeをSesstionTypesリストの対応するもので更新することです。そうすると、コンボボックスが正しく表示されます。しかし、私にはこれは悪いコードの臭いがあります。すべてが非同期でロードされるため、すべてがいつ使用可能になるかを判断する必要があります。これが私がそれをしている方法です:
私の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)
}
つまり、基本的に、Webサービスからデータを取得するたびにインクリメントされるカウンターがあります。3つのもの(コアビジネスオブジェクト+ 2つのルックアップテーブル)を期待しているので、そのカウンターが3になると、参照を一致させます。
私には、これは非常にハッキーに思えます。コンボボックスでValueMemberPathとSelectedValueを指定して、選択したアイテムをリスト内のアイテムと一致させたいと思います。
誰かがこれを行うためのよりクリーンな方法を見ることができますか?この状況はビジネスアプリでは非常に一般的であるため、それを行うための優れた方法があるはずです。