/// <summary>
        /// Binds a <see cref="NotifyCollectionChangedEventHandler"/> to all collections by the name <paramref name="collectionPropertyName"/> on the elements of type <typeparamref name="T"/> on the specified collection.
        /// Note: not all elements of <paramref name="collection"/> are considered; only those of type <typeparamref name="T"/>. You could specify <see cref="INotifyPropertyChanged"/> to revert this.
        /// </summary>
        /// <returns> a disposble which unhooks the <paramref name="handler"/> from all elements. </returns>
        public static IDisposable BindElements <TCollection, T>(
            this TCollection collection,
            string collectionPropertyName,
            NotifyCollectionChangedEventHandler handler) where T : INotifyPropertyChanged where TCollection : INotifyCollectionChanged, IEnumerable <object>
        {
            Contract.Requires(collection != null);
            Contract.Requires(handler != null);

            PropertyChangedEventHandler intermediate = (element, e) =>
            {
                if (e.PropertyName == collectionPropertyName)
                {
                    var _e = (IPropertyMutatedEventArgs)e;
                    if (_e.OldValue is INotifyCollectionChanged oldCollection)
                    {
                        oldCollection.CollectionChanged -= handler;
                    }
                    if (_e.NewValue is INotifyCollectionChanged newCollection)
                    {
                        newCollection.CollectionChanged += handler;
                    }
                }
            };


            var collectionHandler = CollectionChangedEventHandler <object> .Create <TCollection>(add : add, remove : remove);

            collection.CollectionChanged += collectionHandler.CollectionChanged;
            return(new Disposable(() =>
            {
                collection.CollectionChanged -= collectionHandler.CollectionChanged;

                foreach (T element in collection)
                {
                    element.PropertyChanged -= intermediate;
                }
            }));

            void add(TCollection sender, IReadOnlyList <object> newElements, int index)
            {
                Contract.Assert(ReferenceEquals(collection, sender));

                foreach (T element in newElements.OfType <T>())
                {
                    element.PropertyChanged += intermediate;
                    intermediate(element, PropertyMutatedEventArgsExtensions.From(element, collectionPropertyName));
                }
            }

            void remove(TCollection sender, IReadOnlyList <object> oldElements, int index)
            {
                Contract.Assert(ReferenceEquals(collection, sender));

                foreach (T element in oldElements.OfType <T>())
                {
                    element.PropertyChanged -= intermediate;
                }
            }
        }
        /// <summary>
        /// Binds a <see cref="PropertyChangedEventHandler"/> to all elements on the specified collection.
        /// </summary>
        /// <returns> a disposble which unhooks the <paramref name="handler"/> from all elements. </returns>
        public static IDisposable BindElements <TCollection, T>(this TCollection collection, PropertyChangedEventHandler handler) where T : INotifyPropertyChanged where TCollection : INotifyCollectionChanged, IEnumerable <T>
        {
            Contract.Requires(collection != null);
            Contract.Requires(handler != null);

            var collectionHandler = CollectionChangedEventHandler <T> .Create <TCollection>(add : add, remove : remove);

            collection.CollectionChanged += collectionHandler.CollectionChanged;
            return(new Disposable(() =>
            {
                collection.CollectionChanged -= collectionHandler.CollectionChanged;

                foreach (T element in collection)
                {
                    element.PropertyChanged -= handler;
                }
            }));

            void add(TCollection sender, IReadOnlyList <T> newElements, int index)
            {
                Contract.Assert(ReferenceEquals(collection, sender));

                foreach (T element in newElements)
                {
                    element.PropertyChanged += handler;
                }
            }

            void remove(TCollection sender, IReadOnlyList <T> oldElements, int index)
            {
                Contract.Assert(ReferenceEquals(collection, sender));

                foreach (T element in oldElements)
                {
                    element.PropertyChanged -= handler;
                }
            }
        }