예제 #1
0
        /// <summary>
        ///     Forwards the <paramref name="source" /> changes to the <paramref name="target" />.
        /// </summary>
        /// <typeparam name="TKey">The type of the key.</typeparam>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="source">The source observable list.</param>
        /// <param name="target">The target <see cref="IEnhancedBindingList{T}" />.</param>
        /// <param name="includeItemChanges">
        ///     if set to <c>true</c> individual items' changes will be propagated to the
        ///     <paramref name="target" />.
        /// </param>
        /// <param name="scheduler">The scheduler to schedule notifications and changes on.</param>
        /// <returns>
        ///     An <see cref="IDisposable" /> which will forward the changes to the <paramref name="target" /> as long as
        ///     <see cref="IDisposable.Dispose" /> hasn't been called.
        /// </returns>
        public static IDisposable ForwardDictionaryChangesTo <TKey, TValue>(
            this IObservableCache <TKey, TValue> source,
            IEnhancedBindingList <TValue> target,
            bool includeItemChanges = false,
            IScheduler scheduler    = null)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            var sourceObservable = scheduler != null
                ? source.Changes.ObserveOn(scheduler)
                : source.Changes;

            return(sourceObservable.Subscribe(cacheChange =>
            {
                switch (cacheChange.ChangeType)
                {
                case ObservableCacheChangeType.ItemAdded:
                    {
                        target.Add(cacheChange.Value);
                        break;
                    }

                case ObservableCacheChangeType.ItemKeyChanged:
                    {
                        // nothing to do here
                        break;
                    }

                case ObservableCacheChangeType.ItemValueChanged:
                    {
                        if (includeItemChanges)
                        {
                            // check whether target list contains the moved element at its expected index position
                            var targetIndex = target.IndexOf(cacheChange.Value);
                            if (targetIndex == -1)
                            {
                                return;
                            }

                            target.ResetItem(targetIndex);
                        }
                        break;
                    }

                case ObservableCacheChangeType.ItemValueReplaced:
                    {
                        if (includeItemChanges)
                        {
                            if (target.Contains(cacheChange.OldValue))
                            {
                                target.Remove(cacheChange.OldValue);
                            }

                            var newValueTargetIndex = target.IndexOf(cacheChange.Value);
                            if (newValueTargetIndex != -1)
                            {
                                target.ResetItem(newValueTargetIndex);
                            }
                            else
                            {
                                target.Add(cacheChange.Value);
                            }
                        }
                        break;
                    }

                case ObservableCacheChangeType.ItemRemoved:
                    {
                        // check whether target list contains the removed item, and delete if so
                        if (target.Contains(cacheChange.Value))
                        {
                            target.Remove(cacheChange.Value);
                        }
                        break;
                    }

                case ObservableCacheChangeType.Reset:
                    {
                        var originalBindingRaiseListChangedEvents = target.RaiseListChangedEvents;
                        try
                        {
                            target.RaiseListChangedEvents = false;

                            ((ICollection <TValue>)target).Clear();
                            target.AddRange(source.CurrentValues);
                        }
                        finally
                        {
                            target.RaiseListChangedEvents = originalBindingRaiseListChangedEvents;

                            if (originalBindingRaiseListChangedEvents)
                            {
                                target.ResetBindings();
                            }
                        }

                        break;
                    }

                default:
                    throw new ArgumentOutOfRangeException(nameof(cacheChange),
                                                          $"Only {ObservableDictionaryChangeType.ItemAdded}, {ObservableDictionaryChangeType.ItemKeyChanged}, {ObservableDictionaryChangeType.ItemValueChanged}, {ObservableDictionaryChangeType.ItemValueReplaced}, {ObservableDictionaryChangeType.ItemRemoved} and {ObservableDictionaryChangeType.Reset} are supported.");
                }
            }));
        }
