Example #1
0
        public void ReductionBindTriggersHandler()
        {
            // Arrange
            const int expected   = 2;
            var       collection = new ProperObservableCollection <Giraffe>();

            collection.BindCollective <Giraffe, int>(nameof(Giraffe.Property), OnCollectivePropertyChanged, Math.Min);
            int result = -1;

            void OnCollectivePropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                result = ((PropertyMutatedEventArgs <int>)e).NewValue;
            }

            // Act
            collection.Add(new Giraffe()
            {
                Property = expected + 1
            });
            collection.Add(new Giraffe()
            {
                Property = expected
            });

            Assert.AreEqual(expected, result);
        }
        //TODO: make the above readonly collections
        /// <summary>
        /// Syncs the contents of two observable collections by two selectors.
        /// </summary>
        public static ProperObservableCollection <TResult> SyncSelect <T, TResult>(this ProperObservableCollection <T> collection, Func <T, TResult> selector, Func <TResult, T> reverseSelector)
        {
            var result = new ProperObservableCollection <TResult>();

            Sync <T, TResult>(collection, result, selector, reverseSelector);
            return(result);
        }
Example #3
0
        public void PropertyOnCollectionChangedIsPropagated()
        {
            bool eventHandlerInvoked = false;
            var  collection          = new ProperObservableCollection <Giraffe>();

            collection.Bind(nameof(Giraffe.Property), (sender, e) =>
            {
                eventHandlerInvoked = true;
            });
            collection.Add(new Giraffe());

            Assert.IsFalse(eventHandlerInvoked);
            collection[0].Property = 0;
            Assert.IsFalse(eventHandlerInvoked);
            collection[0].Property2 = 1;
            Assert.IsFalse(eventHandlerInvoked);
            collection[0].Property = 1;
            Assert.IsTrue(eventHandlerInvoked);
        }
        /// <summary>
        /// Creates an observable collection whose content reflects the content of the specified collection, filtered by the specified predicate and mapped by the specified selector.
        /// This collection is kept up-to-date with respect to changes in the original colletion.
        /// </summary>
        public static MyReadOnlyObservableCollection <TResult> WhereSelectLive <T, TResult>(this IReadOnlyObservableCollection <T> collection, Func <T, bool> predicate, Func <T, TResult> selector)
        {
            Contract.Requires(collection != null);
            Contract.Requires(selector != null);

            var result = new ProperObservableCollection <TResult>();

            collection.CollectionChanged += collectionChanged;
            collectionChanged(collection, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection));
            return(new MyReadOnlyObservableCollection <TResult>(result));

            void collectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                Contract.Requires(sender == collection);

                switch (e.Action)
                {
                case NotifyCollectionChangedAction.Add:
                    result.InsertRange(e.NewStartingIndex, e.NewItems.Cast <T>().Where(predicate).Select(selector));
                    break;

                case NotifyCollectionChangedAction.Remove:
                    result.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
                    break;

                case NotifyCollectionChangedAction.Replace:
                    result.Replace(e.OldStartingIndex, e.OldItems.Count, e.NewItems.Cast <T>().Where(predicate).Select(selector));
                    break;

                case NotifyCollectionChangedAction.Move:
                    result.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex);
                    break;

                case NotifyCollectionChangedAction.Reset:
                    result.Clear();
                    break;

                default:
                    throw new ArgumentException("No defined NotifyCollectionChangedEventArgs.Action specified", nameof(e));
                }
            }
        }
        /// <summary>
        /// Syncs the contents of two observable collections by two selectors.
        /// </summary>
        public static void Sync <T, TResult>(this ProperObservableCollection <T> collection,
                                             ProperObservableCollection <TResult> secondCollection,
                                             Func <T, TResult> selector,
                                             Func <TResult, T> reverseSelector)
        {
            Contract.Requires(collection != null);
            Contract.Requires(selector != null);
            Contract.Requires(reverseSelector != null);

            bool preventBackSync = false;

            collection.CollectionChanged += collectionChanged;
            if (collection.Count != 0)
            {
                collectionChanged(collection, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection));
            }
            secondCollection.CollectionChanged += reverseCollectionChanged;

            //returns whether the sync operation should be performed
            bool toggleSyncFlag()
            {
                preventBackSync = !preventBackSync;
                return(preventBackSync);
            }

            void collectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (toggleSyncFlag())
                {
                    collectionChanged <T, TResult>(e, secondCollection, selector);
                }
            }

            void reverseCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (toggleSyncFlag())
                {
                    collectionChanged <TResult, T>(e, collection, reverseSelector);
                }
            }
        }
