diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 18402f72..e61f2de0 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -82,5 +82,5 @@ jobs: run: msbuild ${{env.caliburn_sln}} /t:package /p:Configuration=${{env.build_configuration}} - name: publish Nuget Packages to GitHub - run: dotnet nuget push ${{env.nuget_folder}}\**\*.nupkg --source ${{env.package_feed}} --api-key ${{secrets.PUBLISH_NUGET_PACKAGE}} --skip-duplicate + run: dotnet nuget push ${{env.nuget_folder}}\*.nupkg --source ${{env.package_feed}} --api-key ${{secrets.PUBLISH_NUGET_PACKAGE}} --skip-duplicate if: github.event_name != 'pull_request' diff --git a/src/Caliburn.Micro.Avalonia.Tests/MessageBinderTests.cs b/src/Caliburn.Micro.Avalonia.Tests/MessageBinderTests.cs index 30a57c6a..b955f2fe 100644 --- a/src/Caliburn.Micro.Avalonia.Tests/MessageBinderTests.cs +++ b/src/Caliburn.Micro.Avalonia.Tests/MessageBinderTests.cs @@ -5,7 +5,7 @@ public class MessageBinderTests [Fact] public void EvaluateParameterCaseInsensitive() { - MessageBinder.SpecialValues.Add("$sampleParameter", context => 42); + MessageBinder.SpecialValues.TryAdd("$sampleParameter", context => 42); var caseSensitiveValue = MessageBinder.EvaluateParameter("$sampleParameter", typeof(int), new ActionExecutionContext()); Assert.NotEqual("$sampleParameter", caseSensitiveValue); diff --git a/src/Caliburn.Micro.Platform.Tests/MessageBinderTests.cs b/src/Caliburn.Micro.Platform.Tests/MessageBinderTests.cs index 2337e473..029768d7 100644 --- a/src/Caliburn.Micro.Platform.Tests/MessageBinderTests.cs +++ b/src/Caliburn.Micro.Platform.Tests/MessageBinderTests.cs @@ -7,7 +7,7 @@ public class MessageBinderTests [Fact] public void EvaluateParameterCaseInsensitive() { - MessageBinder.SpecialValues.Add("$sampleParameter", context => 42); + MessageBinder.SpecialValues.TryAdd("$sampleParameter", context => 42); var caseSensitiveValue = MessageBinder.EvaluateParameter("$sampleParameter", typeof(int), new ActionExecutionContext()); Assert.NotEqual("$sampleParameter", caseSensitiveValue); diff --git a/src/Caliburn.Micro.Platform/ActionMessage.cs b/src/Caliburn.Micro.Platform/ActionMessage.cs index e0d1afe4..6468aa30 100644 --- a/src/Caliburn.Micro.Platform/ActionMessage.cs +++ b/src/Caliburn.Micro.Platform/ActionMessage.cs @@ -208,7 +208,7 @@ protected override void OnAttached() string eventName = "Loaded"; var trigger = Interaction.GetBehaviors(AssociatedObject) .OfType() - .FirstOrDefault(t => t.Actions.Contains(this)) as EventTrigger; + .FirstOrDefault(t => t.Actions.Contains(this)); Log.Info($"Trigger is null {trigger == null}"); #else string eventName = "Loaded"; diff --git a/src/Caliburn.Micro.Platform/MessageBinder.cs b/src/Caliburn.Micro.Platform/MessageBinder.cs index 4ecbb6f9..c79e2a0f 100644 --- a/src/Caliburn.Micro.Platform/MessageBinder.cs +++ b/src/Caliburn.Micro.Platform/MessageBinder.cs @@ -7,11 +7,12 @@ namespace Caliburn.Micro #endif { using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.ComponentModel; using System.Globalization; using System.Linq; using System.Reflection; - using System.Collections.Generic; - using System.ComponentModel; #if WINDOWS_UWP using Windows.UI.Xaml.Controls; #endif @@ -19,27 +20,29 @@ namespace Caliburn.Micro /// /// A service that is capable of properly binding values to a method's parameters and creating instances of . /// - public static class MessageBinder { + 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) + + public static readonly ConcurrentDictionary> SpecialValues = + new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase) { - {"$eventargs", c => c.EventArgs}, + ["$eventargs"] = c => c.EventArgs, #if XFORMS || MAUI - {"$datacontext", c => c.Source.BindingContext}, - {"$bindingcontext", c => c.Source.BindingContext}, + ["$datacontext"] = c => c.Source.BindingContext, + ["$bindingcontext"] = c => c.Source.BindingContext, #else - {"$datacontext", c => c.Source.DataContext}, + ["$datacontext"] = c => c.Source.DataContext, #endif #if WINDOWS_UWP - {"$clickeditem", c => ((ItemClickEventArgs)c.EventArgs).ClickedItem}, + ["$clickeditem"] = c => ((ItemClickEventArgs)c.EventArgs).ClickedItem, #endif - {"$source", c => c.Source}, - {"$executioncontext", c => c}, - {"$view", c => c.View} + ["$source"] = c => c.Source, + ["$executioncontext"] = c => c, + ["$view"] = c => c.View }; /// @@ -64,11 +67,13 @@ public static class MessageBinder { /// The action execution context. /// The parameters required to complete the invocation. /// The actual parameter values. - public static object[] DetermineParameters(ActionExecutionContext context, ParameterInfo[] requiredParameters) { + 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++) { + for (int i = 0; i < requiredParameters.Length; i++) + { var parameterType = requiredParameters[i].ParameterType; var parameterValue = providedValues[i]; var parameterAsString = parameterValue as string; @@ -76,7 +81,8 @@ public static object[] DetermineParameters(ActionExecutionContext context, Param if (parameterAsString != null) finalValues[i] = CoerceValue(parameterType, EvaluateParameter(parameterAsString, parameterType, context), context); - else finalValues[i] = CoerceValue(parameterType, parameterValue, context); + else + finalValues[i] = CoerceValue(parameterType, parameterValue, context); } return finalValues; @@ -86,7 +92,8 @@ public static object[] DetermineParameters(ActionExecutionContext context, Param /// Transforms the textual parameter into the actual parameter. /// public static Func EvaluateParameter = - (text, parameterType, context) => { + (text, parameterType, context) => + { Func resolver; return SpecialValues.TryGetValue(text, out resolver) ? resolver(context) : text; }; @@ -98,62 +105,76 @@ public static object[] DetermineParameters(ActionExecutionContext context, Param /// 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) { + public static object CoerceValue(Type destinationType, object providedValue, object context) + { + if (providedValue == null) + { return GetDefaultValue(destinationType); } var providedType = providedValue.GetType(); - if (destinationType.GetTypeInfo().IsAssignableFrom(providedType.GetTypeInfo())) { + if (destinationType.GetTypeInfo().IsAssignableFrom(providedType.GetTypeInfo())) + { return providedValue; } - if (CustomConverters.ContainsKey(destinationType)) { + if (CustomConverters.ContainsKey(destinationType)) + { return CustomConverters[destinationType](providedValue, context); } - try { + try + { #if !WINDOWS_UWP && !XFORMS && !MAUI var converter = TypeDescriptor.GetConverter(destinationType); - if (converter.CanConvertFrom(providedType)) { + if (converter.CanConvertFrom(providedType)) + { return converter.ConvertFrom(providedValue); } converter = TypeDescriptor.GetConverter(providedType); - if (converter.CanConvertTo(destinationType)) { + if (converter.CanConvertTo(destinationType)) + { return converter.ConvertTo(providedValue, destinationType); } #endif #if WINDOWS_UWP || XFORMS || MAUI if (destinationType.GetTypeInfo().IsEnum) { #else - if (destinationType.IsEnum) { + if (destinationType.IsEnum) + { #endif var stringValue = providedValue as string; - if (stringValue != null) { + if (stringValue != null) + { return Enum.Parse(destinationType, stringValue, true); } return Enum.ToObject(destinationType, providedValue); } - if (typeof (Guid).GetTypeInfo().IsAssignableFrom(destinationType.GetTypeInfo())) { + if (typeof(Guid).GetTypeInfo().IsAssignableFrom(destinationType.GetTypeInfo())) + { var stringValue = providedValue as string; - if (stringValue != null) { + if (stringValue != null) + { return new Guid(stringValue); } } } - catch { + catch + { return GetDefaultValue(destinationType); } - try { + try + { return Convert.ChangeType(providedValue, destinationType, CultureInfo.CurrentCulture); } - catch { + catch + { return GetDefaultValue(destinationType); } } @@ -163,7 +184,8 @@ public static object CoerceValue(Type destinationType, object providedValue, obj /// /// The type. /// The default value. - public static object GetDefaultValue(Type type) { + public static object GetDefaultValue(Type type) + { #if WINDOWS_UWP || XFORMS || MAUI var typeInfo = type.GetTypeInfo(); return typeInfo.IsClass || typeInfo.IsInterface ? null : System.Activator.CreateInstance(type); diff --git a/src/Caliburn.Micro.Platform/ViewLocator.cs b/src/Caliburn.Micro.Platform/ViewLocator.cs index b04564a4..e5deb0ba 100644 --- a/src/Caliburn.Micro.Platform/ViewLocator.cs +++ b/src/Caliburn.Micro.Platform/ViewLocator.cs @@ -174,7 +174,7 @@ public static void AddTypeMapping(string nsSourceReplaceRegEx, string nsSourceFi foreach (var t in nsTargetsRegEx) { - replist.Add(t + String.Format(nameFormat, basegrp, repsuffix)); + replist.Add(t + GetNameFormat(basegrp, repsuffix)); } var rxbase = RegExHelper.GetNameCaptureGroup("basename"); @@ -188,18 +188,29 @@ public static void AddTypeMapping(string nsSourceReplaceRegEx, string nsSourceFi } } - var rxsrcfilter = String.IsNullOrEmpty(nsSourceFilterRegEx) + var rxsrcfilter = string.IsNullOrEmpty(nsSourceFilterRegEx) ? null - : String.Concat(nsSourceFilterRegEx, String.Format(nameFormat, RegExHelper.NameRegEx, suffix), "$"); + : string.Concat(nsSourceFilterRegEx, GetNameFormat(RegExHelper.NameRegEx, suffix), "$"); var rxsuffix = RegExHelper.GetCaptureGroup("suffix", suffix); NameTransformer.AddRule( - String.Concat(nsSourceReplaceRegEx, String.Format(nameFormat, rxbase, rxsuffix), "$"), + String.Concat(nsSourceReplaceRegEx, GetNameFormat(rxbase, rxsuffix), "$"), replist.ToArray(), rxsrcfilter ); } + private static string GetNameFormat(string firstParameter, string suffix) + { + string result = IsNameFormatValidFormat() ? string.Format(nameFormat, firstParameter, suffix) : nameFormat; + return result; + } + + internal static bool IsNameFormatValidFormat() + { + return nameFormat.Contains("{0}") && nameFormat.Contains("{1}"); + } + /// /// Adds a standard type mapping based on namespace RegEx replace and filter patterns ///