예제 #2
0
        /// <summary>
        /// Forwards the <paramref name="sourceObservable" /> changes to the <paramref name="target" />.
        /// </summary>
        /// <typeparam name="T">The type of the list item(s)</typeparam>
        /// <param name="sourceObservable">The source observable.</param>
        /// <param name="target">The target binding list.</param>
        /// <param name="includeItemChanges">if set to <c>true</c> individual items' changes will be propagated to the
        /// <paramref name="target" /> via replacing the item completely.</param>
        /// <param name="includeMoves">if set to <c>true</c> move operations will be replicated to the <paramref name="target" />.</param>
        /// <param name="addRangePredicateForResets">This filter predicate tests which elements of the source <see cref="IObservableListChange{T}"/> to add
        /// whenever a <see cref="ObservableListChangeType.Reset"/> is received. A reset is forwarded by clearing the <paramref name="target"/> completely and re-filling it with
        /// the source's values, and this predicate determines which ones are added. If no filter predicate is provided, all source values will be re-added to the <paramref name="target"/>.</param>
        /// <returns></returns>
        public static IDisposable ForwardListChangesTo <T>(
            this IObservable <IObservableListChange <T> > sourceObservable,
            IEnhancedBindingList <T> target,
            bool includeItemChanges = true,
            bool includeMoves       = false,
            Func <T, bool> addRangePredicateForResets = null)
        {
            if (sourceObservable == null)
            {
                throw new ArgumentNullException(nameof(sourceObservable));
            }

            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            if (addRangePredicateForResets == null)
            {
                addRangePredicateForResets = _ => true;
            }

            return(sourceObservable.Subscribe(observableListChange =>
            {
                switch (observableListChange.ChangeType)
                {
                case ObservableListChangeType.ItemAdded:
                    {
                        if (includeMoves)
                        {
                            target.Insert(observableListChange.Index, observableListChange.Item);
                        }
                        else
                        {
                            target.Add(observableListChange.Item);
                        }
                        break;
                    }

                case ObservableListChangeType.ItemChanged:
                    {
                        if (includeItemChanges)
                        {
                            // check whether target list contains the moved element at its expected index position
                            var targetIndex = target.IndexOf(observableListChange.Item);
                            if (targetIndex == -1)
                            {
                                return;
                            }

                            target.ResetItem(targetIndex);
                        }
                        break;
                    }

                case ObservableListChangeType.ItemMoved:
                    {
                        if (includeMoves)
                        {
                            // check whether target list contains the moved element at its expected index position
                            if (target.IndexOf(observableListChange.Item) != observableListChange.OldIndex)
                            {
                                throw new InvalidOperationException($"The source and and target lists are no longer in sync: target has a diffent item at index position {observableListChange.OldIndex} than expected.");
                            }

                            target.Move(observableListChange.Item, observableListChange.Index);
                        }
                        break;
                    }

                case ObservableListChangeType.ItemRemoved:
                    {
                        // check whether target list contains the removed item, and delete if so
                        if (target.Contains(observableListChange.Item))
                        {
                            target.Remove(observableListChange.Item);
                        }
                        break;
                    }

                case ObservableListChangeType.Reset:
                    {
                        var originalBindingRaiseListChangedEvents = target.RaiseListChangedEvents;
                        try
                        {
                            target.RaiseListChangedEvents = false;
                            target.Clear();
                            target.AddRange(observableListChange.List.Where(addRangePredicateForResets));
                        }
                        finally
                        {
                            target.RaiseListChangedEvents = originalBindingRaiseListChangedEvents;
                            if (originalBindingRaiseListChangedEvents)
                            {
                                target.ResetBindings();
                            }
                        }

                        break;
                    }

                default:
                    break;
                }
            }));
        }
