#if XFORMS
namespace Caliburn.Micro.Core.Xamarin.Forms
#else
namespace Caliburn.Micro
#endif
{
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
#if !SILVERLIGHT
using System.ComponentModel;
#endif
///
/// A service that is capable of properly binding values to a method's parameters and creating instances of .
///
public static class MessageBinder {
///
/// The special parameter values recognized by the message binder along with their resolvers.
/// Parameter names are case insensitive so the specified names are unique and can be used with different case variations
///
public static readonly Dictionary> SpecialValues =
new Dictionary>(StringComparer.OrdinalIgnoreCase)
{
{"$eventargs", c => c.EventArgs},
#if XFORMS
{"$datacontext", c => c.Source.BindingContext},
{"$bindingcontext", c => c.Source.BindingContext},
#else
{"$datacontext", c => c.Source.DataContext},
#endif
{"$source", c => c.Source},
{"$executioncontext", c => c},
{"$view", c => c.View}
};
///
/// Custom converters used by the framework registered by destination type for which they will be selected.
/// The converter is passed the existing value to convert and a "context" object.
///
public static readonly Dictionary> CustomConverters =
new Dictionary>
{
{
typeof (DateTime), (value, context) => {
DateTime result;
DateTime.TryParse(value.ToString(), out result);
return result;
}
}
};
///
/// Determines the parameters that a method should be invoked with.
///
/// The action execution context.
/// The parameters required to complete the invocation.
/// The actual parameter values.
public static object[] DetermineParameters(ActionExecutionContext context, ParameterInfo[] requiredParameters) {
var providedValues = context.Message.Parameters.OfType().Select(x => x.Value).ToArray();
var finalValues = new object[requiredParameters.Length];
for (int i = 0; i < requiredParameters.Length; i++) {
var parameterType = requiredParameters[i].ParameterType;
var parameterValue = providedValues[i];
var parameterAsString = parameterValue as string;
if (parameterAsString != null)
finalValues[i] = CoerceValue(parameterType,
EvaluateParameter(parameterAsString, parameterType, context), context);
else finalValues[i] = CoerceValue(parameterType, parameterValue, context);
}
return finalValues;
}
///
/// Transforms the textual parameter into the actual parameter.
///
public static Func EvaluateParameter =
(text, parameterType, context) => {
Func resolver;
return SpecialValues.TryGetValue(text, out resolver) ? resolver(context) : text;
};
///
/// Coerces the provided value to the destination type.
///
/// The destination type.
/// The provided value.
/// An optional context value which can be used during conversion.
/// The coerced value.
public static object CoerceValue(Type destinationType, object providedValue, object context) {
if (providedValue == null) {
return GetDefaultValue(destinationType);
}
var providedType = providedValue.GetType();
if (destinationType.IsAssignableFrom(providedType)) {
return providedValue;
}
if (CustomConverters.ContainsKey(destinationType)) {
return CustomConverters[destinationType](providedValue, context);
}
try {
#if !WinRT && !XFORMS
var converter = TypeDescriptor.GetConverter(destinationType);
if (converter.CanConvertFrom(providedType)) {
return converter.ConvertFrom(providedValue);
}
converter = TypeDescriptor.GetConverter(providedType);
if (converter.CanConvertTo(destinationType)) {
return converter.ConvertTo(providedValue, destinationType);
}
#endif
#if WinRT || XFORMS
if (destinationType.GetTypeInfo().IsEnum) {
#else
if (destinationType.IsEnum) {
#endif
var stringValue = providedValue as string;
if (stringValue != null) {
return Enum.Parse(destinationType, stringValue, true);
}
return Enum.ToObject(destinationType, providedValue);
}
if (typeof (Guid).IsAssignableFrom(destinationType)) {
var stringValue = providedValue as string;
if (stringValue != null) {
return new Guid(stringValue);
}
}
}
catch {
return GetDefaultValue(destinationType);
}
try {
return Convert.ChangeType(providedValue, destinationType, CultureInfo.CurrentCulture);
}
catch {
return GetDefaultValue(destinationType);
}
}
///
/// Gets the default value for a type.
///
/// The type.
/// The default value.
public static object GetDefaultValue(Type type) {
#if WinRT || XFORMS
var typeInfo = type.GetTypeInfo();
return typeInfo.IsClass || typeInfo.IsInterface ? null : System.Activator.CreateInstance(type);
#else
return type.IsClass || type.IsInterface ? null : Activator.CreateInstance(type);
#endif
}
}
}