private NotificationChain DeepOnCollection <T1, T2, T3, T4, T5> (
            NotificationChainCallback topLevelCallback,
            Expression <Func <ObservableCollection <T1> > > collectionPropGetter,
            Expression <Func <T1, T2> > prop1Getter,
            Expression <Func <T2, T3> > prop2Getter,
            Expression <Func <T3, T4> > prop3Getter,
            Expression <Func <T4, T5> > prop4Getter)
            where T1 : class, INotifyPropertyChanged
            where T2 : class, INotifyPropertyChanged
            where T3 : class, INotifyPropertyChanged
            where T4 : class, INotifyPropertyChanged
        {
            topLevelCallback.ThrowIfNull("topLevelCallback");
            collectionPropGetter.ThrowIfNull("collectionPropGetter");
            prop1Getter.ThrowIfNull("prop1Getter");
            prop2Getter.ThrowIfNull("prop2Getter");
            prop3Getter.ThrowIfNull("prop3Getter");
            prop4Getter.ThrowIfNull("prop4Getter");

            if (IsFinished || IsDisposed)
            {
                return(this);
            }

            On(collectionPropGetter);

            var mgr = ParentManager.CreateOrGetCollectionManager(collectionPropGetter);

            mgr.CreateOrGet("../" + DependentPropertyName)
            .On(CollectionNotificationChainManager.ObservedCollectionPropertyName)
            .DeepOn(topLevelCallback, prop1Getter, prop2Getter, prop3Getter, prop4Getter);

            return(this);
        }
        private NotificationChain DeepOnCollection <T1, T2> (
            NotificationChainCallback topLevelCallback,
            Expression <Func <ObservableCollection <T1> > > collectionPropGetter,
            Expression <Func <T1, T2> > prop1Getter)
            where T1 : class, INotifyPropertyChanged
        {
            topLevelCallback.ThrowIfNull("topLevelCallback");
            collectionPropGetter.ThrowIfNull("collectionPropGetter");
            prop1Getter.ThrowIfNull("prop1Getter");

            if (IsFinished || IsDisposed)
            {
                return(this);
            }

            // notify when the collection object completely changes
            On(collectionPropGetter);

            // notify when the collection is modified (items added/removed)
            var mgr = ParentManager.CreateOrGetCollectionManager(collectionPropGetter);

            mgr.CreateOrGet("../" + DependentPropertyName)
            .On(CollectionNotificationChainManager.ObservedCollectionPropertyName)
            .On(prop1Getter)
            .AndCall(topLevelCallback);

            return(this);
        }
        /// <summary>
        /// Specifies a property (T1) to observe on the current notifying object, and its sub-properties (T2+) to observe
        /// </summary>
        /// <typeparam name="T0">The top-level (T0) notifyingObject, implements INotifyPropertyChanged</typeparam>
        /// <typeparam name="T1">The property (T1) to observe on T0, implements INotifyPropertyChanged</typeparam>
        /// <typeparam name="T2">The property (T2) to observe on T1, implements INotifyPropertyChanged</typeparam>
        /// <typeparam name="T3">The property (T3) to observe on T2, implements INotifyPropertyChanged</typeparam>
        /// <typeparam name="T4">The property (T4) to observe on T3</typeparam>
        /// <param name="topLevelCallback"></param>
        /// <param name="prop1Getter"></param>
        /// <param name="prop2Getter"></param>
        /// <param name="prop3Getter"></param>
        /// <param name="prop4Getter"></param>
        /// <returns></returns>
        private NotificationChain DeepOn <T0, T1, T2, T3, T4> (
            NotificationChainCallback topLevelCallback,
            Expression <Func <T0, T1> > prop1Getter,
            Expression <Func <T1, T2> > prop2Getter,
            Expression <Func <T2, T3> > prop3Getter,
            Expression <Func <T3, T4> > prop4Getter)
            where T0 : class, INotifyPropertyChanged
            where T1 : class, INotifyPropertyChanged
            where T2 : class, INotifyPropertyChanged
            where T3 : class, INotifyPropertyChanged
        {
            topLevelCallback.ThrowIfNull("topLevelCallback");
            prop1Getter.ThrowIfNull("prop1Getter");
            prop2Getter.ThrowIfNull("prop2Getter");
            prop3Getter.ThrowIfNull("prop3Getter");
            prop4Getter.ThrowIfNull("prop4Getter");

            if (IsFinished || IsDisposed)
            {
                return(this);
            }

            On(prop1Getter);

            var mgr = ParentManager.CreateOrGetManager(prop1Getter);

            mgr.CreateOrGet("../" + DependentPropertyName)
            .DeepOn(topLevelCallback, prop2Getter, prop3Getter, prop4Getter);

            return(this);
        }
        /// <summary>
        /// </summary>
        /// <param name="parentManager"></param>
        /// <param name="dependentPropertyName">Name of the depending property</param>
        public NotificationChain(NotificationChainManager parentManager, String dependentPropertyName)
        {
            parentManager.ThrowIfNull("parentManager");
            dependentPropertyName.ThrowIfNull("dependentPropertyName");

            ParentManager           = parentManager;
            DependentPropertyName   = dependentPropertyName;
            myFireCallbacksCallback = (sender, notifyingProperty, dependentProperty) => Execute(sender, notifyingProperty);
        }
        /// <summary>
        /// Specifies an action to invoke when a notifying property is changed. Multiple actions can be invoked.
        /// </summary>
        /// <param name="onNotifyingPropertyChanged"></param>
        /// <returns></returns>
        public void AddDefaultCall(NotificationChainCallback onNotifyingPropertyChanged)
        {
            onNotifyingPropertyChanged.ThrowIfNull("onNotifyingPropertyChanged");

            if (IsDisposed)
            {
                return;
            }

            myDefaultCallbacks.Add(onNotifyingPropertyChanged);
        }
        /// <summary>
        /// Specifies an action to invoke when a notifying property is changed. Multiple actions can be invoked.
        /// </summary>
        /// <param name="callback"></param>
        /// <returns></returns>
        public NotificationChain AndCall(NotificationChainCallback callback)
        {
            callback.ThrowIfNull("callback");

            if (IsFinished || IsDisposed)
            {
                return(this);
            }

            if (myCallbacks.Contains(callback))
            {
                return(this);
            }

            myCallbacks.Add(callback);

            return(this);
        }
        public void Dispose()
        {
            if (IsDisposed)
            {
                return;
            }

            ParentManager = null;

            myObservedPropertyNames.Clear();
            myObservedPropertyNames = null;

            myObservedRegexes.Clear();
            myObservedRegexes = null;

            myCallbacks.Clear();
            myCallbacks = null;

            myFireCallbacksCallback = null;

            IsDisposed = true;
        }