WPF哨兵对象及如何检查内部类型

31

正如一些人已经发现的那样,WPF 4 中出现了一个新功能(?)——数据绑定引擎可能会将名为"{DisconnectedItem}"的MS.Internal.NamedObject类实例传递到DataContext中,而不是你的代码所期望的数据项(当模板控件被其ItemsControl断开连接时会发生这种情况)。这些被称为哨兵对象。

在现有代码中,这可能会导致意外异常,因为代码没有准备好处理它。这些异常可能被数据绑定子系统吞噬,也可能会造成严重问题。请注意您的调试控制台。

无论如何,我是从这个 MSDN 论坛了解到这一点的。Sam Bent 的帖子解释了所有内容。现在就去读一下,你会想知道这些的。要点是这些事件本不应该被触发(这是个错误),所以:

如果DataContext是哨兵对象,请忽略DataContextChanged事件。

那么,我想检查我的DataContext。但如何呢?考虑以下内容:

public bool IsSentinelObject(object dataContext)
{
    return (dataContext is MS.Internal.NamedObject);
}

你猜会发生什么?它无法编译,因为 MS.Internal.NamedObject 是 internal 的,无法被我访问。当然,我可以像这样进行 hack:

public bool IsSentinelObject(object dataContext)
{
    return dataContext.GetType().FullName == "MS.Internal.NamedObject"
           || dataContext.ToString() == "{DisconnectedObject}";
}

(或其他工作的东西)。我还遵循了Sam的建议,缓存该对象以供稍后参考相等性检查(它是一个单例)。

当然,这意味着我没有问题,不是真的。但我很好奇,这篇文章肯定会使一些用户受益,所以问问也值得:

有没有一种方法可以精确地检查内部NamedObject类型,而不必使用字符串比较?


DataContextChanged在.NET 4.5中不再被触发,当传递{DisconnectedObject}时会出现此问题。详见:https://connect.microsoft.com/VisualStudio/feedback/details/619658/wpf-virtualized-control-disconnecteditem-reference-when-datacontext-switch。 - Steven Jeuris
在将DataContext绑定到DataContext的内部元素(视图模型组合)时,我仍然在.NET 4.5中遇到类似的问题。解决方法是简单地防止这样做,而是更新所有绑定路径到InnerElement.[whathever] - Steven Jeuris
2个回答

30
在.NET 4.5中,您现在可以与BindingOperations.DisconnectedSource进行比较。

16

这个?

var disconnectedItem = typeof(System.Windows.Data.BindingExpressionBase)
    .GetField("DisconnectedItem", BindingFlags.Static | BindingFlags.NonPublic)
    .GetValue(null);

出于好奇,你是怎么找到那个静态字段的?我从来没有想过要查看BindingExpressionBase。 - Tor Haugen
@Tor Haugen:实际上我尝试了几次,但最终我在整个.NET 4源代码上进行了简单的全文搜索。 :-) - dtb
虽然这不是我寻找的确切答案(如何检查对象是否为某些无法访问的类型),但我接受了这个答案,因为它很整洁,并且非常适合检索实例以检查引用相等性。谢谢。 - Tor Haugen
4
不过,不能依赖于非公开的东西。如果微软决定更改名称,代码将开始出现问题。 - Peter Lillevold
@PeterLillevold 这段代码特别容易找到源头。 (虽然最好添加一个空值检查来验证是否找到了“disconnectedItem”,如果没有找到则抛出异常。) - Steven Jeuris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接