/// <summary> Triggers the PropertyChanged event if the new value different from the current value. </summary> /// <param name="field"> A reference to the current value. </param> /// <param name="value"> The new value of the property. </param> /// <param name="propertyName"> The name of the property. </param> protected void Set <TProperty>(ref TProperty field, TProperty value, [CallerMemberName] string propertyName = null) { TProperty oldValue = field; if (!EqualityComparer <TProperty> .Default.Equals(oldValue, value)) { field = value; var e = new PropertyMutatedEventArgs <TProperty>(propertyName, oldValue, value); base.OnPropertyChanged(e); } }
protected void OnPropertyChanged <TParameter>(PropertyMutatedEventArgs <TParameter> e) { if (postponeInvocation) { postponedInvocationEventArgs.Add(e); } else { Invoke(e); } }
/// <summary> Triggers the PropertyChanged event if the new value different from the current value. </summary> /// <param name="value"> The new value of the property. </param> /// <param name="oldValue"> The current value. </param> /// <param name="set"> A function setting the new value. If null is specified, it is not invoked: no action is undertaken to set the value. </param> /// <param name="propertyName"> The name of the property. </param> /// <returns> whether the value changed. </returns> protected bool Set <TParameter>(TParameter value, TParameter oldValue, Action <TParameter> set = null, [CallerMemberName] string propertyName = null) { if (!EqualityComparer <TParameter> .Default.Equals(oldValue, value)) { set?.Invoke(value); var e = new PropertyMutatedEventArgs <TParameter>(propertyName, oldValue, value); OnPropertyChanged(e); return(true); } return(false); }
/// <summary> Triggers the PropertyChanged event if the new value different from the current value. </summary> /// <param name="field"> A reference to the current value. </param> /// <param name="value"> The new value of the property. </param> /// <param name="propertyName"> The name of the property. </param> /// <returns> whether the value changed. </returns> protected bool Set <TParameter>(ref TParameter field, TParameter value, [CallerMemberName] string propertyName = null) { TParameter oldValue = field; if (!EqualityComparer <TParameter> .Default.Equals(oldValue, value)) { field = value; var e = new PropertyMutatedEventArgs <TParameter>(propertyName, oldValue, value); this.OnPropertyChanged(e); return(true); } return(false); }
/// <summary> /// Binds the property on many elements into a combination for which <paramref name="handler"/> is invoked whenever its value changes. /// This is useful for instance when you have a property on a collection that is the reduction of a property of collection element (in which case you can use the handler to set the reduced value). /// </summary> /// <typeparam name="TElement"> The type of the elements of the specified observable collection. </typeparam> /// <typeparam name="TElementProperty"> The type of the property on <typeparamref name="TElement"/> whose name is <paramref name="propertyName"/> and whose values are to be reduced. </typeparam> /// <param name="collection"> The collection to monitor for property changes. </param> /// <param name="propertyName"> The name of the property on the collection elements to monitor. </param> /// <param name="handler"> The function handling the change of the reduced value. The first argument is the collection and the second the new value. </param> /// <param name="combine"> The function taking the previous reduced value and an updated value of one of the properies, and returns their reduction. </param> /// <param name="valueIfCollectionEmpty"> The value to be taken if the specified collection is empty. </param> /// <param name="equalityComparer"> The function comparing <typeparamref name="TElementProperty"/>s for equality, determining when to invoke <paramref name="handler"/>. </param> public static void BindCollective <TElement, TElementProperty>(this INotifyCollectionChanged collection, string propertyName, PropertyChangedEventHandler handler, Func <TElementProperty /*previous resultant*/, TElementProperty /*new property*/, TElementProperty> combine, Option <TElementProperty> valueIfCollectionEmpty = default, IEqualityComparer <TElementProperty> equalityComparer = null) { Contract.Requires(collection != null); Contract.Requires(collection is IEnumerable <TElement>); Contract.Requires(HasProperty <TElement>(propertyName)); Contract.Requires(handler != null); Contract.Requires(combine != null); equalityComparer = equalityComparer ?? EqualityComparer <TElementProperty> .Default; Option <TElementProperty> resultant = Option <TElementProperty> .None; collection.Bind(propertyName, elementPropertyChanged, typeof(TElement), true, onElementRemoved); setResultant(recomputeResultant()); // if handler shouldn't be triggered on the initially present elements, set as resultant = recomputeResultant() rather than through setResultant Option <TElementProperty> recomputeResultant() { Option <TElementProperty> result = Option <TElementProperty> .None; foreach (var element in (IEnumerable <TElement>)collection) { var propValue = GetPropertyValue <TElement, TElementProperty>(element, propertyName); if (!result.HasValue) { result = propValue; } else { result = combine(result.Value, propValue); } } return(result); // returns Option.None when the collection is empty } void onElementRemoved(object sender, PropertyChangedEventArgs e) { Contract.Assert(resultant.HasValue); var propValue = GetPropertyValue <TElement, TElementProperty>((TElement)sender, e.PropertyName); if (equalityComparer.Equals(propValue, resultant.Value)) // if the resultant value was removed from the collection { setResultant(recomputeResultant()); } } void elementPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == propertyName) { var newValue = GetPropertyValue <TElement, TElementProperty>((TElement)sender, e.PropertyName); var newResultant = resultant.HasValue ? combine(resultant.Value, newValue) : newValue; setResultant(newResultant); } } void setResultant(Option <TElementProperty> newResultant) { // newResultant: None means the collection is empty. TElementProperty newValue; if (newResultant.HasValue) { newValue = newResultant.Value; } else { if (valueIfCollectionEmpty.HasValue) { newValue = valueIfCollectionEmpty.Value; } else { // don't trigger the handler if there is no value to set return; } } if (resultant.HasValue) { TElementProperty oldValue = resultant.Value; if (!equalityComparer.Equals(oldValue, newValue)) { handle(oldValue, newValue); } } else { TElementProperty oldValue = valueIfCollectionEmpty.ValueOrDefault(); handle(oldValue, newValue); } } void handle(TElementProperty oldValue, TElementProperty newValue) { var arg = new PropertyMutatedEventArgs <TElementProperty>(propertyName, oldValue, newValue); handler(collection, arg); } }