Skip to content

Commit

Permalink
Introduce IDelegateCommandFactory, so that custom command implementat…
Browse files Browse the repository at this point in the history
…ions are possible
  • Loading branch information
thomasclaudiushuber committed Nov 25, 2023
1 parent 774b9c6 commit 8816a0f
Show file tree
Hide file tree
Showing 16 changed files with 210 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,22 @@ public void ShouldNotBeEqualDifferentWrappedModelPropertyName2()
Assert.NotEqual(_viewModelToGenerate1, _viewModelToGenerate2);
}

[Fact]
public void ShouldNotBeEqualDifferentCommandFactoryType1()
{
_viewModelToGenerate2.CommandFactoryType = "typeof(MyNewCommandFactory)";

Assert.NotEqual(_viewModelToGenerate1, _viewModelToGenerate2);
}

[Fact]
public void ShouldNotBeEqualDifferentCommandFactoryType2()
{
_viewModelToGenerate2.CommandFactoryType = null;

Assert.NotEqual(_viewModelToGenerate1, _viewModelToGenerate2);
}

[Fact]
public void ShouldNotBeEqualDifferentPropertiesToGenerate1()
{
Expand Down Expand Up @@ -342,6 +358,7 @@ private static void FillAllProperties(ViewModelToGenerate viewModelToGenerate)
viewModelToGenerate.IsEventSubscriber = true;
viewModelToGenerate.WrappedModelType = "Employee";
viewModelToGenerate.WrappedModelPropertyName = "EmployeeModel";
viewModelToGenerate.CommandFactoryType = "typeof(MyCommandFactory)";

viewModelToGenerate.PropertiesToGenerate = new List<PropertyToGenerate>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ public EmployeeViewModel()
private void InitializeCommands()
{{
SaveAllCommand = new DelegateCommand(_ => SaveAll());
var commandFactory = new DelegateCommandFactory();
SaveAllCommand = commandFactory.CreateCommand(_ => SaveAll(), null);
}}
public DelegateCommand SaveAllCommand {{ get; private set; }}
public IDelegateCommand SaveAllCommand {{ get; private set; }}
}}
}}
");
Expand Down Expand Up @@ -86,10 +87,11 @@ public EmployeeViewModel()
private void InitializeCommands()
{{
SaveAllCommand = new DelegateCommand(_ => SaveAll(), _ => CanSaveAll());
var commandFactory = new DelegateCommandFactory();
SaveAllCommand = commandFactory.CreateCommand(_ => SaveAll(), _ => CanSaveAll());
}}
public DelegateCommand SaveAllCommand {{ get; private set; }}
public IDelegateCommand SaveAllCommand {{ get; private set; }}
}}
}}
");
Expand Down Expand Up @@ -132,10 +134,11 @@ public EmployeeViewModel()
private void InitializeCommands()
{{
SuperCommand = new DelegateCommand(_ => SaveAll(), _ => CanSaveAll());
var commandFactory = new DelegateCommandFactory();
SuperCommand = commandFactory.CreateCommand(_ => SaveAll(), _ => CanSaveAll());
}}
public DelegateCommand SuperCommand {{ get; private set; }}
public IDelegateCommand SuperCommand {{ get; private set; }}
}}
}}
");
Expand Down Expand Up @@ -182,10 +185,11 @@ public EmployeeViewModel()
private void InitializeCommands()
{{
SaveCommand = new DelegateCommand({expectedConstructorArguments});
var commandFactory = new DelegateCommandFactory();
SaveCommand = commandFactory.CreateCommand({expectedConstructorArguments});
}}
public DelegateCommand SaveCommand {{ get; private set; }}
public IDelegateCommand SaveCommand {{ get; private set; }}
}}
}}
");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,11 @@ public EmployeeViewModel()
private void InitializeCommands()
{{
SaveCommand = new DelegateCommand(_ => Save(), _ => CanSave());
var commandFactory = new DelegateCommandFactory();
SaveCommand = commandFactory.CreateCommand(_ => Save(), _ => CanSave());
}}
public DelegateCommand SaveCommand {{ get; private set; }}
public IDelegateCommand SaveCommand {{ get; private set; }}
public string FirstName
{{
Expand Down Expand Up @@ -180,13 +181,14 @@ public EmployeeViewModel()
private void InitializeCommands()
{{
SaveCommand = new DelegateCommand(_ => Save(), _ => CanSave());
DeleteCommand = new DelegateCommand(_ => Delete(), _ => CanDelete());
var commandFactory = new DelegateCommandFactory();
SaveCommand = commandFactory.CreateCommand(_ => Save(), _ => CanSave());
DeleteCommand = commandFactory.CreateCommand(_ => Delete(), _ => CanDelete());
}}
public DelegateCommand SaveCommand {{ get; private set; }}
public IDelegateCommand SaveCommand {{ get; private set; }}
public DelegateCommand DeleteCommand {{ get; private set; }}
public IDelegateCommand DeleteCommand {{ get; private set; }}
public string FirstName
{{
Expand Down Expand Up @@ -272,10 +274,11 @@ public EmployeeViewModel()
private void InitializeCommands()
{{
SaveCommand = new DelegateCommand(_ => Save(), _ => CanSave());
var commandFactory = new DelegateCommandFactory();
SaveCommand = commandFactory.CreateCommand(_ => Save(), _ => CanSave());
}}
public DelegateCommand SaveCommand {{ get; private set; }}
public IDelegateCommand SaveCommand {{ get; private set; }}
protected override void InvalidateCommands(string? propertyName)
{{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************

using System;
using MvvmGen.Commands;
using Xunit;

namespace MvvmGen.SourceGenerators
Expand Down Expand Up @@ -308,5 +310,73 @@ public EmployeeViewModel()
}}
");
}

[Fact]
public void GenerateCommandPropertyWithCustomCommandType()
{
ShouldGenerateExpectedCode(
@"using MvvmGen;
using MvvmGen.Commands;
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)
{
_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);
}
public class MyCommandFactory : IDelegateCommandFactory
{
public IDelegateCommand CreateCommand(Action<object?> execute, Func<bool,object?> canExecute)
{
return new MyCommand(execute, canExecute);
}
}
[ViewModel(CommandFactoryType=typeof(MyCommandFactory))]
public partial class EmployeeViewModel
{
[Command]public void SaveAll() { }
}
}",
$@"{AutoGeneratedTopContent}
namespace MyCode
{{
partial class EmployeeViewModel : global::MvvmGen.ViewModels.ViewModelBase
{{
public EmployeeViewModel()
{{
this.InitializeCommands();
this.OnInitialize();
}}
partial void OnInitialize();
private void InitializeCommands()
{{
var commandFactory = new MyCode.MyCommandFactory();
SaveAllCommand = commandFactory.CreateCommand(_ => SaveAll(), null);
}}
public IDelegateCommand SaveAllCommand {{ get; private set; }}
}}
}}
");
}
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,16 @@ public EmployeeViewModel()
private void InitializeCommands()
{{
SaveCommand = new DelegateCommand(_ => Save());
var commandFactory = new DelegateCommandFactory();
SaveCommand = commandFactory.CreateCommand(_ => Save(), null);
}}
public DelegateCommand SaveCommand {{ get; private set; }}
public IDelegateCommand SaveCommand {{ get; private set; }}
}}
public interface IEmployeeViewModel
{{
DelegateCommand SaveCommand {{ get; }}
IDelegateCommand SaveCommand {{ get; }}
void Save();
}}
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,32 @@ namespace MvvmGen.Generators
{
internal static class CommandInitializeMethodGenerator
{
internal static void GenerateCommandInitializeMethod(this ViewModelBuilder vmBuilder, IEnumerable<CommandToGenerate>? commandsToGenerate)
internal static void GenerateCommandInitializeMethod(
this ViewModelBuilder vmBuilder,
IEnumerable<CommandToGenerate>? commandsToGenerate,
string? commandFactoryType)
{
if (commandsToGenerate is not null && commandsToGenerate.Any())
{
vmBuilder.AppendLineBeforeMember();
vmBuilder.AppendLine("private void InitializeCommands()");
vmBuilder.AppendLine("{");
vmBuilder.IncreaseIndent();

commandFactoryType ??= "DelegateCommandFactory";
vmBuilder.AppendLine($"var commandFactory = new {commandFactoryType}();");

foreach (var commandToGenerate in commandsToGenerate)
{
vmBuilder.Append($"{commandToGenerate.PropertyName} = new DelegateCommand({GetMethodCall(commandToGenerate.ExecuteMethod)}");
vmBuilder.Append($"{commandToGenerate.PropertyName} = commandFactory.CreateCommand({GetMethodCall(commandToGenerate.ExecuteMethod)}");
if (commandToGenerate.CanExecuteMethod is not null)
{
vmBuilder.Append($", {GetMethodCall(commandToGenerate.CanExecuteMethod)}");
}
else
{
vmBuilder.Append(", null");
}

vmBuilder.AppendLine(");");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal static void GenerateCommandProperties(this ViewModelBuilder vmBuilder,
foreach (var commandToGenerate in commandsToGenerate)
{
vmBuilder.AppendLineBeforeMember();
vmBuilder.AppendLine($"public DelegateCommand {commandToGenerate.PropertyName} {{ get; private set; }}");
vmBuilder.AppendLine($"public IDelegateCommand {commandToGenerate.PropertyName} {{ get; private set; }}");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,28 @@
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************

using System;
using Microsoft.CodeAnalysis;

namespace MvvmGen.Inspectors
{
internal static class ViewModelAttributeInspector
{
internal static string? InspectCommandFactoryType(AttributeData viewModelAttributeData)
{
string? commandFactoryType = null;

foreach (var arg in viewModelAttributeData.NamedArguments)
{
if (arg.Key == "CommandFactoryType")
{
commandFactoryType = arg.Value.Value?.ToString();
}
}

return commandFactoryType;
}

internal static bool InspectGenerateConstructor(AttributeData viewModelAttributeData)
{
var generateConstructor = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ internal static class ViewModelGenerateInterfaceAttributeInspector
foreach (var commandToGenerate in commandsToGenerate)
{
properties ??= new();
properties.Add(new InterfaceProperty(commandToGenerate.PropertyName, "DelegateCommand", true));
properties.Add(new InterfaceProperty(commandToGenerate.PropertyName, "IDelegateCommand", true));
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/MvvmGen.SourceGenerators/Model/ViewModelToGenerate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public ViewModelToGenerate(string className, string namespaceName)

public bool GenerateConstructor { get; set; }

public string? CommandFactoryType { get; set; }

public bool InheritFromViewModelBase { get; set; }

public IEnumerable<CommandToGenerate>? CommandsToGenerate { get; set; }
Expand Down Expand Up @@ -64,6 +66,7 @@ public bool Equals(ViewModelToGenerate? other)
WrappedModelPropertyName == other.WrappedModelPropertyName &&
IsEventSubscriber == other.IsEventSubscriber &&
GenerateConstructor == other.GenerateConstructor &&
CommandFactoryType == other.CommandFactoryType &&
InheritFromViewModelBase == other.InheritFromViewModelBase &&
EqualityComparer<FactoryToGenerate?>.Default.Equals(ViewModelFactoryToGenerate, other.ViewModelFactoryToGenerate) &&
EqualityComparer<InterfaceToGenerate?>.Default.Equals(ViewModelInterfaceToGenerate, other.ViewModelInterfaceToGenerate) &&
Expand All @@ -81,6 +84,7 @@ public override int GetHashCode()
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(NamespaceName);
hashCode = hashCode * -1521134295 + EqualityComparer<string?>.Default.GetHashCode(WrappedModelType);
hashCode = hashCode * -1521134295 + EqualityComparer<string?>.Default.GetHashCode(WrappedModelPropertyName);
hashCode = hashCode * -1521134295 + EqualityComparer<string?>.Default.GetHashCode(CommandFactoryType);
hashCode = hashCode * -1521134295 + IsEventSubscriber.GetHashCode();
hashCode = hashCode * -1521134295 + GenerateConstructor.GetHashCode();
hashCode = hashCode * -1521134295 + InheritFromViewModelBase.GetHashCode();
Expand Down
7 changes: 4 additions & 3 deletions src/MvvmGen.SourceGenerators/ViewModelGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
ClassAccessModifier = accessModifier,
InjectionsToGenerate = ViewModelInjectAttributeInspector.Inspect(viewModelClassSymbol),
GenerateConstructor = ViewModelAttributeInspector.InspectGenerateConstructor(viewModelAttributeData),
CommandFactoryType = ViewModelAttributeInspector.InspectCommandFactoryType(viewModelAttributeData),
ViewModelFactoryToGenerate = ViewModelGenerateFactoryAttributeInspector.Inspect(viewModelClassSymbol),
InheritFromViewModelBase = ViewModelBaseClassInspector.Inspect(viewModelClassSymbol, viewModelBaseClassSymbol),
CommandsToGenerate = commandsToGenerate,
Expand All @@ -90,7 +91,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);
Expand Down Expand Up @@ -139,13 +140,13 @@ private static void Execute(SourceProductionContext context, ViewModelToGenerate

vmBuilder.GenerateConstructor(viewModelToGenerate);

vmBuilder.GenerateCommandInitializeMethod(viewModelToGenerate.CommandsToGenerate);
vmBuilder.GenerateCommandInitializeMethod(viewModelToGenerate.CommandsToGenerate, viewModelToGenerate.CommandFactoryType);

vmBuilder.GenerateCommandProperties(viewModelToGenerate.CommandsToGenerate);

vmBuilder.GenerateProperties(viewModelToGenerate.PropertiesToGenerate);

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

vmBuilder.GenerateInjectionProperties(viewModelToGenerate.InjectionsToGenerate);

Expand Down
9 changes: 8 additions & 1 deletion src/MvvmGen/Attributes/ViewModelAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,17 @@ public ViewModelAttribute(Type modelType)
public Type? ModelType { get; set; }

/// <summary>
/// Gets or sets the name of generated property that contains the wrapped ModelType. If not set, the property has the name Model
/// Gets or sets the name of generated property that contains the wrapped ModelType. If not set, the property has the name Model.
/// </summary>
public string? ModelPropertyName { get; set; }

/// <summary>
/// Gets or sets the command factory type that is used in the ViewModel.
/// If not set, MvvmGen's <see cref="Commands.DelegateCommandFactory"/> will be used.
/// Note that the specified command factory type must implement the <see cref="Commands.IDelegateCommandFactory"/> interface.
/// </summary>
public Type? CommandFactoryType { get; set; }

/// <summary>
/// Gets or sets if a constructor is generated. Default value is true.
/// </summary>
Expand Down
Loading

0 comments on commit 8816a0f

Please sign in to comment.