Я занимаюсь разработкой бизнес-приложения с использованием Silverlight для пользовательского интерфейса и веб-службы WCF для серверной части. В базе данных у меня есть несколько таблиц поиска. Когда служба WCF возвращает бизнес-объект, одно из свойств содержит всю строку из таблицы поиска, а не только внешний ключ, поэтому в пользовательском интерфейсе я могу отображать такие вещи, как описание из таблицы поиска, без повторного вызова оказание услуг. В данный момент я пытаюсь предоставить комбинированный список, связанный со всем списком значений поиска, и правильно его обновить. Бизнес-объект, с которым я имею дело в этом примере, называется Session, а поиск называется SessionType.
Ниже приведено определение комбинированного списка. DataContext устанавливается на экземпляр Session. Я устанавливаю ItemTemplate, потому что в выпадающем списке отображается больше, чем просто список строк.
<ComboBox
x:Name="SessionTypesComboBox"
ItemTemplate="{StaticResource SessionTypeDataTemplate}"
ItemsSource="{Binding Source={StaticResource AllSessionTypes}}"
SelectedItem="{Binding Path=SessionType, Mode=TwoWay}"
/>
И бизнес-объект, и таблица поиска загружаются асинхронно через веб-сервис. Если я больше ничего не сделаю, список со списком будет заполнен SessionTypes, но он не будет показывать начальное значение SessionType из Session. Однако сессия будет обновлена с правильным SessionType, если выбор в выпадающем списке изменен.
Кажется, что происходит то, что привязка SelectedItem не может сопоставить SessionType в Session с его эквивалентом в списке SessionType. Значения объекта совпадают, но ссылки не совпадают.
Обходной путь, который я нашел, состоит в том, чтобы загрузить Session и список SessionTypes, а затем обновить текущий SessionType для Session соответствующим соответствующим из списка 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)
}
Так что в основном у меня есть счетчик, который увеличивается каждый раз, когда я получаю данные от веб-службы. Так как я ожидаю 3 вещи (основной бизнес-объект + 2 таблицы поиска), когда этот счетчик достигает 3, я сопоставляю ссылки.
Мне это кажется очень глупым. Я бы предпочел, чтобы в выпадающем списке были указаны ValueMemberPath и SelectedValue, чтобы сопоставить выбранный элемент с одним в списке.
Может кто-нибудь увидеть более чистый способ сделать это? Такая ситуация очень распространена в бизнес-приложениях, поэтому я уверен, что должен быть хороший способ сделать это.