Skip to content

Commit 30c4ad4

Browse files
committed
Support partial [RelayCommand] methods
1 parent 974e819 commit 30c4ad4

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

src/CommunityToolkit.Mvvm.SourceGenerators/Input/RelayCommandGenerator.Execute.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,15 @@ private static bool IsCommandDefinitionUnique(IMethodSymbol methodSymbol, in Imm
430430
return true;
431431
}
432432

433+
// If the two method symbols are partial and either is the implementation of the other one, this is allowed
434+
if ((methodSymbol is { IsPartialDefinition: true, PartialImplementationPart: { } partialImplementation } &&
435+
SymbolEqualityComparer.Default.Equals(otherSymbol, partialImplementation)) ||
436+
(otherSymbol is { IsPartialDefinition: true, PartialImplementationPart: { } otherPartialImplementation } &&
437+
SymbolEqualityComparer.Default.Equals(methodSymbol, otherPartialImplementation)))
438+
{
439+
continue;
440+
}
441+
433442
diagnostics.Add(
434443
MultipleRelayCommandMethodOverloadsError,
435444
methodSymbol,

tests/CommunityToolkit.Mvvm.UnitTests/Test_RelayCommandAttribute.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,42 @@ static void ValidateTestAttribute(TestValidationAttribute testAttribute)
659659
Assert.AreEqual(testAttribute2.Animal, (Test_ObservablePropertyAttribute.Animal)67);
660660
}
661661

662+
// See https://github.com/CommunityToolkit/dotnet/issues/632
663+
[TestMethod]
664+
public void Test_RelayCommandAttribute_WithPartialCommandMethodDefinitions()
665+
{
666+
ModelWithPartialCommandMethods model = new();
667+
668+
Assert.IsInstanceOfType<RelayCommand>(model.FooCommand);
669+
Assert.IsInstanceOfType<RelayCommand<string>>(model.BarCommand);
670+
Assert.IsInstanceOfType<RelayCommand>(model.BazCommand);
671+
Assert.IsInstanceOfType<AsyncRelayCommand>(model.FooBarCommand);
672+
673+
FieldInfo bazField = typeof(ModelWithPartialCommandMethods).GetField("bazCommand", BindingFlags.Instance | BindingFlags.NonPublic)!;
674+
675+
Assert.IsNotNull(bazField.GetCustomAttribute<RequiredAttribute>());
676+
Assert.IsNotNull(bazField.GetCustomAttribute<MinLengthAttribute>());
677+
Assert.AreEqual(bazField.GetCustomAttribute<MinLengthAttribute>()!.Length, 1);
678+
679+
PropertyInfo bazProperty = typeof(ModelWithPartialCommandMethods).GetProperty("BazCommand")!;
680+
681+
Assert.IsNotNull(bazProperty.GetCustomAttribute<MinLengthAttribute>());
682+
Assert.AreEqual(bazProperty.GetCustomAttribute<MinLengthAttribute>()!.Length, 2);
683+
Assert.IsNotNull(bazProperty.GetCustomAttribute<XmlIgnoreAttribute>());
684+
685+
FieldInfo fooBarField = typeof(ModelWithPartialCommandMethods).GetField("fooBarCommand", BindingFlags.Instance | BindingFlags.NonPublic)!;
686+
687+
Assert.IsNotNull(fooBarField.GetCustomAttribute<RequiredAttribute>());
688+
Assert.IsNotNull(fooBarField.GetCustomAttribute<MinLengthAttribute>());
689+
Assert.AreEqual(fooBarField.GetCustomAttribute<MinLengthAttribute>()!.Length, 1);
690+
691+
PropertyInfo fooBarProperty = typeof(ModelWithPartialCommandMethods).GetProperty("FooBarCommand")!;
692+
693+
Assert.IsNotNull(fooBarProperty.GetCustomAttribute<MinLengthAttribute>());
694+
Assert.AreEqual(fooBarProperty.GetCustomAttribute<MinLengthAttribute>()!.Length, 2);
695+
Assert.IsNotNull(fooBarProperty.GetCustomAttribute<XmlIgnoreAttribute>());
696+
}
697+
662698
#region Region
663699
public class Region
664700
{
@@ -1202,4 +1238,44 @@ public TestValidationAttribute(object? o, Type t, bool flag, double d, string[]
12021238

12031239
public Test_ObservablePropertyAttribute.Animal Animal { get; set; }
12041240
}
1241+
1242+
public partial class ModelWithPartialCommandMethods
1243+
{
1244+
[RelayCommand]
1245+
private partial void Foo();
1246+
1247+
private partial void Foo()
1248+
{
1249+
}
1250+
1251+
private partial void Bar(string name);
1252+
1253+
[RelayCommand]
1254+
private partial void Bar(string name)
1255+
{
1256+
}
1257+
1258+
[RelayCommand]
1259+
[field: Required]
1260+
[property: MinLength(2)]
1261+
partial void Baz();
1262+
1263+
[field: MinLength(1)]
1264+
[property: XmlIgnore]
1265+
partial void Baz()
1266+
{
1267+
}
1268+
1269+
[field: Required]
1270+
[property: MinLength(2)]
1271+
private partial Task FooBarAsync();
1272+
1273+
[RelayCommand]
1274+
[field: MinLength(1)]
1275+
[property: XmlIgnore]
1276+
private partial Task FooBarAsync()
1277+
{
1278+
return Task.CompletedTask;
1279+
}
1280+
}
12051281
}

0 commit comments

Comments
 (0)