From dc79350e1b570cba0a199219e85cc5e26b74f077 Mon Sep 17 00:00:00 2001 From: David Vreony Date: Fri, 7 Jun 2024 23:33:23 +0100 Subject: [PATCH 1/2] add support for input, output args on commands --- src/Vetuviem.Core/CommandBinding.cs | 61 +++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/Vetuviem.Core/CommandBinding.cs b/src/Vetuviem.Core/CommandBinding.cs index 5b30a0e..e3c2d9d 100644 --- a/src/Vetuviem.Core/CommandBinding.cs +++ b/src/Vetuviem.Core/CommandBinding.cs @@ -15,12 +15,9 @@ namespace Vetuviem.Core /// Represents a command binding between a control and a viewmodel. /// /// The type for the viewmodel. - public sealed class CommandBinding : ICommandBinding + public class CommandBinding : CommandBinding where TViewModel : class { - private readonly Expression?>> _viewModelBinding; - private readonly string? _toEvent; - /// /// Initializes a new instance of the class. /// @@ -29,6 +26,62 @@ public sealed class CommandBinding : ICommandBinding?>> viewModelBinding, string? toEvent = null) + : base( + viewModelBinding, + toEvent) + { + } + } + + /// + /// Represents a command binding between a control and a viewmodel. + /// + /// The type for the viewmodel. + /// + /// The type of the values that are the result of command execution. + /// + public class CommandBinding : CommandBinding + where TViewModel : class + { + /// + /// Initializes a new instance of the class. + /// + /// Expression for the View Model binding. + /// If specified, bind to the specific event instead of the default. + public CommandBinding( + Expression?>> viewModelBinding, + string? toEvent = null) + : base( + viewModelBinding, + toEvent) + { + } + } + + /// + /// Represents a command binding between a control and a viewmodel. + /// + /// The type for the viewmodel. + /// + /// The type of parameter values passed in during command execution. + /// + /// + /// The type of the values that are the result of command execution. + /// + public class CommandBinding : ICommandBinding + where TViewModel : class + { + private readonly Expression?>> _viewModelBinding; + private readonly string? _toEvent; + + /// + /// Initializes a new instance of the class. + /// + /// Expression for the View Model binding. + /// If specified, bind to the specific event instead of the default. + public CommandBinding( + Expression?>> viewModelBinding, + string? toEvent = null) { _viewModelBinding = viewModelBinding; _toEvent = toEvent; From b97cf7116df8112ada4c961fab44e755a81dd5e9 Mon Sep 17 00:00:00 2001 From: David Vreony Date: Sat, 8 Jun 2024 20:48:02 +0100 Subject: [PATCH 2/2] prep for command binding fix --- .../ViewModels/QuestionnaireViewModel.cs | 2 +- .../Views/QuestionnaireView.xaml.cs | 3 + src/Vetuviem.Core/CommandBinding.cs | 62 ++----------------- .../ControlBindingModelPropertyGenerator.cs | 2 + ...enericControlBindingModelClassGenerator.cs | 44 ++++++++----- 5 files changed, 38 insertions(+), 75 deletions(-) diff --git a/src/ReactiveUI.WPF.SampleApp/ViewModels/QuestionnaireViewModel.cs b/src/ReactiveUI.WPF.SampleApp/ViewModels/QuestionnaireViewModel.cs index c424952..3c3684c 100644 --- a/src/ReactiveUI.WPF.SampleApp/ViewModels/QuestionnaireViewModel.cs +++ b/src/ReactiveUI.WPF.SampleApp/ViewModels/QuestionnaireViewModel.cs @@ -71,7 +71,7 @@ public QuestionnaireViewModel() vm => vm.AnswerFive, vm => vm.AnswerFiveLengthRemaining); - LaunchInteraction = ReactiveCommand.CreateFromTask(() => OnLaunchInteraction()); + LaunchInteraction = ReactiveCommand.CreateFromTask(OnLaunchInteraction); } /// diff --git a/src/ReactiveUI.WPF.SampleApp/Views/QuestionnaireView.xaml.cs b/src/ReactiveUI.WPF.SampleApp/Views/QuestionnaireView.xaml.cs index 0650aec..81dfe8c 100644 --- a/src/ReactiveUI.WPF.SampleApp/Views/QuestionnaireView.xaml.cs +++ b/src/ReactiveUI.WPF.SampleApp/Views/QuestionnaireView.xaml.cs @@ -25,6 +25,9 @@ public QuestionnaireView() private void OnWhenActivated(Action disposeWithAction) { +#if TBC + disposeWithAction(this.BindCommand(ViewModel, vw => vw.LaunchInteraction, vm => vm.LaunchInteraction)); +#endif new QuestionnaireViewBindingModels().ApplyBindings( disposeWithAction, this, diff --git a/src/Vetuviem.Core/CommandBinding.cs b/src/Vetuviem.Core/CommandBinding.cs index e3c2d9d..76ab45a 100644 --- a/src/Vetuviem.Core/CommandBinding.cs +++ b/src/Vetuviem.Core/CommandBinding.cs @@ -4,7 +4,6 @@ using System; using System.Linq.Expressions; -using System.Reactive; using System.Reactive.Disposables; using System.Windows.Input; using ReactiveUI; @@ -15,72 +14,19 @@ namespace Vetuviem.Core /// Represents a command binding between a control and a viewmodel. /// /// The type for the viewmodel. - public class CommandBinding : CommandBinding + public class CommandBinding : ICommandBinding where TViewModel : class { - /// - /// Initializes a new instance of the class. - /// - /// Expression for the View Model binding. - /// If specified, bind to the specific event instead of the default. - public CommandBinding( - Expression?>> viewModelBinding, - string? toEvent = null) - : base( - viewModelBinding, - toEvent) - { - } - } - - /// - /// Represents a command binding between a control and a viewmodel. - /// - /// The type for the viewmodel. - /// - /// The type of the values that are the result of command execution. - /// - public class CommandBinding : CommandBinding - where TViewModel : class - { - /// - /// Initializes a new instance of the class. - /// - /// Expression for the View Model binding. - /// If specified, bind to the specific event instead of the default. - public CommandBinding( - Expression?>> viewModelBinding, - string? toEvent = null) - : base( - viewModelBinding, - toEvent) - { - } - } - - /// - /// Represents a command binding between a control and a viewmodel. - /// - /// The type for the viewmodel. - /// - /// The type of parameter values passed in during command execution. - /// - /// - /// The type of the values that are the result of command execution. - /// - public class CommandBinding : ICommandBinding - where TViewModel : class - { - private readonly Expression?>> _viewModelBinding; + private readonly Expression> _viewModelBinding; private readonly string? _toEvent; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Expression for the View Model binding. /// If specified, bind to the specific event instead of the default. public CommandBinding( - Expression?>> viewModelBinding, + Expression> viewModelBinding, string? toEvent = null) { _viewModelBinding = viewModelBinding; diff --git a/src/Vetuviem.SourceGenerator/Features/ControlBindingModels/ControlBindingModelPropertyGenerator.cs b/src/Vetuviem.SourceGenerator/Features/ControlBindingModels/ControlBindingModelPropertyGenerator.cs index 15bd824..40fc657 100644 --- a/src/Vetuviem.SourceGenerator/Features/ControlBindingModels/ControlBindingModelPropertyGenerator.cs +++ b/src/Vetuviem.SourceGenerator/Features/ControlBindingModels/ControlBindingModelPropertyGenerator.cs @@ -156,6 +156,8 @@ private static TypeSyntax GetBindingTypeSyntax( prop, desiredCommandInterface); +#error we need to change command to handle the control type instead of the property type. This is because BindCommand binds to the control, not the property. + var returnType = prop.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); var type = SyntaxFactory.ParseTypeName($"global::Vetuviem.Core.{bindingName}?"); return type; diff --git a/src/Vetuviem.SourceGenerator/Features/ControlBindingModels/GenericControlBindingModelClassGenerator.cs b/src/Vetuviem.SourceGenerator/Features/ControlBindingModels/GenericControlBindingModelClassGenerator.cs index 89d2167..b7f35d3 100644 --- a/src/Vetuviem.SourceGenerator/Features/ControlBindingModels/GenericControlBindingModelClassGenerator.cs +++ b/src/Vetuviem.SourceGenerator/Features/ControlBindingModels/GenericControlBindingModelClassGenerator.cs @@ -388,14 +388,23 @@ private static StatementSyntax[] GetApplyBindingMethodBody( continue; } - var propType = propertySymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + var propType = propertySymbol.Type; + var propTypeDisplayString = propType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + + var isCommand = !string.IsNullOrWhiteSpace(desiredCommandInterface) && + (propType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Equals(desiredCommandInterface, StringComparison.Ordinal) + || propType.AllInterfaces.Any(interfaceName => interfaceName.GetFullName().Equals(desiredCommandInterface, StringComparison.Ordinal))); + + var expressionArg = isCommand + ? propertySymbol.Name + : $"global::Vetuviem.Core.ExpressionHelpers.GetControlPropertyExpressionFromViewExpression(VetuviemControlBindingExpression, \"{propertySymbol.Name}\")"; var invokeArgs = new[] { "registerForDisposalAction", "view", "viewModel", - $"global::Vetuviem.Core.ExpressionHelpers.GetControlPropertyExpressionFromViewExpression(VetuviemControlBindingExpression, \"{propertySymbol.Name}\")", + expressionArg, }; var invocationStatement = RoslynGenerationHelpers.GetMethodOnPropertyOfVariableInvocationSyntax( @@ -406,7 +415,7 @@ private static StatementSyntax[] GetApplyBindingMethodBody( AddInvocationStatementToRelevantCollection( propertySymbol, - desiredCommandInterface, + isCommand, invocationStatement, commandBindingStatements, oneWayBindingStatements, @@ -471,14 +480,23 @@ private static StatementSyntax[] GetApplyBindingCompositeDisposableMethodBody(IN continue; } - var propType = propertySymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + var propType = propertySymbol.Type; + var propTypeDisplayString = propType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + + var isCommand = !string.IsNullOrWhiteSpace(desiredCommandInterface) && + (propType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Equals(desiredCommandInterface, StringComparison.Ordinal) + || propType.AllInterfaces.Any(interfaceName => interfaceName.GetFullName().Equals(desiredCommandInterface, StringComparison.Ordinal))); + + var expressionArg = isCommand + ? propertySymbol.Name + : $"global::Vetuviem.Core.ExpressionHelpers.GetControlPropertyExpressionFromViewExpression(VetuviemControlBindingExpression, \"{propertySymbol.Name}\")"; var invokeArgs = new[] { "compositeDisposable", "view", "viewModel", - $"global::Vetuviem.Core.ExpressionHelpers.GetControlPropertyExpressionFromViewExpression(VetuviemControlBindingExpression, \"{propertySymbol.Name}\")", + expressionArg, }; var invocationStatement = RoslynGenerationHelpers.GetMethodOnPropertyOfVariableInvocationSyntax( @@ -489,7 +507,7 @@ private static StatementSyntax[] GetApplyBindingCompositeDisposableMethodBody(IN AddInvocationStatementToRelevantCollection( propertySymbol, - desiredCommandInterface, + isCommand, invocationStatement, commandBindingStatements, oneWayBindingStatements, @@ -505,22 +523,16 @@ private static StatementSyntax[] GetApplyBindingCompositeDisposableMethodBody(IN private static void AddInvocationStatementToRelevantCollection( IPropertySymbol prop, - string? desiredCommandInterface, + bool isCommand, StatementSyntax invocation, ICollection commandBindingStatements, ICollection oneWayBindingStatements, ICollection twoWayBindingStatements) { - if (!string.IsNullOrWhiteSpace(desiredCommandInterface)) + if (isCommand) { - var propType = prop.Type; - var isCommand = propType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Equals(desiredCommandInterface, StringComparison.Ordinal) - || propType.AllInterfaces.Any(interfaceName => interfaceName.GetFullName().Equals(desiredCommandInterface, StringComparison.Ordinal)); - if (isCommand) - { - commandBindingStatements.Add(invocation); - return; - } + commandBindingStatements.Add(invocation); + return; } if (prop.IsReadOnly)