Example #6
0
        public void PropertyOnCollectionChangedIsNotPropagatedIfRemoved()
        {
            //Arrange
            bool eventHandlerInvoked = false;
            var  collection          = new ProperObservableCollection <Giraffe>();

            collection.Bind(nameof(Giraffe.Property), (sender, e) =>
            {
                eventHandlerInvoked = true;
            });
            var giraffe = new Giraffe();

            collection.Add(giraffe);

            //Act
            collection.Remove(giraffe);
            giraffe.Property = 1;

            //Assert
            Assert.IsFalse(eventHandlerInvoked);
        }
        private static void collectionChanged <T, TResult>(NotifyCollectionChangedEventArgs e, ProperObservableCollection <TResult> result, Func <T, TResult> selector)
        {
            Contract.Requires(e != null);
            Contract.Requires(result != null);
            Contract.Requires(selector != null);

            switch (e.Action)
            {
            case NotifyCollectionChangedAction.Add:
                if (e.NewStartingIndex == -1)
                {
                    result.AddRange(e.NewItems.Cast <T>().Select(selector));
                }
                else
                {
                    result.InsertRange(e.NewStartingIndex, e.NewItems.Cast <T>().Select(selector));
                }
                break;

            case NotifyCollectionChangedAction.Remove:
                result.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
                break;

            case NotifyCollectionChangedAction.Replace:
                result.Replace(e.OldStartingIndex, e.OldItems.Count, e.NewItems.Cast <T>().Select(selector));
                break;

            case NotifyCollectionChangedAction.Move:
                result.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex);
                break;

            case NotifyCollectionChangedAction.Reset:
                result.Clear();
                break;

            default:
                throw new ArgumentException("No defined NotifyCollectionChangedEventArgs.Action specified", nameof(e));
            }
        }
        /// <summary>
        /// Creates an observable collection whose content reflects the content of the specified collection, filtered by the specified predicate and mapped by the specified many selector.
        /// This collection is kept up-to-date with respect to changes in the original colletion.
        /// </summary>
        public static MyReadOnlyObservableCollection <TResult> WhereSelectManyLive <T, TResult>(this ObservableCollection <T> collection, Func <T, bool> predicate, Func <T, IEnumerable <TResult> > selector)
        {
            Contract.Requires(collection != null);
            Contract.Requires(selector != null);

            List <int> elementCountsPerSourceElement = new List <int>();

            int cumulativeElements(int sourceElementIndex)
            {
                return(elementCountsPerSourceElement.Take(sourceElementIndex).Sum());
            }

            var result = new ProperObservableCollection <TResult>();

            collection.CollectionChanged += collectionChanged;
            collectionChanged(collection, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection));
            return(new MyReadOnlyObservableCollection <TResult>(result));

            void collectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                Contract.Requires(sender == collection);

                switch (e.Action)
                {
                case NotifyCollectionChangedAction.Add:
                {
                    int sourceItemIndex = e.NewStartingIndex;
                    int resultIndex     = cumulativeElements(e.NewStartingIndex);
                    foreach (var newSourceItem in e.NewItems.Cast <T>().Where(predicate))
                    {
                        int originalResultCount = result.Count;
                        result.InsertRange(resultIndex, selector(newSourceItem));
                        int selectedElementCount = result.Count - originalResultCount;

                        resultIndex += selectedElementCount;
                        elementCountsPerSourceElement.Insert(sourceItemIndex++, selectedElementCount);
                    }
                    break;
                }

                case NotifyCollectionChangedAction.Remove:
                {
                    int resultIndex         = cumulativeElements(e.OldStartingIndex);
                    int resultToRemoveCount = elementCountsPerSourceElement.Skip(e.OldStartingIndex).Take(e.OldItems.Count).Sum();
                    result.RemoveRange(resultIndex, resultToRemoveCount);
                    elementCountsPerSourceElement.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
                    break;
                }

                case NotifyCollectionChangedAction.Replace:
                    result.Replace(e.OldStartingIndex, e.OldItems.Count, e.NewItems.Cast <T>().Where(predicate).SelectMany(selector));
                    break;

                case NotifyCollectionChangedAction.Move:
                    result.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex);
                    break;

                case NotifyCollectionChangedAction.Reset:
                    result.Clear();
                    break;

                default:
                    throw new ArgumentException("No defined NotifyCollectionChangedEventArgs.Action specified", nameof(e));
                }
            }
        }