我正在开发一个业务应用程序,将 Silverlight 用于 UI,将 WCF Web 服务用于后端。在数据库中,我有许多查找表。当 WCF 服务返回一个业务对象时,其中一个属性包含查找表中的整行,而不仅仅是外键,因此在 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 列表,然后用 SessionTypes 列表中的对应更新当前 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)
}
所以基本上我有一个计数器,每次我从 web 服务取回数据时都会递增。因为我期待 3 件事(核心业务对象 + 2 个查找表),所以当该计数器达到 3 时,我匹配引用。
对我来说,这似乎很hacky。我宁愿看到组合框指定一个 ValueMemberPath 和 SelectedValue 以将所选项目与列表中的一个匹配。
任何人都可以看到更清洁的方法吗?这种情况在商业应用中很常见,所以我相信一定有一个很好的方法来做到这一点。