From d2e7ef17c66b9a6b46830cdf9ff2fe27693dfb9f Mon Sep 17 00:00:00 2001 From: Nick Randolph Date: Tue, 13 Oct 2020 10:02:41 +1100 Subject: [PATCH] Auto registering view models --- .../EventToNavigationMessageGenerator.cs | 79 ++++++++++++++++--- BuildIt.Navigation/EventMessageAttribute.cs | 19 +++-- .../MvvmApplicationServices.cs | 33 ++------ .../ViewModels/FourthViewModel.cs | 4 +- .../ViewModels/MainViewModel.cs | 1 + .../ViewModels/SecondViewModel.cs | 1 + .../ViewModels/ThirdViewModel.cs | 1 + 7 files changed, 94 insertions(+), 44 deletions(-) diff --git a/BuildIt.Navigation.Generator/EventToNavigationMessageGenerator.cs b/BuildIt.Navigation.Generator/EventToNavigationMessageGenerator.cs index 944b113..d299aa8 100644 --- a/BuildIt.Navigation.Generator/EventToNavigationMessageGenerator.cs +++ b/BuildIt.Navigation.Generator/EventToNavigationMessageGenerator.cs @@ -22,7 +22,7 @@ public void Execute(GeneratorExecutionContext context) // the generator infrastructure will create a receiver and populate it // we can retrieve the populated instance via the context var syntaxReceiver = context.SyntaxReceiver as SyntaxReceiver; - if (string.IsNullOrWhiteSpace( syntaxReceiver?.AppServiceTypeName)) + if (string.IsNullOrWhiteSpace(syntaxReceiver?.AppServiceTypeName)) { return; } @@ -119,9 +119,25 @@ namespace {syntaxReceiver.AppServiceNamespace} {{ public partial class {syntaxReceiver.AppServiceTypeName} {{ - - partial void {syntaxReceiver.RegistrationPartialMethodName}(NavigationEvents events) + partial void {syntaxReceiver.RegistrationServicesPartialMethodName}(IServiceCollection serviceRegistrations) + {{ +"); + + foreach (var reg in syntaxReceiver.AutoRegistrations) + { + sourceBuilder.AppendLine( +$@" + serviceRegistrations.AddTransient<{reg.ViewModelTypeName}>(); +"); + } + + // finish creating the source to inject + sourceBuilder.Append($@" + }} + + + partial void {syntaxReceiver.RegistrationPartialMethodName}(NavigationEvents events) {{ "); @@ -156,7 +172,7 @@ public partial class {syntaxReceiver.AppServiceTypeName} // inject the created source into the users compilation context.AddSource("MvvmApplicationServices.generated.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8)); } - catch(Exception ex) + catch (Exception ex) { } @@ -180,12 +196,17 @@ private class SyntaxReceiver : ISyntaxReceiver public string AppServiceNamespace { get; set; }// => "MvvmNavigation"; public string RegistrationPartialMethodName { get; set; }// = "RegisterEvents"; + public string RegistrationServicesPartialMethodName { get; set; }// = "RegisterEvents"; - public string Namespaces => string.Join(Environment.NewLine, (from reg in Registrations - where reg.ParameterNamespace!=null - select reg.ParameterNamespace) + public string Namespaces => string.Join(Environment.NewLine, + (from reg in Registrations + where reg.ParameterNamespace != null + select reg.ParameterNamespace) .Union(from reg in Registrations - where reg.ViewModelNamespace!=null + where reg.ViewModelNamespace != null + select reg.ViewModelNamespace) + .Union(from reg in AutoRegistrations + where reg.ViewModelNamespace != null select reg.ViewModelNamespace) .Distinct() .OrderBy(x => x) @@ -194,6 +215,8 @@ private class SyntaxReceiver : ISyntaxReceiver public IList Registrations { get; } = new List(); + public IList AutoRegistrations { get; } = new List(); + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { try @@ -203,6 +226,7 @@ public void OnVisitSyntaxNode(SyntaxNode syntaxNode) if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax) { var classAttribute = classDeclarationSyntax.AttributeLists.FirstOrDefault()?.Attributes.FirstOrDefault(attrib => (attrib.Name as IdentifierNameSyntax)?.Identifier.Text + "Attribute" == typeof(ApplicationServiceAttribute).Name); + var regAttribute = classDeclarationSyntax.AttributeLists.FirstOrDefault()?.Attributes.FirstOrDefault(attrib => (attrib.Name as IdentifierNameSyntax)?.Identifier.Text + "Attribute" == typeof(RegisterAttribute).Name); if (classAttribute != null) { AppServiceTypeName = classDeclarationSyntax.Identifier.ValueText; @@ -222,6 +246,26 @@ public void OnVisitSyntaxNode(SyntaxNode syntaxNode) .FirstOrDefault() as IdentifierNameSyntax; RegistrationPartialMethodName = identifier.Identifier.ValueText; + var secondArgument = classAttribute.ArgumentList.Arguments.Skip(1).FirstOrDefault(); + identifier = secondArgument.Expression + .DescendantNodes() + .Where(x => x.Kind() == SyntaxKind.IdentifierName) + .Skip(1) + .FirstOrDefault() as IdentifierNameSyntax; + RegistrationServicesPartialMethodName = identifier.Identifier.ValueText; + + + } + else if (regAttribute != null) + { + var namespaceSyntax = classDeclarationSyntax.Parent as NamespaceDeclarationSyntax; + AutoRegistrations.Add(new ViewModelRegistration + { + ViewModelTypeName = classDeclarationSyntax.Identifier.ValueText, + ViewModelNamespace = namespaceSyntax.Name.ToString() + + }); + } } @@ -231,9 +275,10 @@ public void OnVisitSyntaxNode(SyntaxNode syntaxNode) if (messageAttribute != null) { var eventName = eventDeclarationSyntax.Declaration.Variables.FirstOrDefault().Identifier.ValueText; - Registrations.Add(new EventRegistration() { + Registrations.Add(new EventRegistration() + { EventSyntax = eventDeclarationSyntax, - EventName = eventName + EventName = eventName }); //SemanticModel model = compilation.GetSemanticModel(syntaxTree); //ISymbol symbol = model.GetSymbolInfo(syntaxNode).Symbol; @@ -250,17 +295,26 @@ public void OnVisitSyntaxNode(SyntaxNode syntaxNode) } } } - catch(Exception ex) + catch (Exception ex) { } } + + + } - private class EventRegistration + + private class ViewModelRegistration { public string ViewModelTypeName { get; set; } public string ViewModelNamespace { get; set; } + + } + + private class EventRegistration : ViewModelRegistration + { public string MessageTypeName { get; set; } //public string MessageNamespace { get; set; } public string EventName { get; set; } @@ -269,6 +323,7 @@ private class EventRegistration public string ParameterValueAsString { get; set; } public EventFieldDeclarationSyntax EventSyntax { get; set; } + } } } diff --git a/BuildIt.Navigation/EventMessageAttribute.cs b/BuildIt.Navigation/EventMessageAttribute.cs index 2bbd709..bafc700 100644 --- a/BuildIt.Navigation/EventMessageAttribute.cs +++ b/BuildIt.Navigation/EventMessageAttribute.cs @@ -5,13 +5,13 @@ namespace BuildIt.Navigation { [AttributeUsage(AttributeTargets.Event)] - public class EventMessageAttribute:Attribute + public class EventMessageAttribute : Attribute { - public Type MessageType { get; } + public Type MessageType { get; } public Object MessageParameter { get; set; } - public EventMessageAttribute(Type messageType, object parameter=null) + public EventMessageAttribute(Type messageType, object parameter = null) { MessageType = messageType; MessageParameter = parameter; @@ -24,7 +24,7 @@ public class ViewModelAttribute : Attribute public Type ViewModelType { get; } public string InitMethodName { get; } - public ViewModelAttribute(Type viewModelType, string initMethod=null) + public ViewModelAttribute(Type viewModelType, string initMethod = null) { ViewModelType = viewModelType; InitMethodName = initMethod; @@ -34,11 +34,13 @@ public ViewModelAttribute(Type viewModelType, string initMethod=null) [AttributeUsage(AttributeTargets.Class)] public class ApplicationServiceAttribute : Attribute { - public string RegistrationMethod { get; } + public string RegistrationMethod { get; } + public string RegistrationServicesMethod { get; } - public ApplicationServiceAttribute(string registrationMethod) + public ApplicationServiceAttribute(string registrationMethod, string serviceRegistrationMethod) { RegistrationMethod = registrationMethod; + RegistrationServicesMethod = serviceRegistrationMethod; } } @@ -52,4 +54,9 @@ public ViewModelMappingRegisterAttribute(string registrationMethod) RegistrationMappingMethod = registrationMethod; } } + + [AttributeUsage(AttributeTargets.Class)] + public class RegisterAttribute : Attribute + { + } } diff --git a/MvvmNavigation.Core/MvvmApplicationServices.cs b/MvvmNavigation.Core/MvvmApplicationServices.cs index 7136055..0970b54 100644 --- a/MvvmNavigation.Core/MvvmApplicationServices.cs +++ b/MvvmNavigation.Core/MvvmApplicationServices.cs @@ -10,9 +10,10 @@ namespace MvvmNavigation - [ApplicationService(nameof(RegisterEvents))] + [ApplicationService(nameof(RegisterEvents), nameof(RegisterServices))] public partial class MvvmApplicationServices:ApplicationService { + partial void RegisterServices(IServiceCollection serviceRegistrations); partial void RegisterEvents(NavigationEvents events); @@ -20,30 +21,10 @@ protected override void InitializeApplicationServices(IServiceCollection service { base.InitializeApplicationServices(serviceRegistrations); - // HelloWorldGenerated.HelloWorld.SayHello(); - serviceRegistrations.AddSingleton(sp => { var events = new NavigationEvents(); - - // Explicit creation of navigation message - //.Register - // ((v, a) => v.ViewModelDone += a, (v, a) => v.ViewModelDone -= a, (nav) => (s, ev) => nav(s.Complete(CompletionStates.One))); - - // Implicit creation of completed navigation message - //.RegisterMessageWithParameter, CompletionStates> - // ((v, a) => v.ViewModelAlsoDone += a, (v, a) => v.ViewModelAlsoDone -= a, CompletionStates.Two); - - //// Implicit creation of close navigation message - //.RegisterMessage - // ((v, a) => v.ViewModelDone += a, (v, a) => v.ViewModelDone -= a) - - //// Explicit creation of close navigation message - //.Register - // ((v, a) => v.ViewModelDone += a, (v, a) => v.ViewModelDone -= a, (nav) => (s, ev) => nav(s.Close())); - RegisterEvents(events); - return events; }); @@ -75,10 +56,12 @@ protected override void InitializeApplicationServices(IServiceCollection service return routes; }); - serviceRegistrations.AddTransient(); - serviceRegistrations.AddTransient(); - serviceRegistrations.AddTransient(); - serviceRegistrations.AddTransient(); + //serviceRegistrations.AddTransient(); + //serviceRegistrations.AddTransient(); + //serviceRegistrations.AddTransient(); + //serviceRegistrations.AddTransient(); + + RegisterServices(serviceRegistrations); } } diff --git a/MvvmNavigation.Core/ViewModels/FourthViewModel.cs b/MvvmNavigation.Core/ViewModels/FourthViewModel.cs index c937ca3..44cd778 100644 --- a/MvvmNavigation.Core/ViewModels/FourthViewModel.cs +++ b/MvvmNavigation.Core/ViewModels/FourthViewModel.cs @@ -1,7 +1,9 @@ -using Microsoft.Toolkit.Mvvm.ComponentModel; +using BuildIt.Navigation; +using Microsoft.Toolkit.Mvvm.ComponentModel; namespace MvvmNavigation.ViewModels { + [Register] public class FourthViewModel : ObservableObject { public string Title { get; } = "Fourth Page - VM"; diff --git a/MvvmNavigation.Core/ViewModels/MainViewModel.cs b/MvvmNavigation.Core/ViewModels/MainViewModel.cs index c836eac..1de3de4 100644 --- a/MvvmNavigation.Core/ViewModels/MainViewModel.cs +++ b/MvvmNavigation.Core/ViewModels/MainViewModel.cs @@ -7,6 +7,7 @@ namespace MvvmNavigation.ViewModels { + [Register] public class MainViewModel: ObservableObject { diff --git a/MvvmNavigation.Core/ViewModels/SecondViewModel.cs b/MvvmNavigation.Core/ViewModels/SecondViewModel.cs index 887e0c8..6066e37 100644 --- a/MvvmNavigation.Core/ViewModels/SecondViewModel.cs +++ b/MvvmNavigation.Core/ViewModels/SecondViewModel.cs @@ -5,6 +5,7 @@ namespace MvvmNavigation.ViewModels { + [Register] public class SecondViewModel : ObservableObject { private int count; diff --git a/MvvmNavigation.Core/ViewModels/ThirdViewModel.cs b/MvvmNavigation.Core/ViewModels/ThirdViewModel.cs index 207336d..307bbeb 100644 --- a/MvvmNavigation.Core/ViewModels/ThirdViewModel.cs +++ b/MvvmNavigation.Core/ViewModels/ThirdViewModel.cs @@ -7,6 +7,7 @@ namespace MvvmNavigation.ViewModels { + [Register] public class ThirdViewModel: ObservableObject { [EventMessage(typeof(CloseMessage))]