MessageBinder.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #if XFORMS
  2. namespace Caliburn.Micro.Core.Xamarin.Forms
  3. #else
  4. namespace Caliburn.Micro
  5. #endif
  6. {
  7. using System;
  8. using System.Globalization;
  9. using System.Linq;
  10. using System.Reflection;
  11. using System.Collections.Generic;
  12. #if !SILVERLIGHT
  13. using System.ComponentModel;
  14. #endif
  15. /// <summary>
  16. /// A service that is capable of properly binding values to a method's parameters and creating instances of <see cref="IResult"/>.
  17. /// </summary>
  18. public static class MessageBinder {
  19. /// <summary>
  20. /// The special parameter values recognized by the message binder along with their resolvers.
  21. /// Parameter names are case insensitive so the specified names are unique and can be used with different case variations
  22. /// </summary>
  23. public static readonly Dictionary<string, Func<ActionExecutionContext, object>> SpecialValues =
  24. new Dictionary<string, Func<ActionExecutionContext, object>>(StringComparer.OrdinalIgnoreCase)
  25. {
  26. {"$eventargs", c => c.EventArgs},
  27. #if XFORMS
  28. {"$datacontext", c => c.Source.BindingContext},
  29. {"$bindingcontext", c => c.Source.BindingContext},
  30. #else
  31. {"$datacontext", c => c.Source.DataContext},
  32. #endif
  33. {"$source", c => c.Source},
  34. {"$executioncontext", c => c},
  35. {"$view", c => c.View}
  36. };
  37. /// <summary>
  38. /// Custom converters used by the framework registered by destination type for which they will be selected.
  39. /// The converter is passed the existing value to convert and a "context" object.
  40. /// </summary>
  41. public static readonly Dictionary<Type, Func<object, object, object>> CustomConverters =
  42. new Dictionary<Type, Func<object, object, object>>
  43. {
  44. {
  45. typeof (DateTime), (value, context) => {
  46. DateTime result;
  47. DateTime.TryParse(value.ToString(), out result);
  48. return result;
  49. }
  50. }
  51. };
  52. /// <summary>
  53. /// Determines the parameters that a method should be invoked with.
  54. /// </summary>
  55. /// <param name="context">The action execution context.</param>
  56. /// <param name="requiredParameters">The parameters required to complete the invocation.</param>
  57. /// <returns>The actual parameter values.</returns>
  58. public static object[] DetermineParameters(ActionExecutionContext context, ParameterInfo[] requiredParameters) {
  59. var providedValues = context.Message.Parameters.OfType<Parameter>().Select(x => x.Value).ToArray();
  60. var finalValues = new object[requiredParameters.Length];
  61. for (int i = 0; i < requiredParameters.Length; i++) {
  62. var parameterType = requiredParameters[i].ParameterType;
  63. var parameterValue = providedValues[i];
  64. var parameterAsString = parameterValue as string;
  65. if (parameterAsString != null)
  66. finalValues[i] = CoerceValue(parameterType,
  67. EvaluateParameter(parameterAsString, parameterType, context), context);
  68. else finalValues[i] = CoerceValue(parameterType, parameterValue, context);
  69. }
  70. return finalValues;
  71. }
  72. /// <summary>
  73. /// Transforms the textual parameter into the actual parameter.
  74. /// </summary>
  75. public static Func<string, Type, ActionExecutionContext, object> EvaluateParameter =
  76. (text, parameterType, context) => {
  77. Func<ActionExecutionContext, object> resolver;
  78. return SpecialValues.TryGetValue(text, out resolver) ? resolver(context) : text;
  79. };
  80. /// <summary>
  81. /// Coerces the provided value to the destination type.
  82. /// </summary>
  83. /// <param name="destinationType">The destination type.</param>
  84. /// <param name="providedValue">The provided value.</param>
  85. /// <param name="context">An optional context value which can be used during conversion.</param>
  86. /// <returns>The coerced value.</returns>
  87. public static object CoerceValue(Type destinationType, object providedValue, object context) {
  88. if (providedValue == null) {
  89. return GetDefaultValue(destinationType);
  90. }
  91. var providedType = providedValue.GetType();
  92. if (destinationType.IsAssignableFrom(providedType)) {
  93. return providedValue;
  94. }
  95. if (CustomConverters.ContainsKey(destinationType)) {
  96. return CustomConverters[destinationType](providedValue, context);
  97. }
  98. try {
  99. #if !WinRT && !XFORMS
  100. var converter = TypeDescriptor.GetConverter(destinationType);
  101. if (converter.CanConvertFrom(providedType)) {
  102. return converter.ConvertFrom(providedValue);
  103. }
  104. converter = TypeDescriptor.GetConverter(providedType);
  105. if (converter.CanConvertTo(destinationType)) {
  106. return converter.ConvertTo(providedValue, destinationType);
  107. }
  108. #endif
  109. #if WinRT || XFORMS
  110. if (destinationType.GetTypeInfo().IsEnum) {
  111. #else
  112. if (destinationType.IsEnum) {
  113. #endif
  114. var stringValue = providedValue as string;
  115. if (stringValue != null) {
  116. return Enum.Parse(destinationType, stringValue, true);
  117. }
  118. return Enum.ToObject(destinationType, providedValue);
  119. }
  120. if (typeof (Guid).IsAssignableFrom(destinationType)) {
  121. var stringValue = providedValue as string;
  122. if (stringValue != null) {
  123. return new Guid(stringValue);
  124. }
  125. }
  126. }
  127. catch {
  128. return GetDefaultValue(destinationType);
  129. }
  130. try {
  131. return Convert.ChangeType(providedValue, destinationType, CultureInfo.CurrentCulture);
  132. }
  133. catch {
  134. return GetDefaultValue(destinationType);
  135. }
  136. }
  137. /// <summary>
  138. /// Gets the default value for a type.
  139. /// </summary>
  140. /// <param name="type">The type.</param>
  141. /// <returns>The default value.</returns>
  142. public static object GetDefaultValue(Type type) {
  143. #if WinRT || XFORMS
  144. var typeInfo = type.GetTypeInfo();
  145. return typeInfo.IsClass || typeInfo.IsInterface ? null : System.Activator.CreateInstance(type);
  146. #else
  147. return type.IsClass || type.IsInterface ? null : Activator.CreateInstance(type);
  148. #endif
  149. }
  150. }
  151. }