Skip to content

Commit

Permalink
Merge pull request #91 from thomasclaudiushuber/issue/75
Browse files Browse the repository at this point in the history
Support custom command implementations
  • Loading branch information
thomasclaudiushuber committed Dec 10, 2023
2 parents c7685d3 + 0502a25 commit ba24c8e
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 7 deletions.
Expand Up @@ -375,6 +375,22 @@ public void ShouldBeEqual8()
Assert.Equal(_viewModelToGenerate1, _viewModelToGenerate2);
}

[Fact]
public void ShouldNotBeEqualDifferentCommandType()
{
_viewModelToGenerate2.CommandType = "MyCommand";

Assert.NotEqual(_viewModelToGenerate1, _viewModelToGenerate2);
}

[Fact]
public void ShouldBeEqual9()
{
_viewModelToGenerate1.CommandType = "DelegateCommand";

Assert.Equal(_viewModelToGenerate1, _viewModelToGenerate2);
}

private static void FillAllProperties(ViewModelToGenerate viewModelToGenerate)
{
viewModelToGenerate.ClassAccessModifier = "public";
Expand Down
Expand Up @@ -174,5 +174,62 @@ public EmployeeViewModel()
}}
");
}

[Fact]
public void GenerateCommandPropertyWithCustomCommandType()
{
ShouldGenerateExpectedCode(
@"using MvvmGen;
namespace MyCode
{
public class MyCommand : IDelegateCommand
{
private readonly Action<object?> _execute;
private readonly Func<object?, bool>? _canExecute;
public MyCommand(Action<object?> execute, Func<object?, bool>? canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public event EventHandler? CanExecuteChanged;
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
public void Execute(object? parameter) => _execute(parameter);
public bool CanExecute(object? parameter) => _canExecute == null || _canExecute(parameter);
}
[ViewModel(CommandType=typeof(MyCommand))]
public partial class EmployeeViewModel
{
[Command]
public void SaveAll() { }
}
}",
$@"{AutoGeneratedTopContent}
namespace MyCode
{{
partial class EmployeeViewModel : global::MvvmGen.ViewModels.ViewModelBase
{{
private IDelegateCommand? _saveAllCommand;
public EmployeeViewModel()
{{
this.OnInitialize();
}}
partial void OnInitialize();
public IDelegateCommand SaveAllCommand => _saveAllCommand ??= new MyCode.MyCommand(_ => SaveAll());
}}
}}
");
}

}
}
Expand Up @@ -11,14 +11,16 @@ namespace MvvmGen.Generators
{
internal static class CommandPropertyGenerator
{
internal static void GenerateCommandProperties(this ViewModelBuilder vmBuilder, IEnumerable<CommandToGenerate>? commandsToGenerate)
internal static void GenerateCommandProperties(this ViewModelBuilder vmBuilder,
IEnumerable<CommandToGenerate>? commandsToGenerate,
string commandType)
{
if (commandsToGenerate is not null)
{
foreach (var commandToGenerate in commandsToGenerate)
{
vmBuilder.AppendLineBeforeMember();
vmBuilder.Append($"public IDelegateCommand {commandToGenerate.PropertyName} => {commandToGenerate.FieldName} ??= new DelegateCommand({GetMethodCall(commandToGenerate.ExecuteMethod)}");
vmBuilder.Append($"public IDelegateCommand {commandToGenerate.PropertyName} => {commandToGenerate.FieldName} ??= new {commandType}({GetMethodCall(commandToGenerate.ExecuteMethod)}");
if (commandToGenerate.CanExecuteMethod is not null)
{
vmBuilder.Append($", {GetMethodCall(commandToGenerate.CanExecuteMethod)}");
Expand Down
Expand Up @@ -39,5 +39,20 @@ internal static bool InspectGenerateConstructor(AttributeData viewModelAttribute

return modelPropertyName;
}

internal static string InspectCommandType(AttributeData viewModelAttributeData)
{
string commandType = "DelegateCommand";

foreach (var arg in viewModelAttributeData.NamedArguments)
{
if (arg.Key == "CommandType")
{
commandType = arg.Value.Value?.ToString();

Check warning on line 51 in src/MvvmGen.SourceGenerators/Inspectors/ViewModelAttributeInspector.cs

View workflow job for this annotation

GitHub Actions / build-mvvmgen

Converting null literal or possible null value to non-nullable type.

Check warning on line 51 in src/MvvmGen.SourceGenerators/Inspectors/ViewModelAttributeInspector.cs

View workflow job for this annotation

GitHub Actions / build-mvvmgen

Converting null literal or possible null value to non-nullable type.

Check warning on line 51 in src/MvvmGen.SourceGenerators/Inspectors/ViewModelAttributeInspector.cs

View workflow job for this annotation

GitHub Actions / build-mvvmgen-purecodegen

Converting null literal or possible null value to non-nullable type.

Check warning on line 51 in src/MvvmGen.SourceGenerators/Inspectors/ViewModelAttributeInspector.cs

View workflow job for this annotation

GitHub Actions / build-mvvmgen-purecodegen

Converting null literal or possible null value to non-nullable type.
}
}

return commandType;

Check warning on line 55 in src/MvvmGen.SourceGenerators/Inspectors/ViewModelAttributeInspector.cs

View workflow job for this annotation

GitHub Actions / build-mvvmgen

Possible null reference return.

Check warning on line 55 in src/MvvmGen.SourceGenerators/Inspectors/ViewModelAttributeInspector.cs

View workflow job for this annotation

GitHub Actions / build-mvvmgen

Possible null reference return.

Check warning on line 55 in src/MvvmGen.SourceGenerators/Inspectors/ViewModelAttributeInspector.cs

View workflow job for this annotation

GitHub Actions / build-mvvmgen-purecodegen

Possible null reference return.

Check warning on line 55 in src/MvvmGen.SourceGenerators/Inspectors/ViewModelAttributeInspector.cs

View workflow job for this annotation

GitHub Actions / build-mvvmgen-purecodegen

Possible null reference return.
}
}
}
4 changes: 3 additions & 1 deletion src/MvvmGen.SourceGenerators/Model/CommandToGenerate.cs
Expand Up @@ -15,7 +15,7 @@ public CommandToGenerate(CommandMethod executeMethod, string propertyName)
{
ExecuteMethod = executeMethod;
PropertyName = propertyName;
FieldName = $"_{PropertyName.Substring(0,1).ToLower()}{PropertyName.Substring(1)}";
FieldName = $"_{PropertyName.Substring(0, 1).ToLower()}{PropertyName.Substring(1)}";
}

public CommandMethod ExecuteMethod { get; }
Expand All @@ -26,6 +26,8 @@ public CommandToGenerate(CommandMethod executeMethod, string propertyName)

public CommandMethod? CanExecuteMethod { get; set; }

public string CommandType { get; set; } = "DelegateCommand";

public override bool Equals(object? obj)
{
return Equals(obj as CommandToGenerate);
Expand Down
3 changes: 3 additions & 0 deletions src/MvvmGen.SourceGenerators/Model/ViewModelToGenerate.cs
Expand Up @@ -37,6 +37,8 @@ public ViewModelToGenerate(string className, string namespaceName)

public bool InheritFromViewModelBase { get; set; }

public string CommandType { get; set; } = "DelegateCommand";

public IEnumerable<CommandToGenerate>? CommandsToGenerate { get; set; }

public IEnumerable<CommandInvalidationToGenerate>? CommandInvalidationsToGenerate { get; set; }
Expand Down Expand Up @@ -66,6 +68,7 @@ public bool Equals(ViewModelToGenerate? other)
WrappedModelPropertyName == other.WrappedModelPropertyName &&
IsEventSubscriber == other.IsEventSubscriber &&
GenerateConstructor == other.GenerateConstructor &&
CommandType== other.CommandType &&
InheritFromViewModelBase == other.InheritFromViewModelBase &&
EqualityComparer<FactoryToGenerate?>.Default.Equals(ViewModelFactoryToGenerate, other.ViewModelFactoryToGenerate) &&
EqualityComparer<InterfaceToGenerate?>.Default.Equals(ViewModelInterfaceToGenerate, other.ViewModelInterfaceToGenerate) &&
Expand Down
9 changes: 5 additions & 4 deletions src/MvvmGen.SourceGenerators/ViewModelGenerator.cs
Expand Up @@ -81,7 +81,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
{
ClassAccessModifier = accessModifier,
InjectionsToGenerate = ViewModelInjectAttributeInspector.Inspect(viewModelClassSymbol),
BaseClassInjectionsToGenerate=ViewModelInjectAttributeInspector.Inspect(viewModelClassSymbol.BaseType),
BaseClassInjectionsToGenerate = ViewModelInjectAttributeInspector.Inspect(viewModelClassSymbol.BaseType),
CommandType = ViewModelAttributeInspector.InspectCommandType(viewModelAttributeData),
GenerateConstructor = ViewModelAttributeInspector.InspectGenerateConstructor(viewModelAttributeData),
ViewModelFactoryToGenerate = ViewModelGenerateFactoryAttributeInspector.Inspect(viewModelClassSymbol),
InheritFromViewModelBase = ViewModelBaseClassInspector.Inspect(viewModelClassSymbol, viewModelBaseClassSymbol),
Expand All @@ -91,7 +92,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
};

viewModelToGenerate.WrappedModelPropertyName = ViewModelAttributeInspector.InspectModelPropertyName(viewModelAttributeData);
viewModelToGenerate.WrappedModelType = ModelMemberInspector.Inspect(viewModelAttributeData, viewModelToGenerate.PropertiesToGenerate,viewModelToGenerate.WrappedModelPropertyName);
viewModelToGenerate.WrappedModelType = ModelMemberInspector.Inspect(viewModelAttributeData, viewModelToGenerate.PropertiesToGenerate, viewModelToGenerate.WrappedModelPropertyName);

viewModelToGenerate.ViewModelInterfaceToGenerate = ViewModelGenerateInterfaceAttributeInspector.Inspect(viewModelClassSymbol,
viewModelToGenerate.PropertiesToGenerate, viewModelToGenerate.CommandsToGenerate, context.Node.SyntaxTree);
Expand Down Expand Up @@ -142,11 +143,11 @@ private static void Execute(SourceProductionContext context, ViewModelToGenerate

vmBuilder.GenerateConstructor(viewModelToGenerate);

vmBuilder.GenerateCommandProperties(viewModelToGenerate.CommandsToGenerate);
vmBuilder.GenerateCommandProperties(viewModelToGenerate.CommandsToGenerate, viewModelToGenerate.CommandType);

vmBuilder.GenerateProperties(viewModelToGenerate.PropertiesToGenerate);

vmBuilder.GenerateModelProperty(viewModelToGenerate.WrappedModelType,viewModelToGenerate.WrappedModelPropertyName);
vmBuilder.GenerateModelProperty(viewModelToGenerate.WrappedModelType, viewModelToGenerate.WrappedModelPropertyName);

vmBuilder.GenerateInjectionProperties(viewModelToGenerate.InjectionsToGenerate);

Expand Down
14 changes: 14 additions & 0 deletions src/MvvmGen/Attributes/ViewModelAttribute.cs
Expand Up @@ -44,5 +44,19 @@ public ViewModelAttribute(Type modelType)
/// Gets or sets if a constructor is generated. Default value is true.
/// </summary>
public bool GenerateConstructor { get; set; } = true;

/// <summary>
/// Gets or sets the <see cref="MvvmGen.Commands.IDelegateCommand"/> implementation to use.
/// That your <see cref="MvvmGen.Commands.IDelegateCommand"/> implementation works seemlessly
/// with MvvmGen, it must have a constructor with the following signature:
/// <code>
/// public YourCommand(Action<object?> execute, Func<object?, bool>? canExecute = null)
/// {
/// }
/// </code>
/// If this property is not set, the <see cref="MvvmGen.Commands.DelegateCommand"/> class is used
/// as an <see cref="MvvmGen.Commands.IDelegateCommand"/> implementation.
/// </summary>
public Type? CommandType { get; set; }
}
}

0 comments on commit ba24c8e

Please sign in to comment.