WPF Sentinel objects and how to check for an inter

2019-02-02 03:38发布

问题:

As some of you have discovered, a new feature (?) appeared WPF 4, where the data binding engine may pass your custom control instances of the class MS.Internal.NamedObject with the name "{DisconnectedItem}" into the DataContext - instead of the data item your code is expecting (this happens when a templated control is disconnected by its ItemsControl). These are called sentinel objects.

In existing code, this can lead to spurious exceptions where the code is unprepared for it. These can be swallowed up by the data binding subsystem, or they can wreak havoc. Keep an eye on your debug console.

Anyway, I learned about this on this MSDN forum. And there's a post by Sam Bent which explains it all. Go read it now, you'll want to know this. The essence is that these events should never have fired (that's the bug), so:

Ignore the DataContextChanged event if the DataContext is a sentinel object.

So, so I want to check my DataContext. But how? Consider:

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

Guess what happens? It doesn't compile because MS.Internal.NamedObject is internal, and not accessible to me. Of course, I can hack it like this:

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

(or something, which works). I have also followed Sam's suggestion to cache the object for later reference equality checks (it's a singleton).

Of course, this means I don't have a problem, not really. But I'm curious, and this posting will be sure to benefit some users, so it's worth asking anyway:

Is there a way I can exactly check the type against the internal NamedObject type, without resorting to string comparisons?

回答1:

This one?

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


回答2:

In .NET 4.5, you can now compare against BindingOperations.DisconnectedSource.