#region CPL License /* Nuclex Framework Copyright (C) 2002-2014 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; #if NO_CONCURRENT_COLLECTIONS using System.Collections.Generic; #else using System.Collections.Concurrent; #endif using System.ComponentModel; #if !NO_LINQ_EXPRESSIONS using System.Linq.Expressions; #endif namespace Nuclex.Support { /// Contains helper methods for property change notifications public static class PropertyChangedEventArgsHelper { /// /// A property change event argument container that indicates that all /// properties have changed their value. /// public static readonly PropertyChangedEventArgs Wildcard = new PropertyChangedEventArgs(null); /// Initializes a new property changed argument helper static PropertyChangedEventArgsHelper() { #if NO_CONCURRENT_COLLECTIONS cache = new Dictionary(); #else cache = new ConcurrentDictionary(); #endif } #if !NO_LINQ_EXPRESSIONS /// /// Provides a property change argument container for the specified property /// /// /// Property for which an event argument container will be provided /// /// The event argument container for a property of the specified name /// /// /// This method transparently caches instances of the argument containers /// to avoid feeding the garbage collector. A typical application only has /// in the order of tens to hundreds of different properties for which changes /// will be reported, making a cache to avoid garbage collections viable. /// /// /// /// PropertyChangedEventArgs arguments = /// PropertyChangedEventArgsHelper.GetArgumentsFor(() => SomeProperty); /// /// /// public static PropertyChangedEventArgs GetArgumentsFor( Expression> property ) { return GetArgumentsFor(ObservableHelper.GetPropertyName(property)); } #endif /// /// Provides a property change argument container for the specified property /// /// /// Property for which an event argument container will be provided /// /// The event argument container for a property of the specified name /// /// /// This method transparently caches instances of the argument containers /// to avoid feeding the garbage collector. A typical application only has /// in the order of tens to hundreds of different properties for which changes /// will be reported, making a cache to avoid garbage collections viable. /// /// /// /// PropertyChangedEventArgs arguments = /// PropertyChangedEventArgsHelper.GetArgumentsFor("SomeProperty"); /// /// /// public static PropertyChangedEventArgs GetArgumentsFor(string propertyName) { if(string.IsNullOrEmpty(propertyName)) { return Wildcard; } #if NO_CONCURRENT_COLLECTIONS lock(cache) { // Try to reuse the change notification if an instance already exists PropertyChangedEventArgs arguments; if(!cache.TryGetValue(propertyName, out arguments)) { arguments = new PropertyChangedEventArgs(propertyName); cache.Add(propertyName, arguments); } return arguments; } #else // If an instance for this property already exists, just return it PropertyChangedEventArgs arguments; if(cache.TryGetValue(propertyName, out arguments)) { return arguments; } // No instance existed (at least a short moment ago), so create a new one return cache.GetOrAdd(propertyName, new PropertyChangedEventArgs(propertyName)); #endif } #if !NO_LINQ_EXPRESSIONS /// /// Determines whether the property change affects the specified property /// /// /// Type of the property that will be tested for being affected /// /// /// Property change that has been reported by the observed object /// /// Property that will be tested for being affected /// Whether the specified property is affected by the property change /// /// /// By using this method, you can shorten the code needed to test whether /// a property change notification affects a specific property. You also /// avoid hardcoding the property name, which would have the adverse effect /// of not updating the textual property names during F2 refactoring. /// /// /// /// private void propertyChanged(object sender, PropertyChangedEventArgs arguments) { /// if(arguments.AreAffecting(() => ViewModel.DisplayedValue)) { /// updateDisplayedValueFromViewModel(); /// } // Do not use else if here or wildcards will not work /// if(arguments.AreAffecting(() => ViewModel.OtherValue)) { /// updateOtherValueFromViewModel(); /// } /// } /// /// /// public static bool AreAffecting( this PropertyChangedEventArgs arguments, Expression> property ) { if(arguments.AffectAllProperties()) { return true; } string propertyName = ObservableHelper.GetPropertyName(property); return (arguments.PropertyName == propertyName); } #endif /// /// Determines whether the property change affects the specified property /// /// /// Property change that has been reported by the observed object /// /// Property that will be tested for being affected /// Whether the specified property is affected by the property change /// /// /// By using this method, you can shorten the code needed to test whether /// a property change notification affects a specific property. /// /// /// /// private void propertyChanged(object sender, PropertyChangedEventArgs arguments) { /// if(arguments.AreAffecting("DisplayedValue")) { /// updateDisplayedValueFromViewModel(); /// } // Do not use else if here or wildcards will not work /// if(arguments.AreAffecting("OtherValue")) { /// updateOtherValueFromViewModel(); /// } /// } /// /// /// public static bool AreAffecting( this PropertyChangedEventArgs arguments, string propertyName ) { if(arguments.AffectAllProperties()) { return true; } return (arguments.PropertyName == propertyName); } /// Determines whether a property change notification is a wildcard /// /// Property change notification that will be checked on being a wildcard /// /// /// Whether the property change is a wildcard, indicating that all properties /// have changed. /// /// /// /// As stated on MSDN: "The PropertyChanged event can indicate all properties /// on the object have changed by using either Nothing or String.Empty as /// the property name in the PropertyChangedEventArgs." /// /// /// This method offers an expressive way of checking for that eventuality. /// /// /// /// private void propertyChanged(object sender, PropertyChangedEventArgs arguments) { /// if(arguments.AffectAllProperties()) { /// // Do something /// } /// } /// /// /// public static bool AffectAllProperties(this PropertyChangedEventArgs arguments) { return string.IsNullOrEmpty(arguments.PropertyName); } /// /// Caches PropertyChangedEventArgs instances to avoid feeding the garbage collector /// #if NO_CONCURRENT_COLLECTIONS private static readonly Dictionary cache; #else private static readonly ConcurrentDictionary cache; #endif } } // namespace Nuclex.Support