/// <summary> Invokes the specified handler when a collection property on a property changes, when the source changes,
        /// or when the collection changes. </summary>
        /// <param name="propertyName"> The name of the property on the source. </param>
        /// <param name="collectionPropertyOnPropertyName"> The name of the collection property on the property on the source, i.e. <code>source.property.collectionProperty</code></param>
        /// <param name="handler"> The handler to be invoked at the moments listed above. </param>
        public static void BindCollection(this INotifyPropertyChanged source, string propertyName, string collectionPropertyOnPropertyName, NotifyCollectionChangedEventHandler handler)
        {
            Contract.Requires(GetPropertyType(source.GetType(), propertyName, collectionPropertyOnPropertyName).Implements(typeof(INotifyCollectionChanged)), $"The collection property '{collectionPropertyOnPropertyName}' on the source must implement '{nameof(INotifyCollectionChanged)}'");

            source.Bind(propertyName, collectionPropertyOnPropertyName, RebindCollectionChangedHandler);

            void RebindCollectionChangedHandler(object sender, PropertyChangedEventArgs eventArg)
            {
                IPropertyMutatedEventArgs e = (IPropertyMutatedEventArgs)eventArg;

                Contract.Requires(sender != null);
                Contract.Requires(e != null);
                Contract.Requires(e.OldValue == null || e.OldValue.GetType().Implements(typeof(INotifyCollectionChanged)), "The collection instance must implement 'INotifyCollectionChanged'");
                Contract.Requires(e.NewValue == null || e.NewValue.GetType().Implements(typeof(INotifyCollectionChanged)), "The collection instance must implement 'INotifyCollectionChanged'");
                Contract.Requires(e.OldValue == null || e.OldValue.GetType().Implements(typeof(System.Collections.IList)), "The collection instance must implement 'IList'");
                Contract.Requires(e.NewValue == null || e.NewValue.GetType().Implements(typeof(System.Collections.IList)), "The collection instance must implement 'IList'");

                if (e.OldValue is INotifyCollectionChanged oldCollection)
                {
                    oldCollection.CollectionChanged -= handler;
                }
                if (e.NewValue is INotifyCollectionChanged newCollection)
                {
                    newCollection.CollectionChanged += handler;
                    handler(sender, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newCollection, e.OldValue as System.Collections.IList));
                }
            }
        }
Beispiel #2
0
        private static Action ForwardPropertyChanged(this INotifyPropertyChanged source, ImmutableQueue <JsonPathNode> dependencyNodes, INotifyPropertyChanged target, string targetPropertyName)
        {
            WeakReference <INotifyPropertyChanged> wr = new WeakReference <INotifyPropertyChanged>(target);
            Action unsubscribe = null;

            dependencyNodes = dependencyNodes.Dequeue(out var node);

            switch (node)
            {
            case JsonPathPropertySelectorNode propertySelectorNode:
                string sourcePropertyName = propertySelectorNode.PropertyName;
                unsubscribe = source.ForwardPropertyChanged(sourcePropertyName, target, targetPropertyName);

                if (dependencyNodes.IsEmpty)
                {
                    return(unsubscribe);
                }

                Action unsubscribeNested = null;

                if (propertySelectorNode.GetValue(source) is INotifyPropertyChanged nestedSource)
                {
                    unsubscribeNested = nestedSource.ForwardPropertyChanged(dependencyNodes, target, targetPropertyName);
                    unsubscribe      += unsubscribeNested;
                }

                unsubscribe += source.Bind <INotifyPropertyChanged, object>(sourcePropertyName, (value) =>
                {
                    unsubscribe -= unsubscribeNested;
                    unsubscribeNested?.Invoke();
                    if (!wr.TryGetTarget(out var t))
                    {
                        return;
                    }

                    if (value is INotifyPropertyChanged nestedSrc)
                    {
                        unsubscribeNested = nestedSrc.ForwardPropertyChanged(dependencyNodes, t, targetPropertyName);
                        unsubscribe      += unsubscribeNested;
                    }
                });