diff --git a/src/Caliburn.Micro.Platform/ViewModelBinder.cs b/src/Caliburn.Micro.Platform/ViewModelBinder.cs index 49032030..84673684 100644 --- a/src/Caliburn.Micro.Platform/ViewModelBinder.cs +++ b/src/Caliburn.Micro.Platform/ViewModelBinder.cs @@ -41,7 +41,8 @@ namespace Caliburn.Micro /// /// Binds a view to a view model. /// - public static class ViewModelBinder { + public static class ViewModelBinder + { const string AsyncSuffix = "Async"; static readonly ILog Log = LogManager.GetLog(typeof(ViewModelBinder)); @@ -75,7 +76,8 @@ public static class ViewModelBinder { /// /// The view to check. /// Whether or not conventions should be applied to the view. - public static bool ShouldApplyConventions(FrameworkElement view) { + public static bool ShouldApplyConventions(FrameworkElement view) + { var overriden = View.GetApplyConventions(view); return overriden.GetValueOrDefault(ApplyConventionsByDefault); } @@ -84,30 +86,35 @@ public static bool ShouldApplyConventions(FrameworkElement view) { /// Creates data bindings on the view's controls based on the provided properties. /// /// Parameters include named Elements to search through and the type of view model to determine conventions for. Returns unmatched elements. - public static Func, Type, IEnumerable> BindProperties = (namedElements, viewModelType) => { + public static Func, Type, IEnumerable> BindProperties = (namedElements, viewModelType) => + { var unmatchedElements = new List(); #if !XFORMS && !MAUI - foreach (var element in namedElements) { + foreach (var element in namedElements) + { var cleanName = element.Name.Trim('_'); var parts = cleanName.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries); var property = viewModelType.GetPropertyCaseInsensitive(parts[0]); var interpretedViewModelType = viewModelType; - for (int i = 1; i < parts.Length && property != null; i++) { + for (int i = 1; i < parts.Length && property != null; i++) + { interpretedViewModelType = property.PropertyType; property = interpretedViewModelType.GetPropertyCaseInsensitive(parts[i]); } - if (property == null) { + if (property == null) + { unmatchedElements.Add(element); Log.Info("Binding Convention Not Applied: Element {0} did not match a property.", element.Name); continue; } var convention = ConventionManager.GetElementConvention(element.GetType()); - if (convention == null) { + if (convention == null) + { unmatchedElements.Add(element); Log.Warn("Binding Convention Not Applied: No conventions configured for {0}.", element.GetType()); continue; @@ -121,10 +128,12 @@ public static bool ShouldApplyConventions(FrameworkElement view) { convention ); - if (applied) { + if (applied) + { Log.Info("Binding Convention Applied: Element {0}.", element.Name); } - else { + else + { Log.Info("Binding Convention Not Applied: Element {0} has existing binding.", element.Name); unmatchedElements.Add(element); } @@ -138,7 +147,8 @@ public static bool ShouldApplyConventions(FrameworkElement view) { /// Attaches instances of to the view's controls based on the provided methods. /// /// Parameters include the named elements to search through and the type of view model to determine conventions for. Returns unmatched elements. - public static Func, Type, IEnumerable> BindActions = (namedElements, viewModelType) => { + public static Func, Type, IEnumerable> BindActions = (namedElements, viewModelType) => + { var unmatchedElements = namedElements.ToList(); #if !XFORMS && !MAUI #if WINDOWS_UWP || XFORMS || MAUI @@ -146,21 +156,24 @@ public static bool ShouldApplyConventions(FrameworkElement view) { #else var methods = viewModelType.GetMethods(); #endif - - foreach (var method in methods) { - Log.Info($"Searching for methods control {method.Name} unmatchedElements count {unmatchedElements.Count}"); + + foreach (var method in methods) + { + Log.Info($"Searching for methods control {method.Name} unmatchedElements count {unmatchedElements.Count}"); var foundControl = unmatchedElements.FindName(method.Name); - if (foundControl == null && IsAsyncMethod(method)) { + if (foundControl == null && IsAsyncMethod(method)) + { var methodNameWithoutAsyncSuffix = method.Name.Substring(0, method.Name.Length - AsyncSuffix.Length); foundControl = unmatchedElements.FindName(methodNameWithoutAsyncSuffix); } - if(foundControl == null) { + if (foundControl == null) + { Log.Info("Action Convention Not Applied: No actionable element for {0}. {1}", method.Name, unmatchedElements.Count); - foreach(var element in unmatchedElements) + foreach (var element in unmatchedElements) { - Log.Info($"Unnamed element {element.Name}"); + Log.Info($"Unnamed element {element.Name}"); } continue; } @@ -179,10 +192,12 @@ public static bool ShouldApplyConventions(FrameworkElement view) { var message = method.Name; var parameters = method.GetParameters(); - if (parameters.Length > 0) { + if (parameters.Length > 0) + { message += "("; - foreach (var parameter in parameters) { + foreach (var parameter in parameters) + { var paramName = parameter.Name; var specialValue = "$" + paramName.ToLower(); @@ -203,7 +218,8 @@ public static bool ShouldApplyConventions(FrameworkElement view) { return unmatchedElements; }; - static bool IsAsyncMethod(MethodInfo method) { + static bool IsAsyncMethod(MethodInfo method) + { return typeof(Task).GetTypeInfo().IsAssignableFrom(method.ReturnType.GetTypeInfo()) && method.Name.EndsWith(AsyncSuffix, StringComparison.OrdinalIgnoreCase); } @@ -217,19 +233,8 @@ static bool IsAsyncMethod(MethodInfo method) { /// Binds the specified viewModel to the view. /// ///Passes the the view model, view and creation context (or null for default) to use in applying binding. - public static Action Bind = (viewModel, view, context) => { -#if !WINDOWS_UWP && !XFORMS && !MAUI - // when using d:DesignInstance, Blend tries to assign the DesignInstanceExtension class as the DataContext, - // so here we get the actual ViewModel which is in the Instance property of DesignInstanceExtension - if (View.InDesignMode) { - var vmType = viewModel.GetType(); - if (vmType.FullName == "Microsoft.Expression.DesignModel.InstanceBuilders.DesignInstanceExtension") { - var propInfo = vmType.GetProperty("Instance", BindingFlags.Instance | BindingFlags.NonPublic); - viewModel = propInfo.GetValue(viewModel, null); - } - } -#endif - + public static Action Bind = (viewModel, view, context) => + { Log.Info("Binding {0} and {1}.", view, viewModel); #if XFORMS @@ -240,29 +245,35 @@ static bool IsAsyncMethod(MethodInfo method) { var noContext = Caliburn.Micro.Bind.NoContextProperty; #endif - if ((bool)view.GetValue(noContext)) { + if ((bool)view.GetValue(noContext)) + { Action.SetTargetWithoutContext(view, viewModel); } - else { + else + { Action.SetTarget(view, viewModel); } var viewAware = viewModel as IViewAware; - if (viewAware != null) { + if (viewAware != null) + { Log.Info("Attaching {0} to {1}.", view, viewAware); viewAware.AttachView(view, context); } - if ((bool)view.GetValue(ConventionsAppliedProperty)) { + if ((bool)view.GetValue(ConventionsAppliedProperty)) + { return; } var element = View.GetFirstNonGeneratedView(view) as FrameworkElement; - if (element == null) { + if (element == null) + { return; } - if (!ShouldApplyConventions(element)) { + if (!ShouldApplyConventions(element)) + { Log.Info("Skipping conventions for {0} and {1}.", element, viewModel); return; }