2

我正在开发一个业务应用程序,将 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 以将所选项目与列表中的一个匹配。

任何人都可以看到更清洁的方法吗?这种情况在商业应用中很常见,所以我相信一定有一个很好的方法来做到这一点。

4

5 回答 5

2

杰夫,

为了确认我理解您的问题:数据绑定基础结构似乎无法识别认为“相等”两个对象实际上是相等的 - 因此初始SelectedItem设置不正确,因为数据绑定在您的StaticResource集合匹配Session.SessionType。您可以通过“扁平化”引用来解决此问题(即,您强制在代码Session.SessionType中引用等于。Where((st)...First()

我们也遇到过类似的问题

Silverlight 不会因为知道它们代表相同的数据而自动将来自不同“来源”的两个对象“等同”起来,这确实有点道理。就像您说的“对象值相同但引用不同”。但是如何使数据绑定等同于它们呢?

我们想到/尝试的事情:

  • 在类上实现 .Equals() (SessionType在您的情况下)

  • 在类上实施 operator == (SessionType在你的情况下)

  • IEquatable在课堂上实施(SessionType在你的情况下)

  • 使集合仅Strings 并绑定到字符串属性

但最终我们放弃并使用了与您相同的方法 - 从“集合”中“提取”正确的引用等于对象(在所有内容加载后)并将其插入SelectedItem-bound 属性。

我同意你关于代码气味的看法,并且怀疑必须有更好的解决方案。到目前为止,我们在属性访问器和 no-op 中IValueConverter的所有调试都没有找到解决方案——但如果我们找到了,我也会在这里发布。

于 2009-01-26T04:25:40.537 回答
1

我不确定我是否完全理解这个问题(现在还早:))但是你不能在一个电话中转移你需要的所有物品吗?(即使您必须将 3 包装在一个新的 DTO 类中),那么您可以使用一个完整的事件来更新当前会话类型。它仍然不完美,但至少你不必保留任何计数器。

我还将所有这些逻辑移动到 ViewModel 并绑定到它,但这只是我 :)

于 2009-01-24T06:51:17.633 回答
1

你最好绑定到 ObservableCollection 然后使用其他代码(MVVM 的视图模型部分不是一个糟糕的选择)在后台更新它。这样您就可以与 UI 分离,并且在 UI 刚刚绑定时更容易处理更新。

于 2009-01-24T07:38:26.117 回答
0

感谢您的回答,以上所有内容都很有用!我正在转向 MVVM 方式,并将多个服务调用组合成一个(也减少了往返开销)。看起来我暂时会坚持查找重新引用 - 如果我找到更好的方法,我也会发布它。

于 2009-01-26T20:36:55.333 回答
0

geofftnz,你有没有找到任何好的解决方案?

CraigD,我怀疑覆盖 Equals 等是一个很好的解决方案。首先,这是在生成的代理类 SessionType 中完成的,因此这些更改将在每次服务引用更新时丢失。其次,SessionType 设置器中的通知(这里 SessionType 是相同的生成的客户端代理类)使用 ReferenceEquals 调用......所以这是触摸生成代码的另一个地方!好的,第一件事可以通过手工制作的部分类 SessionType 完成(更新后不会丢失),但第二件事肯定不能以同样的方式完成。

于 2010-03-18T16:01:27.647 回答