2

I'm looking to create something akin to one of the features of VS or Blend whereby when one selects multiple objects, the property grid shows the value for any property that is shared by all objects and shows nothing for properties that differ between the objects.

I've managed to implement this behaviour for CLR properties using a dynamic object:

  • _knownProperties is just a list of properties which have previously been requested
  • _collection is an IEnumerable instance
public override bool TryGetMember( GetMemberBinder binder, out object result ) {
    Debug.WriteLine( "Getting " + binder.Name + "..." );

    if (!_knownProperties.Contains( binder.Name ))
        _knownProperties.Add( binder.Name );

    IEnumerator it = _collection.GetEnumerator();

    if (!it.MoveNext()) {
        result = null;
        Debug.WriteLine( "No elements in collection" );
        return true;
    }

    Type t = it.Current.GetType();
    PropertyInfo pinf = t.GetProperty( binder.Name );
    if (pinf == null) {
        result = null;
        Debug.WriteLine( "Property doesn't exist." );
        return true;
    }
    result = pinf.GetValue( it.Current, null );

    if (result == null) {
        Debug.WriteLine( "Null result" );
        return true;
    }
    while (it.MoveNext())
        if (!result.Equals( it.Current.GetType().GetProperty( binder.Name ).GetValue( it.Current, null ) )) {
            result = null;
            Debug.WriteLine( "Null result" );
            return true;
        }

    Debug.WriteLine( "Result: " + result.ToString() );
    return true;
}

I'm accessing these properties via WPF Bindings. Can anyone think of a way to implement this for DependencyProperties? If I attempt to bind to an attached property on the object, I get an ArgumentNullException in the property system (where the object that is null can't possibly be null according to the source I have)

  • {Binding Selection.SomeClrProperty,...} works fine (Selection is one of the dynamic objects, SomeClrProperty is a property on every element of the collection.
  • {Binding Selection.(SomeClass.SomeAttachedProperty),...} Fires an error in the property system

The Exception:

System.ArgumentNullException was unhandled
Message=Key cannot be null.
Parameter name: key
Source=System
ParamName=key
StackTrace:
   at System.Collections.Specialized.HybridDictionary.get_Item(Object key)
   at System.ComponentModel.PropertyChangedEventManager.PrivateAddListener(INotifyPropertyChanged source, IWeakEventListener listener, String propertyName)
   at System.ComponentModel.PropertyChangedEventManager.AddListener(INotifyPropertyChanged source, IWeakEventListener listener, String propertyName)
   at MS.Internal.Data.PropertyPathWorker.ReplaceItem(Int32 k, Object newO, Object parent)
   at MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(Int32 k, ICollectionView collectionView, Object newValue, Boolean isASubPropertyChange)
   at MS.Internal.Data.ClrBindingWorker.AttachDataItem()
   at System.Windows.Data.BindingExpression.Activate(Object item)
   ...
svick
  • 236,525
  • 50
  • 385
  • 514
Richard SP.
  • 497
  • 6
  • 15
  • To clarify, you're trying to include both static and instance properties in your collection? ("SomeClass.SomeAttachedProperty" appears to be static, but I see in your GetValue calls that you're not passing null as the source -- a necessary part of getting static values.) – Brannon Mar 21 '12 at 13:32
  • @Brannon, `SomeClass.SomeAttachedProperty` would be a static field but the value of the property is surely atteached to an instance. When using that syntax, none of the functions in the DynamicObject seem to be called, it simply produced the stated error. I imagine that this is due to bindings not actually using the DependencyObject accessors/mutators but getting the values directly out of the property system. – Richard SP. Mar 21 '12 at 13:49

1 Answers1

0

Use OneTime bindings to prevent WPF from trying to put a listener on the attached property:

{Binding Selection.(SomeClass.SomeAttachedProperty), Mode=OneTime, ...}
Anton Tykhyy
  • 19,370
  • 5
  • 54
  • 56