|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
namespace System.Windows.Forms.Integration
{
/// <summary>
/// Provides a translation function for a mapped property of the host control.
/// </summary>
/// <param name="host">The host control (either WindowsFormsHost or ElementHost) whose property is being mapped.</param>
/// <param name="propertyName">The name of the property being translated.</param>
/// <param name="value">The new value of the property</param>
public delegate void PropertyTranslator(object host, String propertyName, object value);
/// <summary>
/// Defines how property changes in the host control are mapped to the hosted control or element.
/// </summary>
public class PropertyMap
{
private object _sourceObject;
private Dictionary<string, PropertyTranslator> _defaultTranslators;
private Dictionary<String, PropertyTranslator> _wrappedDictionary;
/// <summary>
/// Initializes a new instance of the PropertyMap class.
/// </summary>
public PropertyMap()
{
_wrappedDictionary = new Dictionary<string, PropertyTranslator>();
}
/// <summary>
/// Initializes a new instance of a System.Windows.Forms.Integration.PropertyMap.
/// </summary>
public PropertyMap(object source)
: this()
{
_sourceObject = source;
}
/// <summary>
/// Identifies the source of the properties in the property map.
/// </summary>
protected object SourceObject
{
get
{
return _sourceObject;
}
}
/// <summary>
/// Gets or sets the PropertyTranslator for the specified property name. If no
/// PropertyTranslator exists, it will be added to the property map. If a
/// PropertyTranslator already exists, it will be replaced.
/// </summary>
/// <param name="propertyName">The name of the host control property whose PropertyTranslator you want to get
/// or set.</param>
/// <returns></returns>
public PropertyTranslator this[String propertyName]
{
get
{
ThrowIfPropertyDoesntExistOnSource(propertyName);
PropertyTranslator value;
if (_wrappedDictionary.TryGetValue(propertyName, out value))
{
return value;
}
return null;
}
set
{
//Run through our regular invalid property checks, then
//apply the translator
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentNullException(string.Format(CultureInfo.CurrentCulture, SR.WFI_NullArgument, "propertyName"));
}
if (value == null)
{
throw new ArgumentNullException(string.Format(CultureInfo.CurrentCulture, SR.WFI_NullArgument, "translator"));
}
ThrowIfPropertyDoesntExistOnSource(propertyName);
_wrappedDictionary[propertyName] = value; //This will replace an existing mapping, unlike Add.
Apply(propertyName);
}
}
/// <summary>
/// Returns an ICollection of strings identifying all of the host control
/// properties that have been mapped.
/// </summary>
public ICollection Keys
{
get
{
return _wrappedDictionary.Keys;
}
}
/// <summary>
/// Returns an ICollection of all PropertyTranslator delegates currently being used to
/// map the host control properties.
/// </summary>
public ICollection Values
{
get
{
return _wrappedDictionary.Values;
}
}
/// <summary>
/// Adds a PropertyTranslator delegate to the property map that runs when the specified property
/// of the host control changes.
/// </summary>
/// <param name="propertyName">A string containing the name of the host control property to translate.</param>
/// <param name="translator">A PropertyTranslator delegate which will be called when the specificied property changes.</param>
public void Add(String propertyName, PropertyTranslator translator)
{
if (Contains(propertyName))
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.WFI_PropertyMappingExists, propertyName));
}
this[propertyName] = translator;
}
private void ThrowIfPropertyDoesntExistOnSource(string propertyName)
{
if (SourceObject != null)
{
if (GetProperty(propertyName) == null)
{
// Property 'Foreground' doesn't exist on type 'Window'
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.WFI_PropertyDoesntExist, propertyName, SourceObject.GetType().FullName));
}
}
}
/// <summary>
/// Runs the PropertyTranslator for the given property, based on the
/// SourceObject's current value.
/// </summary>
/// <param name="propertyName"></param>
public void Apply(string propertyName)
{
if (Contains(propertyName))
{
ThrowIfPropertyDoesntExistOnSource(propertyName);
PropertyInfo property = GetProperty(propertyName);
if (property != null)
{
RunTranslator(this[propertyName], SourceObject, propertyName, property.GetValue(SourceObject, null));
}
}
}
/// <summary>
/// Runs the PropertyTranslator for all properties, based on the
/// SourceObject's current values.
/// </summary>
public void ApplyAll()
{
foreach (KeyValuePair<string, PropertyTranslator> entry in DefaultTranslators)
{
Apply(entry.Key);
}
}
private PropertyInfo GetProperty(string propertyName)
{
if (SourceObject == null)
{
return null;
}
return SourceObject.GetType().GetProperty(propertyName, Type.EmptyTypes);
}
/// <summary>
/// Removes all property mappings from the property map so that the properties
/// are no longer translated.
/// </summary>
public void Clear()
{
_wrappedDictionary.Clear();
}
/// <summary>
/// Determines whether the specified property is being mapped.
/// </summary>
/// <param name="propertyName">A string containing the name of the host control property.</param>
/// <returns></returns>
public bool Contains(String propertyName)
{
return _wrappedDictionary.ContainsKey(propertyName);
}
/// <summary>
/// Removes the specified property from the property map so that the property is
/// no longer translated.
/// </summary>
/// <param name="propertyName">A string containing the name of the host control property.</param>
public void Remove(String propertyName)
{
_wrappedDictionary.Remove(propertyName);
}
/// <summary>
/// Gets the list of properties that are translated by default.
/// </summary>
protected Dictionary<string, PropertyTranslator> DefaultTranslators
{
get
{
if (_defaultTranslators == null)
{
_defaultTranslators = new Dictionary<string, PropertyTranslator>();
}
return _defaultTranslators;
}
}
internal bool PropertyMappedToEmptyTranslator(string propertyName)
{
return (this[propertyName] == EmptyPropertyTranslator);
}
/// <summary>
/// Translator for individual property
/// </summary>
internal void EmptyPropertyTranslator(object host, string propertyName, object value)
{
}
/// <summary>
/// Restores the default property mapping for the specified property.
/// </summary>
/// <param name="propertyName">A string containing the name of the host control property.</param>
public void Reset(String propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentNullException(string.Format(CultureInfo.CurrentCulture, SR.WFI_ArgumentNullOrEmpty, "propertyName"));
}
//Resetting means get the value from the list of default values,
//if it's there, otherwise removing the item you've added.
PropertyTranslator value;
if (DefaultTranslators.TryGetValue(propertyName, out value))
{
this[propertyName] = value;
}
else
{
Remove(propertyName);
}
}
/// <summary>
/// Restores the default property mapping for all of the host control's properties.
/// </summary>
public void ResetAll()
{
Clear();
AddDefaultPropertyTranslators();
}
private void AddDefaultPropertyTranslators()
{
foreach (KeyValuePair<string, PropertyTranslator> entry in DefaultTranslators)
{
Add(entry.Key, entry.Value);
}
}
/// <summary>
/// Use this to look up and call the proper property translation delegate. This finds the
/// delegate in this mapping, and calls it with the parameters provided.
/// </summary>
/// <param name="host">The host control (either WindowsFormsHost or ElementHost) whose property is being mapped.</param>
/// <param name="propertyName">The name of the property being translated.</param>
/// <param name="value">The new value of the property.</param>
internal void OnPropertyChanged(object host, string propertyName, object value)
{
PropertyTranslator translator;
if (!_wrappedDictionary.TryGetValue(propertyName, out translator) || translator == null)
{
return;
}
RunTranslator(translator, host, propertyName, value);
}
//Disable the PreSharp error 56500 Avoid `swallowing errors by catching non-specific exceptions.
//In this specific case, we are catching the exception and firing an event which can be handled in user code
//The user handling the event can make the determination on whether or not the exception should be rethrown
//(Wrapped in an InvalidOperationException).
#pragma warning disable 1634, 1691
#pragma warning disable 56500
internal void RunTranslator(PropertyTranslator translator, object host, string propertyName, object value)
{
try
{
translator(host, propertyName, value);
}
catch (Exception ex)
{
PropertyMappingExceptionEventArgs args = new PropertyMappingExceptionEventArgs(ex, propertyName, value);
if (_propertyMappingError != null)
{
_propertyMappingError(SourceObject, args);
}
if (args.ThrowException)
{
throw new InvalidOperationException(SR.WFI_PropertyMapError, ex);
}
}
}
#pragma warning restore 56500
#pragma warning restore 1634, 1691
private event EventHandler<PropertyMappingExceptionEventArgs> _propertyMappingError;
/// <summary>
/// Occurs when an exception is raised by a PropertyMap delegate.
/// </summary>
public event EventHandler<PropertyMappingExceptionEventArgs> PropertyMappingError
{
add { _propertyMappingError += value; }
remove { _propertyMappingError -= value; }
}
}
}
|