예제 #3
0
        /// <summary>
        /// Forwards the <paramref name="sourceObservable" /> changes to the <paramref name="target" />.
        /// </summary>
        /// <typeparam name="TKey">The type of the key.</typeparam>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="sourceObservable">The source observable.</param>
        /// <param name="target">The target <see cref="IEnhancedBindingList{TValue}" />.</param>
        /// <param name="includeItemChanges">if set to <c>true</c> individual items' changes will be propagated to the <paramref name="target" />.</param>
        /// <param name="addRangePredicateForResets">This filter predicate tests which elements of the source <see cref="IObservableDictionary{TKey,TValue}" /> to add
        /// whenever a <see cref="ObservableDictionaryChangeType.Reset" /> is received. A reset is forwarded by clearing the <paramref name="target" /> completely and re-filling it with
        /// the source's values, and this predicate determines which ones are added. If no filter predicate is provided, all source values will be re-added to the <paramref name="target" />.</param>
        /// <param name="addDistinctValuesOnResetOnly">if set to <c>true</c> only distinct values will be re-added on <see cref="ObservableDictionaryChangeType.Reset" /> changes.</param>
        /// <param name="valueComparerForResets">The value equality comparer to use for reset changes and if <paramref name="valueComparerForResets"/> is set to [true]. If none is provided, the default one for the value type will be used</param>
        /// <returns>
        /// An <see cref="IDisposable" /> which will forward the changes to the <paramref name="target" /> as long as <see cref="IDisposable.Dispose" /> hasn't been called.
        /// </returns>
        /// <exception cref="System.ArgumentNullException">
        /// </exception>
        public static IDisposable ForwardDictionaryChangesTo <TKey, TValue>(
            this IObservable <IObservableDictionaryChange <TKey, TValue> > sourceObservable,
            IEnhancedBindingList <TValue> target,
            bool includeItemChanges = false,
            Func <KeyValuePair <TKey, TValue>, bool> addRangePredicateForResets = null,
            bool addDistinctValuesOnResetOnly = true,
            IEqualityComparer <TValue> valueComparerForResets = null)
        {
            if (sourceObservable == null)
            {
                throw new ArgumentNullException(nameof(sourceObservable));
            }
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            if (addRangePredicateForResets == null)
            {
                addRangePredicateForResets = _ => true;
            }

            if (addDistinctValuesOnResetOnly == true && valueComparerForResets == null)
            {
                valueComparerForResets = EqualityComparer <TValue> .Default;
            }

            return(sourceObservable.Subscribe(dictionaryChange =>
            {
                switch (dictionaryChange.ChangeType)
                {
                case ObservableDictionaryChangeType.ItemAdded:
                    {
                        target.Add(dictionaryChange.Value);
                        break;
                    }

                case ObservableDictionaryChangeType.ItemKeyChanged:
                    {
                        // nothing to do here
                        break;
                    }

                case ObservableDictionaryChangeType.ItemValueChanged:
                    {
                        if (includeItemChanges)
                        {
                            // check whether target list contains the moved element at its expected index position
                            var targetIndex = target.IndexOf(dictionaryChange.Value);
                            if (targetIndex == -1)
                            {
                                return;
                            }

                            target.ResetItem(targetIndex);
                        }
                        break;
                    }

                case ObservableDictionaryChangeType.ItemValueReplaced:
                    {
                        if (includeItemChanges)
                        {
                            if (target.Contains(dictionaryChange.OldValue))
                            {
                                target.Remove(dictionaryChange.OldValue);
                            }

                            var newValueTargetIndex = target.IndexOf(dictionaryChange.Value);
                            if (newValueTargetIndex != -1)
                            {
                                target.ResetItem(newValueTargetIndex);
                            }
                            else
                            {
                                target.Add(dictionaryChange.Value);
                            }
                        }
                        break;
                    }

                case ObservableDictionaryChangeType.ItemRemoved:
                    {
                        // check whether target list contains the removed item, and delete if so
                        if (target.Contains(dictionaryChange.Value))
                        {
                            target.Remove(dictionaryChange.Value);
                        }
                        break;
                    }

                case ObservableDictionaryChangeType.Reset:
                    {
                        var originalBindingRaiseListChangedEvents = target.RaiseListChangedEvents;
                        try
                        {
                            target.RaiseListChangedEvents = false;

                            target.Clear();

                            var rangeofValuesToAdd = dictionaryChange.Dictionary
                                                     .Where(keyValuePair => addRangePredicateForResets(keyValuePair))
                                                     .Select(kvp => kvp.Value);

                            if (addDistinctValuesOnResetOnly == true)
                            {
                                rangeofValuesToAdd = rangeofValuesToAdd.Distinct(valueComparerForResets);
                            }

                            target.AddRange(rangeofValuesToAdd);
                        }
                        finally
                        {
                            target.RaiseListChangedEvents = originalBindingRaiseListChangedEvents;

                            if (originalBindingRaiseListChangedEvents)
                            {
                                target.ResetBindings();
                            }
                        }

                        break;
                    }

                default:
                    throw new ArgumentOutOfRangeException(nameof(dictionaryChange),
                                                          $"Only {ObservableDictionaryChangeType.ItemAdded}, {ObservableDictionaryChangeType.ItemKeyChanged}, {ObservableDictionaryChangeType.ItemValueChanged}, {ObservableDictionaryChangeType.ItemValueReplaced}, {ObservableDictionaryChangeType.ItemRemoved} and {ObservableDictionaryChangeType.Reset} are supported.");
                }
            }));
        }