#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2017 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);
///
///
///
[Obsolete("Prefer the C# 'nameof()' operator to using a Linq expression")]
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();
/// }
/// }
///
///
///
[Obsolete("Prefer the C# 'nameof()' operator to using a Linq expression")]
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