mirror of
https://github.com/chylex/.NET-Community-Toolkit.git
synced 2024-11-25 01:42:46 +01:00
1441 lines
49 KiB
C#
1441 lines
49 KiB
C#
// Licensed to the .NET Foundation under one or more agreements.
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
// See the LICENSE file in the project root for more information.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading.Tasks;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using CommunityToolkit.Mvvm.ExternalAssembly;
|
|
using CommunityToolkit.Mvvm.Input;
|
|
using CommunityToolkit.Mvvm.Messaging;
|
|
using CommunityToolkit.Mvvm.Messaging.Messages;
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
|
|
namespace CommunityToolkit.Mvvm.UnitTests;
|
|
|
|
[TestClass]
|
|
public partial class Test_ObservablePropertyAttribute
|
|
{
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyAttribute_Events()
|
|
{
|
|
SampleModel model = new();
|
|
|
|
(PropertyChangingEventArgs, int) changing = default;
|
|
(PropertyChangedEventArgs, int) changed = default;
|
|
|
|
model.PropertyChanging += (s, e) =>
|
|
{
|
|
Assert.IsNull(changing.Item1);
|
|
Assert.IsNull(changed.Item1);
|
|
Assert.AreSame(model, s);
|
|
Assert.IsNotNull(s);
|
|
Assert.IsNotNull(e);
|
|
|
|
changing = (e, model.Data);
|
|
};
|
|
|
|
model.PropertyChanged += (s, e) =>
|
|
{
|
|
Assert.IsNotNull(changing.Item1);
|
|
Assert.IsNull(changed.Item1);
|
|
Assert.AreSame(model, s);
|
|
Assert.IsNotNull(s);
|
|
Assert.IsNotNull(e);
|
|
|
|
changed = (e, model.Data);
|
|
};
|
|
|
|
model.Data = 42;
|
|
|
|
Assert.AreEqual(changing.Item1?.PropertyName, nameof(SampleModel.Data));
|
|
Assert.AreEqual(changing.Item2, 0);
|
|
Assert.AreEqual(changed.Item1?.PropertyName, nameof(SampleModel.Data));
|
|
Assert.AreEqual(changed.Item2, 42);
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4225
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyAttributeWithinRegion_Events()
|
|
{
|
|
SampleModel model = new();
|
|
|
|
(PropertyChangingEventArgs, int) changing = default;
|
|
(PropertyChangedEventArgs, int) changed = default;
|
|
|
|
model.PropertyChanging += (s, e) =>
|
|
{
|
|
Assert.IsNull(changing.Item1);
|
|
Assert.IsNull(changed.Item1);
|
|
Assert.AreSame(model, s);
|
|
Assert.IsNotNull(s);
|
|
Assert.IsNotNull(e);
|
|
|
|
changing = (e, model.Counter);
|
|
};
|
|
|
|
model.PropertyChanged += (s, e) =>
|
|
{
|
|
Assert.IsNotNull(changing.Item1);
|
|
Assert.IsNull(changed.Item1);
|
|
Assert.AreSame(model, s);
|
|
Assert.IsNotNull(s);
|
|
Assert.IsNotNull(e);
|
|
|
|
changed = (e, model.Counter);
|
|
};
|
|
|
|
model.Counter = 42;
|
|
|
|
Assert.AreEqual(changing.Item1?.PropertyName, nameof(SampleModel.Counter));
|
|
Assert.AreEqual(changing.Item2, 0);
|
|
Assert.AreEqual(changed.Item1?.PropertyName, nameof(SampleModel.Counter));
|
|
Assert.AreEqual(changed.Item2, 42);
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4225
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyAttributeRightBelowRegion_Events()
|
|
{
|
|
SampleModel model = new();
|
|
|
|
(PropertyChangingEventArgs, string?) changing = default;
|
|
(PropertyChangedEventArgs, string?) changed = default;
|
|
|
|
model.PropertyChanging += (s, e) =>
|
|
{
|
|
Assert.IsNull(changing.Item1);
|
|
Assert.IsNull(changed.Item1);
|
|
Assert.AreSame(model, s);
|
|
Assert.IsNotNull(s);
|
|
Assert.IsNotNull(e);
|
|
|
|
changing = (e, model.Name);
|
|
};
|
|
|
|
model.PropertyChanged += (s, e) =>
|
|
{
|
|
Assert.IsNotNull(changing.Item1);
|
|
Assert.IsNull(changed.Item1);
|
|
Assert.AreSame(model, s);
|
|
Assert.IsNotNull(s);
|
|
Assert.IsNotNull(e);
|
|
|
|
changed = (e, model.Name);
|
|
};
|
|
|
|
model.Name = "Bob";
|
|
|
|
Assert.AreEqual(changing.Item1?.PropertyName, nameof(SampleModel.Name));
|
|
Assert.AreEqual(changing.Item2, null);
|
|
Assert.AreEqual(changed.Item1?.PropertyName, nameof(SampleModel.Name));
|
|
Assert.AreEqual(changed.Item2, "Bob");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyAttribute_Access_BothPublicDefault()
|
|
{
|
|
PropertyInfo prop = typeof(SampleModeWithAccessModifiers).GetProperty(nameof(SampleModeWithAccessModifiers.PrivateSetter))!;
|
|
Assert.IsTrue(prop.GetGetMethod()!.IsPublic);
|
|
Assert.IsTrue(prop.GetSetMethod()!.IsPublic);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyAttribute_Access_PrivateGetter()
|
|
{
|
|
PropertyInfo prop = typeof(SampleModeWithAccessModifiers).GetProperty(nameof(SampleModeWithAccessModifiers.PrivateGetter))!;
|
|
Assert.IsTrue(prop.GetGetMethod()!.IsPrivate);
|
|
Assert.IsTrue(prop.GetSetMethod()!.IsPublic);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyAttribute_Access_PrivateSetter()
|
|
{
|
|
PropertyInfo prop = typeof(SampleModeWithAccessModifiers).GetProperty(nameof(SampleModeWithAccessModifiers.PrivateSetter))!;
|
|
Assert.IsTrue(prop.GetGetMethod()!.IsPublic);
|
|
Assert.IsTrue(prop.GetSetMethod()!.IsPrivate);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyAttribute_Access_BothInternal1()
|
|
{
|
|
PropertyInfo prop = typeof(SampleModeWithAccessModifiers).GetProperty(nameof(SampleModeWithAccessModifiers.InternalGetterSetter1))!;
|
|
Assert.IsTrue(prop.GetGetMethod()!.IsAssembly);
|
|
Assert.IsTrue(prop.GetSetMethod()!.IsAssembly);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyAttribute_Access_BothInternal2()
|
|
{
|
|
PropertyInfo prop = typeof(SampleModeWithAccessModifiers).GetProperty(nameof(SampleModeWithAccessModifiers.InternalGetterSetter2))!;
|
|
Assert.IsTrue(prop.GetGetMethod()!.IsAssembly);
|
|
Assert.IsTrue(prop.GetSetMethod()!.IsAssembly);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_NotifyPropertyChangedForAttribute_Events()
|
|
{
|
|
DependentPropertyModel model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
|
|
model.Name = "Bob";
|
|
model.Surname = "Ross";
|
|
|
|
CollectionAssert.AreEqual(new[] { nameof(model.Name), nameof(model.FullName), nameof(model.Alias), nameof(model.Surname), nameof(model.FullName), nameof(model.Alias) }, propertyNames);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_ValidationAttributes()
|
|
{
|
|
PropertyInfo nameProperty = typeof(MyFormViewModel).GetProperty(nameof(MyFormViewModel.Name))!;
|
|
|
|
Assert.IsNotNull(nameProperty.GetCustomAttribute<RequiredAttribute>());
|
|
Assert.IsNotNull(nameProperty.GetCustomAttribute<MinLengthAttribute>());
|
|
Assert.AreEqual(nameProperty.GetCustomAttribute<MinLengthAttribute>()!.Length, 1);
|
|
Assert.IsNotNull(nameProperty.GetCustomAttribute<MaxLengthAttribute>());
|
|
Assert.AreEqual(nameProperty.GetCustomAttribute<MaxLengthAttribute>()!.Length, 100);
|
|
|
|
PropertyInfo ageProperty = typeof(MyFormViewModel).GetProperty(nameof(MyFormViewModel.Age))!;
|
|
|
|
Assert.IsNotNull(ageProperty.GetCustomAttribute<RangeAttribute>());
|
|
Assert.AreEqual(ageProperty.GetCustomAttribute<RangeAttribute>()!.Minimum, 0);
|
|
Assert.AreEqual(ageProperty.GetCustomAttribute<RangeAttribute>()!.Maximum, 120);
|
|
|
|
PropertyInfo emailProperty = typeof(MyFormViewModel).GetProperty(nameof(MyFormViewModel.Email))!;
|
|
|
|
Assert.IsNotNull(emailProperty.GetCustomAttribute<EmailAddressAttribute>());
|
|
|
|
PropertyInfo comboProperty = typeof(MyFormViewModel).GetProperty(nameof(MyFormViewModel.IfThisWorksThenThatsGreat))!;
|
|
|
|
TestValidationAttribute testAttribute = comboProperty.GetCustomAttribute<TestValidationAttribute>()!;
|
|
|
|
Assert.IsNotNull(testAttribute);
|
|
Assert.IsNull(testAttribute.O);
|
|
Assert.AreEqual(testAttribute.T, typeof(SampleModel));
|
|
Assert.AreEqual(testAttribute.Flag, true);
|
|
Assert.AreEqual(testAttribute.D, 6.28);
|
|
CollectionAssert.AreEqual(testAttribute.Names, new[] { "Bob", "Ross" });
|
|
|
|
object[]? nestedArray = (object[]?)testAttribute.NestedArray;
|
|
|
|
Assert.IsNotNull(nestedArray);
|
|
Assert.AreEqual(nestedArray!.Length, 3);
|
|
Assert.AreEqual(nestedArray[0], 1);
|
|
Assert.AreEqual(nestedArray[1], "Hello");
|
|
Assert.IsTrue(nestedArray[2] is int[]);
|
|
CollectionAssert.AreEqual((int[])nestedArray[2], new[] { 2, 3, 4 });
|
|
|
|
Assert.AreEqual(testAttribute.Animal, Animal.Llama);
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4216
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyWithValueNamedField()
|
|
{
|
|
ModelWithValueProperty model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
|
|
model.Value = "Hello world";
|
|
|
|
Assert.AreEqual(model.Value, "Hello world");
|
|
|
|
CollectionAssert.AreEqual(new[] { nameof(model.Value) }, propertyNames);
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4216
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyWithValueNamedField_WithValidationAttributes()
|
|
{
|
|
ModelWithValuePropertyWithValidation model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
|
|
bool errorsChanged = false;
|
|
|
|
model.ErrorsChanged += (s, e) => errorsChanged = true;
|
|
|
|
model.Value = "Hello world";
|
|
|
|
Assert.AreEqual(model.Value, "Hello world");
|
|
|
|
// The [NotifyDataErrorInfo] attribute wasn't used, so the property shouldn't be validated
|
|
Assert.IsFalse(errorsChanged);
|
|
|
|
CollectionAssert.AreEqual(new[] { nameof(model.Value) }, propertyNames);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyWithValueNamedField_WithValidationAttributesAndValidation()
|
|
{
|
|
ModelWithValuePropertyWithAutomaticValidation model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
|
|
List<DataErrorsChangedEventArgs> errors = new();
|
|
|
|
model.ErrorsChanged += (s, e) => errors.Add(e);
|
|
|
|
model.Value = "Bo";
|
|
|
|
Assert.IsTrue(model.HasErrors);
|
|
Assert.AreEqual(errors.Count, 1);
|
|
Assert.AreEqual(errors[0].PropertyName, nameof(ModelWithValuePropertyWithAutomaticValidation.Value));
|
|
|
|
model.Value = "Hello world";
|
|
|
|
Assert.IsFalse(model.HasErrors);
|
|
Assert.AreEqual(errors.Count, 2);
|
|
Assert.AreEqual(errors[1].PropertyName, nameof(ModelWithValuePropertyWithAutomaticValidation.Value));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyWithValueNamedField_WithValidationAttributesAndValidation_WithClassLevelAttribute()
|
|
{
|
|
ModelWithValuePropertyWithAutomaticValidationWithClassLevelAttribute model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
|
|
List<DataErrorsChangedEventArgs> errors = new();
|
|
|
|
model.ErrorsChanged += (s, e) => errors.Add(e);
|
|
|
|
model.Value = "Bo";
|
|
|
|
Assert.IsTrue(model.HasErrors);
|
|
Assert.AreEqual(errors.Count, 1);
|
|
Assert.AreEqual(errors[0].PropertyName, nameof(ModelWithValuePropertyWithAutomaticValidationWithClassLevelAttribute.Value));
|
|
|
|
model.Value = "Hello world";
|
|
|
|
Assert.IsFalse(model.HasErrors);
|
|
Assert.AreEqual(errors.Count, 2);
|
|
Assert.AreEqual(errors[1].PropertyName, nameof(ModelWithValuePropertyWithAutomaticValidationWithClassLevelAttribute.Value));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_ObservablePropertyWithValueNamedField_WithValidationAttributesAndValidation_InheritingClassLevelAttribute()
|
|
{
|
|
ModelWithValuePropertyWithAutomaticValidationInheritingClassLevelAttribute model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
|
|
List<DataErrorsChangedEventArgs> errors = new();
|
|
|
|
model.ErrorsChanged += (s, e) => errors.Add(e);
|
|
|
|
model.Value2 = "Bo";
|
|
|
|
Assert.IsTrue(model.HasErrors);
|
|
Assert.AreEqual(errors.Count, 1);
|
|
Assert.AreEqual(errors[0].PropertyName, nameof(ModelWithValuePropertyWithAutomaticValidationInheritingClassLevelAttribute.Value2));
|
|
|
|
model.Value2 = "Hello world";
|
|
|
|
Assert.IsFalse(model.HasErrors);
|
|
Assert.AreEqual(errors.Count, 2);
|
|
Assert.AreEqual(errors[1].PropertyName, nameof(ModelWithValuePropertyWithAutomaticValidationInheritingClassLevelAttribute.Value2));
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4184
|
|
[TestMethod]
|
|
public void Test_GeneratedPropertiesWithValidationAttributesOverFields()
|
|
{
|
|
ViewModelWithValidatableGeneratedProperties model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
|
|
// Assign these fields directly to bypass the validation that is executed in the generated setters.
|
|
// We only need those generated properties to be there to check whether they are correctly detected.
|
|
model.first = "A";
|
|
model.last = "This is a very long name that exceeds the maximum length of 60 for this property";
|
|
|
|
Assert.IsFalse(model.HasErrors);
|
|
|
|
model.RunValidation();
|
|
|
|
Assert.IsTrue(model.HasErrors);
|
|
|
|
ValidationResult[] validationErrors = model.GetErrors().ToArray();
|
|
|
|
Assert.AreEqual(validationErrors.Length, 2);
|
|
|
|
CollectionAssert.AreEqual(new[] { nameof(ViewModelWithValidatableGeneratedProperties.First) }, validationErrors[0].MemberNames.ToArray());
|
|
CollectionAssert.AreEqual(new[] { nameof(ViewModelWithValidatableGeneratedProperties.Last) }, validationErrors[1].MemberNames.ToArray());
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_NotifyPropertyChangedFor()
|
|
{
|
|
DependentPropertyModel model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
int canExecuteRequests = 0;
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
model.MyCommand.CanExecuteChanged += (s, e) => canExecuteRequests++;
|
|
|
|
model.Surname = "Ross";
|
|
|
|
Assert.AreEqual(1, canExecuteRequests);
|
|
|
|
CollectionAssert.AreEqual(new[] { nameof(model.Surname), nameof(model.FullName), nameof(model.Alias) }, propertyNames);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_NotifyPropertyChangedFor_GeneratedCommand()
|
|
{
|
|
DependentPropertyModel2 model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
int canExecuteRequests = 0;
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
model.TestFromMethodCommand.CanExecuteChanged += (s, e) => canExecuteRequests++;
|
|
|
|
model.Text = "Ross";
|
|
|
|
Assert.AreEqual(1, canExecuteRequests);
|
|
|
|
CollectionAssert.AreEqual(new[] { nameof(model.Text), nameof(model.FullName), nameof(model.Alias) }, propertyNames);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_NotifyPropertyChangedFor_IRelayCommandProperty()
|
|
{
|
|
DependentPropertyModel3 model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
int canExecuteRequests = 0;
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
model.MyCommand.CanExecuteChanged += (s, e) => canExecuteRequests++;
|
|
|
|
model.Text = "Ross";
|
|
|
|
Assert.AreEqual(1, canExecuteRequests);
|
|
|
|
CollectionAssert.AreEqual(new[] { nameof(model.Text), nameof(model.FullName), nameof(model.Alias) }, propertyNames);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_NotifyPropertyChangedFor_IAsyncRelayCommandOfTProperty()
|
|
{
|
|
DependentPropertyModel4 model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
int canExecuteRequests = 0;
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
model.MyCommand.CanExecuteChanged += (s, e) => canExecuteRequests++;
|
|
|
|
model.Text = "Ross";
|
|
|
|
Assert.AreEqual(1, canExecuteRequests);
|
|
|
|
CollectionAssert.AreEqual(new[] { nameof(model.Text), nameof(model.FullName), nameof(model.Alias) }, propertyNames);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_OnPropertyChangingAndChangedPartialMethods()
|
|
{
|
|
ViewModelWithImplementedUpdateMethods model = new();
|
|
|
|
model.Name = nameof(Test_OnPropertyChangingAndChangedPartialMethods);
|
|
|
|
Assert.AreEqual(nameof(Test_OnPropertyChangingAndChangedPartialMethods), model.NameChangingValue);
|
|
Assert.AreEqual(nameof(Test_OnPropertyChangingAndChangedPartialMethods), model.NameChangedValue);
|
|
|
|
model.Number = 99;
|
|
|
|
Assert.AreEqual(99, model.NumberChangedValue);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_OnPropertyChangingAndChangedPartialMethodWithAdditionalValidation()
|
|
{
|
|
ViewModelWithImplementedUpdateMethodAndAdditionalValidation model = new();
|
|
|
|
// The actual validation is performed inside the model itself.
|
|
// This test validates that the order with which methods/events are generated is:
|
|
// - On<PROPERTY_NAME>Changing(value);
|
|
// - OnPropertyChanging();
|
|
// - field = value;
|
|
// - On<PROPERTY_NAME>Changed(value);
|
|
// - OnPropertyChanged();
|
|
model.Name = "B";
|
|
|
|
Assert.AreEqual("B", model.Name);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_NotifyPropertyChangedRecipients_WithObservableObject()
|
|
{
|
|
Test_NotifyPropertyChangedRecipients_Test(
|
|
factory: static messenger => new BroadcastingViewModel(messenger),
|
|
setter: static (model, value) => model.Name = value,
|
|
propertyName: nameof(BroadcastingViewModel.Name));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_NotifyPropertyChangedRecipients_WithObservableRecipientAttribute()
|
|
{
|
|
Test_NotifyPropertyChangedRecipients_Test(
|
|
factory: static messenger => new BroadcastingViewModelWithAttribute(messenger),
|
|
setter: static (model, value) => model.Name = value,
|
|
propertyName: nameof(BroadcastingViewModelWithAttribute.Name));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_NotifyPropertyChangedRecipients_WithInheritedObservableRecipientAttribute()
|
|
{
|
|
Test_NotifyPropertyChangedRecipients_Test(
|
|
factory: static messenger => new BroadcastingViewModelWithInheritedAttribute(messenger),
|
|
setter: static (model, value) => model.Name2 = value,
|
|
propertyName: nameof(BroadcastingViewModelWithInheritedAttribute.Name2));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_NotifyPropertyChangedRecipients_WithObservableObject_WithClassLevelAttribute()
|
|
{
|
|
Test_NotifyPropertyChangedRecipients_Test(
|
|
factory: static messenger => new BroadcastingViewModelWithClassLevelAttribute(messenger),
|
|
setter: static (model, value) => model.Name = value,
|
|
propertyName: nameof(BroadcastingViewModelWithClassLevelAttribute.Name));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_NotifyPropertyChangedRecipients_WithObservableRecipientAttribute_WithClassLevelAttribute()
|
|
{
|
|
Test_NotifyPropertyChangedRecipients_Test(
|
|
factory: static messenger => new BroadcastingViewModelWithAttributeAndClassLevelAttribute(messenger),
|
|
setter: static (model, value) => model.Name = value,
|
|
propertyName: nameof(BroadcastingViewModelWithAttributeAndClassLevelAttribute.Name));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_NotifyPropertyChangedRecipients_WithInheritedObservableRecipientAttribute_WithClassLevelAttribute()
|
|
{
|
|
Test_NotifyPropertyChangedRecipients_Test(
|
|
factory: static messenger => new BroadcastingViewModelWithInheritedClassLevelAttribute(messenger),
|
|
setter: static (model, value) => model.Name2 = value,
|
|
propertyName: nameof(BroadcastingViewModelWithInheritedClassLevelAttribute.Name2));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_NotifyPropertyChangedRecipients_WithInheritedObservableRecipientAttributeAndClassLevelAttribute()
|
|
{
|
|
Test_NotifyPropertyChangedRecipients_Test(
|
|
factory: static messenger => new BroadcastingViewModelWithInheritedAttributeAndClassLevelAttribute(messenger),
|
|
setter: static (model, value) => model.Name2 = value,
|
|
propertyName: nameof(BroadcastingViewModelWithInheritedAttributeAndClassLevelAttribute.Name2));
|
|
}
|
|
|
|
private void Test_NotifyPropertyChangedRecipients_Test<T>(Func<IMessenger, T> factory, Action<T, string?> setter, string propertyName)
|
|
where T : notnull
|
|
{
|
|
IMessenger messenger = new StrongReferenceMessenger();
|
|
|
|
T model = factory(messenger);
|
|
|
|
List<(object Sender, PropertyChangedMessage<string?> Message)> messages = new();
|
|
|
|
messenger.Register<PropertyChangedMessage<string?>>(model, (r, m) => messages.Add((r, m)));
|
|
|
|
setter(model, "Bob");
|
|
|
|
Assert.AreEqual(1, messages.Count);
|
|
Assert.AreSame(model, messages[0].Sender);
|
|
Assert.AreEqual(null, messages[0].Message.OldValue);
|
|
Assert.AreEqual("Bob", messages[0].Message.NewValue);
|
|
Assert.AreEqual(propertyName, messages[0].Message.PropertyName);
|
|
|
|
setter(model, "Ross");
|
|
|
|
Assert.AreEqual(2, messages.Count);
|
|
Assert.AreSame(model, messages[1].Sender);
|
|
Assert.AreEqual("Bob", messages[1].Message.OldValue);
|
|
Assert.AreEqual("Ross", messages[1].Message.NewValue);
|
|
Assert.AreEqual(propertyName, messages[0].Message.PropertyName);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_ObservableProperty_ObservableRecipientDoesNotBroadcastByDefault()
|
|
{
|
|
IMessenger messenger = new StrongReferenceMessenger();
|
|
RecipientWithNonBroadcastingProperty model = new(messenger);
|
|
|
|
List<(object Sender, PropertyChangedMessage<string?> Message)> messages = new();
|
|
|
|
messenger.Register<PropertyChangedMessage<string?>>(model, (r, m) => messages.Add((r, m)));
|
|
|
|
model.Name = "Bob";
|
|
model.Name = "Alice";
|
|
model.Name = null;
|
|
|
|
// The [NotifyPropertyChangedRecipients] attribute wasn't used, so no messages should have been sent
|
|
Assert.AreEqual(messages.Count, 0);
|
|
}
|
|
|
|
#if NET6_0_OR_GREATER
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/155
|
|
[TestMethod]
|
|
public void Test_ObservableProperty_NullabilityAnnotations_Simple()
|
|
{
|
|
// List<string?>?
|
|
NullabilityInfoContext context = new();
|
|
NullabilityInfo info = context.Create(typeof(NullableRepro).GetProperty(nameof(NullableRepro.NullableList))!);
|
|
|
|
Assert.AreEqual(typeof(List<string>), info.Type);
|
|
Assert.AreEqual(NullabilityState.Nullable, info.ReadState);
|
|
Assert.AreEqual(NullabilityState.Nullable, info.WriteState);
|
|
Assert.AreEqual(1, info.GenericTypeArguments.Length);
|
|
|
|
NullabilityInfo elementInfo = info.GenericTypeArguments[0];
|
|
|
|
Assert.AreEqual(typeof(string), elementInfo.Type);
|
|
Assert.AreEqual(NullabilityState.Nullable, elementInfo.ReadState);
|
|
Assert.AreEqual(NullabilityState.Nullable, elementInfo.WriteState);
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/155
|
|
[TestMethod]
|
|
public void Test_ObservableProperty_NullabilityAnnotations_Complex()
|
|
{
|
|
// Foo<Foo<string?, int>.Bar<object?>?, StrongBox<Foo<int, string?>.Bar<object>?>?>?
|
|
NullabilityInfoContext context = new();
|
|
NullabilityInfo info = context.Create(typeof(NullableRepro).GetProperty(nameof(NullableRepro.NullableMess))!);
|
|
|
|
Assert.AreEqual(typeof(Foo<Foo<string?, int>.Bar<object?>?, StrongBox<Foo<int, string?>.Bar<object>?>?>), info.Type);
|
|
Assert.AreEqual(NullabilityState.Nullable, info.ReadState);
|
|
Assert.AreEqual(NullabilityState.Nullable, info.WriteState);
|
|
Assert.AreEqual(2, info.GenericTypeArguments.Length);
|
|
|
|
NullabilityInfo leftInfo = info.GenericTypeArguments[0];
|
|
|
|
Assert.AreEqual(typeof(Foo<string?, int>.Bar<object?>), leftInfo.Type);
|
|
Assert.AreEqual(NullabilityState.Nullable, leftInfo.ReadState);
|
|
Assert.AreEqual(NullabilityState.Nullable, leftInfo.WriteState);
|
|
Assert.AreEqual(3, leftInfo.GenericTypeArguments.Length);
|
|
|
|
NullabilityInfo leftInfo0 = leftInfo.GenericTypeArguments[0];
|
|
|
|
Assert.AreEqual(typeof(string), leftInfo0.Type);
|
|
Assert.AreEqual(NullabilityState.Nullable, leftInfo0.ReadState);
|
|
Assert.AreEqual(NullabilityState.Nullable, leftInfo0.WriteState);
|
|
|
|
NullabilityInfo leftInfo1 = leftInfo.GenericTypeArguments[1];
|
|
|
|
Assert.AreEqual(typeof(int), leftInfo1.Type);
|
|
Assert.AreEqual(NullabilityState.NotNull, leftInfo1.ReadState);
|
|
Assert.AreEqual(NullabilityState.NotNull, leftInfo1.WriteState);
|
|
|
|
NullabilityInfo leftInfo2 = leftInfo.GenericTypeArguments[2];
|
|
|
|
Assert.AreEqual(typeof(object), leftInfo2.Type);
|
|
Assert.AreEqual(NullabilityState.Nullable, leftInfo2.ReadState);
|
|
Assert.AreEqual(NullabilityState.Nullable, leftInfo2.WriteState);
|
|
|
|
NullabilityInfo rightInfo = info.GenericTypeArguments[1];
|
|
|
|
Assert.AreEqual(typeof(StrongBox<Foo<int, string?>.Bar<object>?>), rightInfo.Type);
|
|
Assert.AreEqual(NullabilityState.Nullable, rightInfo.ReadState);
|
|
Assert.AreEqual(NullabilityState.Nullable, rightInfo.WriteState);
|
|
Assert.AreEqual(1, rightInfo.GenericTypeArguments.Length);
|
|
|
|
NullabilityInfo rightInnerInfo = rightInfo.GenericTypeArguments[0];
|
|
|
|
Assert.AreEqual(typeof(Foo<int, string?>.Bar<object>), rightInnerInfo.Type);
|
|
Assert.AreEqual(NullabilityState.Nullable, rightInnerInfo.ReadState);
|
|
Assert.AreEqual(NullabilityState.Nullable, rightInnerInfo.WriteState);
|
|
Assert.AreEqual(3, rightInnerInfo.GenericTypeArguments.Length);
|
|
|
|
NullabilityInfo rightInfo0 = rightInnerInfo.GenericTypeArguments[0];
|
|
|
|
Assert.AreEqual(typeof(int), rightInfo0.Type);
|
|
Assert.AreEqual(NullabilityState.NotNull, rightInfo0.ReadState);
|
|
Assert.AreEqual(NullabilityState.NotNull, rightInfo0.WriteState);
|
|
|
|
NullabilityInfo rightInfo1 = rightInnerInfo.GenericTypeArguments[1];
|
|
|
|
Assert.AreEqual(typeof(string), rightInfo1.Type);
|
|
Assert.AreEqual(NullabilityState.Nullable, rightInfo1.ReadState);
|
|
Assert.AreEqual(NullabilityState.Nullable, rightInfo1.WriteState);
|
|
|
|
NullabilityInfo rightInfo2 = rightInnerInfo.GenericTypeArguments[2];
|
|
|
|
Assert.AreEqual(typeof(object), rightInfo2.Type);
|
|
//Assert.AreEqual(NullabilityState.NotNull, rightInfo2.ReadState);
|
|
//Assert.AreEqual(NullabilityState.NotNull, rightInfo2.WriteState);
|
|
|
|
// The commented out lines are to work around a bug in the NullabilityInfo API in .NET 6.
|
|
// This has been fixed for .NET 7: https://github.com/dotnet/runtime/pull/63556. The test
|
|
// cases above can be uncommented when the .NET 7 target (or a more recent version) is added.
|
|
}
|
|
#endif
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/201
|
|
[TestMethod]
|
|
public void Test_ObservableProperty_InheritedMembersAsAttributeTargets()
|
|
{
|
|
ConcreteViewModel model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
List<object?> canExecuteChangedArgs = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
model.DoSomethingCommand.CanExecuteChanged += (s, _) => canExecuteChangedArgs.Add(s);
|
|
model.ManualCommand.CanExecuteChanged += (s, _) => canExecuteChangedArgs.Add(s);
|
|
|
|
model.A = nameof(model.A);
|
|
model.B = nameof(model.B);
|
|
model.C = nameof(model.C);
|
|
model.D = nameof(model.D);
|
|
|
|
CollectionAssert.AreEqual(new[]
|
|
{
|
|
nameof(model.A),
|
|
nameof(model.Content),
|
|
nameof(model.B),
|
|
nameof(model.SomeGeneratedProperty),
|
|
nameof(model.C),
|
|
nameof(model.D)
|
|
}, propertyNames);
|
|
|
|
CollectionAssert.AreEqual(new[] { model.DoSomethingCommand, model.ManualCommand }, canExecuteChangedArgs);
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/224
|
|
[TestMethod]
|
|
public void Test_ObservableProperty_WithinGenericTypeWithMultipleTypeParameters()
|
|
{
|
|
ModelWithMultipleGenericParameters<int, string> model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
|
|
model.Value = true;
|
|
model.TValue = 42;
|
|
model.UValue = "Hello";
|
|
model.List = new List<int>() { 420 };
|
|
|
|
Assert.AreEqual(model.Value, true);
|
|
Assert.AreEqual(model.TValue, 42);
|
|
Assert.AreEqual(model.UValue, "Hello");
|
|
CollectionAssert.AreEqual(new[] { 420 }, model.List);
|
|
|
|
CollectionAssert.AreEqual(new[] { nameof(model.Value), nameof(model.TValue), nameof(model.UValue), nameof(model.List) }, propertyNames);
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/222
|
|
[TestMethod]
|
|
public void Test_ObservableProperty_WithBaseViewModelWithObservableObjectAttributeInAnotherAssembly()
|
|
{
|
|
ModelWithObservablePropertyAndBaseClassInAnotherAssembly model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
|
|
Assert.AreEqual(model.OtherProperty, "Ok");
|
|
|
|
model.MyProperty = "A";
|
|
model.OtherProperty = "B";
|
|
|
|
Assert.AreEqual(model.MyProperty, "A");
|
|
Assert.AreEqual(model.OtherProperty, "B");
|
|
|
|
CollectionAssert.AreEqual(new[] { nameof(model.MyProperty), nameof(model.OtherProperty) }, propertyNames);
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/230
|
|
[TestMethod]
|
|
public void Test_ObservableProperty_ModelWithCultureAwarePropertyName()
|
|
{
|
|
ModelWithCultureAwarePropertyName model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
|
|
model.InputFolder = 42;
|
|
|
|
Assert.AreEqual(model.InputFolder, 42);
|
|
|
|
CollectionAssert.AreEqual(new[] { nameof(model.InputFolder) }, propertyNames);
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/242
|
|
[TestMethod]
|
|
public void Test_ObservableProperty_ModelWithNotifyPropertyChangedRecipientsAndDisplayAttributeLast()
|
|
{
|
|
IMessenger messenger = new StrongReferenceMessenger();
|
|
ModelWithNotifyPropertyChangedRecipientsAndDisplayAttributeLast model = new(messenger);
|
|
|
|
List<string?> propertyNames = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
|
|
object newValue = new();
|
|
bool isMessageReceived = false;
|
|
|
|
messenger.Register<Test_ObservablePropertyAttribute, PropertyChangedMessage<object>>(this, (r, m) =>
|
|
{
|
|
if (m.Sender != model)
|
|
{
|
|
Assert.Fail();
|
|
}
|
|
|
|
if (m.NewValue != newValue)
|
|
{
|
|
Assert.Fail();
|
|
}
|
|
|
|
isMessageReceived = true;
|
|
});
|
|
|
|
model.SomeProperty = newValue;
|
|
|
|
Assert.AreEqual(model.SomeProperty, newValue);
|
|
Assert.IsTrue(isMessageReceived);
|
|
|
|
CollectionAssert.AreEqual(new[] { nameof(model.SomeProperty) }, propertyNames);
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/257
|
|
[TestMethod]
|
|
public void Test_ObservableProperty_InheritedModelWithCommandUsingInheritedObservablePropertyForCanExecute()
|
|
{
|
|
InheritedModelWithCommandUsingInheritedObservablePropertyForCanExecute model = new();
|
|
|
|
Assert.IsFalse(model.SaveCommand.CanExecute(null));
|
|
|
|
model.CanSave = true;
|
|
|
|
Assert.IsTrue(model.SaveCommand.CanExecute(null));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_ObservableProperty_ForwardsSpecialCasesDataAnnotationAttributes()
|
|
{
|
|
PropertyInfo propertyInfo = typeof(ModelWithAdditionalDataAnnotationAttributes).GetProperty(nameof(ModelWithAdditionalDataAnnotationAttributes.Name))!;
|
|
|
|
DisplayAttribute? displayAttribute = (DisplayAttribute?)propertyInfo.GetCustomAttribute(typeof(DisplayAttribute));
|
|
|
|
Assert.IsNotNull(displayAttribute);
|
|
Assert.AreEqual(displayAttribute!.Name, "MyProperty");
|
|
Assert.AreEqual(displayAttribute.ResourceType, typeof(List<double>));
|
|
Assert.AreEqual(displayAttribute.Prompt, "Foo bar baz");
|
|
|
|
KeyAttribute? keyAttribute = (KeyAttribute?)propertyInfo.GetCustomAttribute(typeof(KeyAttribute));
|
|
|
|
Assert.IsNotNull(keyAttribute);
|
|
|
|
EditableAttribute? editableAttribute = (EditableAttribute?)propertyInfo.GetCustomAttribute(typeof(EditableAttribute));
|
|
|
|
Assert.IsNotNull(keyAttribute);
|
|
Assert.IsTrue(editableAttribute!.AllowEdit);
|
|
|
|
UIHintAttribute? uiHintAttribute = (UIHintAttribute?)propertyInfo.GetCustomAttribute(typeof(UIHintAttribute));
|
|
|
|
Assert.IsNotNull(uiHintAttribute);
|
|
Assert.AreEqual(uiHintAttribute!.UIHint, "MyControl");
|
|
Assert.AreEqual(uiHintAttribute.PresentationLayer, "WPF");
|
|
Assert.AreEqual(uiHintAttribute.ControlParameters.Count, 3);
|
|
Assert.IsTrue(uiHintAttribute.ControlParameters.ContainsKey("Foo"));
|
|
Assert.IsTrue(uiHintAttribute.ControlParameters.ContainsKey("Bar"));
|
|
Assert.IsTrue(uiHintAttribute.ControlParameters.ContainsKey("Baz"));
|
|
Assert.AreEqual(uiHintAttribute.ControlParameters["Foo"], 42);
|
|
Assert.AreEqual(uiHintAttribute.ControlParameters["Bar"], 3.14);
|
|
Assert.AreEqual(uiHintAttribute.ControlParameters["Baz"], "Hello");
|
|
|
|
ScaffoldColumnAttribute? scaffoldColumnAttribute = (ScaffoldColumnAttribute?)propertyInfo.GetCustomAttribute(typeof(ScaffoldColumnAttribute));
|
|
|
|
Assert.IsNotNull(scaffoldColumnAttribute);
|
|
Assert.IsTrue(scaffoldColumnAttribute!.Scaffold);
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/271
|
|
[TestMethod]
|
|
public void Test_ObservableProperty_ModelWithObservablePropertyInRootNamespace()
|
|
{
|
|
ModelWithObservablePropertyInRootNamespace model = new();
|
|
|
|
List<string?> propertyNames = new();
|
|
|
|
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
|
|
|
|
model.Number = 3.14f;
|
|
|
|
// We mostly just need to verify this class compiles fine with the right generated code
|
|
CollectionAssert.AreEqual(propertyNames, new[] { nameof(model.Number) });
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/272
|
|
[TestMethod]
|
|
public void Test_ObservableProperty_WithCommandReferencingGeneratedPropertyFromOtherAssembly()
|
|
{
|
|
ModelWithOverriddenCommandMethodFromExternalBaseModel model = new();
|
|
|
|
Assert.IsFalse(model.HasSaved);
|
|
Assert.IsFalse(model.SaveCommand.CanExecute(null));
|
|
|
|
model.CanSave = true;
|
|
|
|
Assert.IsTrue(model.SaveCommand.CanExecute(null));
|
|
|
|
model.SaveCommand.Execute(null);
|
|
|
|
Assert.IsTrue(model.HasSaved);
|
|
}
|
|
|
|
public abstract partial class BaseViewModel : ObservableObject
|
|
{
|
|
public string? Content { get; set; }
|
|
|
|
[ObservableProperty]
|
|
private string? someGeneratedProperty;
|
|
|
|
[RelayCommand]
|
|
private void DoSomething()
|
|
{
|
|
}
|
|
|
|
public IRelayCommand ManualCommand { get; } = new RelayCommand(() => { });
|
|
}
|
|
|
|
public partial class ConcreteViewModel : BaseViewModel
|
|
{
|
|
// Inherited property
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(Content))]
|
|
private string? _a;
|
|
|
|
// Inherited generated property
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(SomeGeneratedProperty))]
|
|
private string? _b;
|
|
|
|
// Inherited generated command
|
|
[ObservableProperty]
|
|
[NotifyCanExecuteChangedFor(nameof(DoSomethingCommand))]
|
|
private string? _c;
|
|
|
|
// Inherited manual command
|
|
[ObservableProperty]
|
|
[NotifyCanExecuteChangedFor(nameof(ManualCommand))]
|
|
private string? _d;
|
|
}
|
|
|
|
public partial class SampleModel : ObservableObject
|
|
{
|
|
/// <summary>
|
|
/// This is a sample data field within <see cref="SampleModel"/> of type <see cref="int"/>.
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private int data;
|
|
|
|
#region More properties
|
|
|
|
[ObservableProperty]
|
|
private int counter;
|
|
|
|
#endregion
|
|
|
|
[ObservableProperty]
|
|
private string? name;
|
|
}
|
|
|
|
public partial class SampleModeWithAccessModifiers : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private int publicDefault;
|
|
|
|
[ObservableProperty(Access.Internal)]
|
|
private int internalGetterSetter1;
|
|
|
|
[ObservableProperty(Getter = Access.Internal, Setter = Access.Internal)]
|
|
private int internalGetterSetter2;
|
|
|
|
[ObservableProperty(Getter = Access.Private)]
|
|
private int privateGetter;
|
|
|
|
[ObservableProperty(Setter = Access.Private)]
|
|
private int privateSetter;
|
|
}
|
|
|
|
[INotifyPropertyChanged]
|
|
public sealed partial class DependentPropertyModel
|
|
{
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(FullName))]
|
|
[NotifyPropertyChangedFor(nameof(Alias))]
|
|
private string? name;
|
|
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(FullName), nameof(Alias))]
|
|
[NotifyCanExecuteChangedFor(nameof(MyCommand))]
|
|
private string? surname;
|
|
|
|
public string FullName => $"{Name} {Surname}";
|
|
|
|
public string Alias => $"{Name?.ToLower()}{Surname?.ToLower()}";
|
|
|
|
public RelayCommand MyCommand { get; } = new(() => { });
|
|
}
|
|
|
|
[INotifyPropertyChanged]
|
|
public sealed partial class DependentPropertyModel2
|
|
{
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(FullName), nameof(Alias))]
|
|
[NotifyCanExecuteChangedFor(nameof(TestFromMethodCommand))]
|
|
private string? text;
|
|
|
|
public string FullName => "";
|
|
|
|
public string Alias => "";
|
|
|
|
public RelayCommand MyCommand { get; } = new(() => { });
|
|
|
|
[RelayCommand]
|
|
private void TestFromMethod()
|
|
{
|
|
}
|
|
}
|
|
|
|
[INotifyPropertyChanged]
|
|
public sealed partial class DependentPropertyModel3
|
|
{
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(FullName), nameof(Alias))]
|
|
[NotifyCanExecuteChangedFor(nameof(MyCommand))]
|
|
private string? text;
|
|
|
|
public string FullName => "";
|
|
|
|
public string Alias => "";
|
|
|
|
public IRelayCommand MyCommand { get; } = new RelayCommand(() => { });
|
|
}
|
|
|
|
[INotifyPropertyChanged]
|
|
public sealed partial class DependentPropertyModel4
|
|
{
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(FullName), nameof(Alias))]
|
|
[NotifyCanExecuteChangedFor(nameof(MyCommand))]
|
|
private string? text;
|
|
|
|
public string FullName => "";
|
|
|
|
public string Alias => "";
|
|
|
|
public IAsyncRelayCommand<string> MyCommand { get; } = new AsyncRelayCommand<string>(_ => Task.CompletedTask);
|
|
}
|
|
|
|
public partial class MyFormViewModel : ObservableValidator
|
|
{
|
|
[ObservableProperty]
|
|
[Required]
|
|
[MinLength(1)]
|
|
[MaxLength(100)]
|
|
private string? name;
|
|
|
|
[ObservableProperty]
|
|
[Range(0, 120)]
|
|
private int age;
|
|
|
|
[ObservableProperty]
|
|
[EmailAddress]
|
|
private string? email;
|
|
|
|
[ObservableProperty]
|
|
[TestValidation(null, typeof(SampleModel), true, 6.28, new[] { "Bob", "Ross" }, NestedArray = new object[] { 1, "Hello", new int[] { 2, 3, 4 } }, Animal = Animal.Llama)]
|
|
private int ifThisWorksThenThatsGreat;
|
|
}
|
|
|
|
private sealed class TestValidationAttribute : ValidationAttribute
|
|
{
|
|
public TestValidationAttribute(object? o, Type t, bool flag, double d, string[] names)
|
|
{
|
|
O = o;
|
|
T = t;
|
|
Flag = flag;
|
|
D = d;
|
|
Names = names;
|
|
}
|
|
|
|
public object? O { get; }
|
|
|
|
public Type T { get; }
|
|
|
|
public bool Flag { get; }
|
|
|
|
public double D { get; }
|
|
|
|
public string[] Names { get; }
|
|
|
|
public object? NestedArray { get; set; }
|
|
|
|
public Animal Animal { get; set; }
|
|
}
|
|
|
|
public enum Animal
|
|
{
|
|
Cat,
|
|
Dog,
|
|
Llama
|
|
}
|
|
|
|
public partial class ModelWithValueProperty : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private string? value;
|
|
}
|
|
|
|
public partial class ModelWithValuePropertyWithValidation : ObservableValidator
|
|
{
|
|
[ObservableProperty]
|
|
[Required]
|
|
[MinLength(5)]
|
|
private string? value;
|
|
}
|
|
|
|
public partial class ModelWithValuePropertyWithAutomaticValidation : ObservableValidator
|
|
{
|
|
[ObservableProperty]
|
|
[Required]
|
|
[MinLength(5)]
|
|
[NotifyDataErrorInfo]
|
|
private string? value;
|
|
}
|
|
|
|
[NotifyDataErrorInfo]
|
|
public partial class ModelWithValuePropertyWithAutomaticValidationWithClassLevelAttribute : ObservableValidator
|
|
{
|
|
[ObservableProperty]
|
|
[Required]
|
|
[MinLength(5)]
|
|
private string? value;
|
|
}
|
|
|
|
public partial class ModelWithValuePropertyWithAutomaticValidationInheritingClassLevelAttribute : ModelWithValuePropertyWithAutomaticValidationWithClassLevelAttribute
|
|
{
|
|
[ObservableProperty]
|
|
[Required]
|
|
[MinLength(5)]
|
|
private string? value2;
|
|
}
|
|
|
|
public partial class ViewModelWithValidatableGeneratedProperties : ObservableValidator
|
|
{
|
|
[Required]
|
|
[MinLength(2)]
|
|
[MaxLength(60)]
|
|
[Display(Name = "FirstName")]
|
|
[ObservableProperty]
|
|
public string first = "Bob";
|
|
|
|
[Display(Name = "LastName")]
|
|
[Required]
|
|
[MinLength(2)]
|
|
[MaxLength(60)]
|
|
[ObservableProperty]
|
|
public string last = "Jones";
|
|
|
|
public void RunValidation() => ValidateAllProperties();
|
|
}
|
|
|
|
public partial class ViewModelWithImplementedUpdateMethods : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
public string? name = "Bob";
|
|
|
|
[ObservableProperty]
|
|
public int number = 42;
|
|
|
|
public string? NameChangingValue { get; private set; }
|
|
|
|
public string? NameChangedValue { get; private set; }
|
|
|
|
public int NumberChangedValue { get; private set; }
|
|
|
|
partial void OnNameChanging(string? value)
|
|
{
|
|
NameChangingValue = value;
|
|
}
|
|
|
|
partial void OnNameChanged(string? value)
|
|
{
|
|
NameChangedValue = value;
|
|
}
|
|
|
|
partial void OnNumberChanged(int value)
|
|
{
|
|
NumberChangedValue = value;
|
|
}
|
|
}
|
|
|
|
public partial class ViewModelWithImplementedUpdateMethodAndAdditionalValidation : ObservableObject
|
|
{
|
|
private int step;
|
|
|
|
[ObservableProperty]
|
|
public string? name = "A";
|
|
|
|
partial void OnNameChanging(string? value)
|
|
{
|
|
Assert.AreEqual(0, this.step);
|
|
|
|
this.step = 1;
|
|
|
|
Assert.AreEqual("A", this.name);
|
|
Assert.AreEqual("B", value);
|
|
}
|
|
|
|
partial void OnNameChanged(string? value)
|
|
{
|
|
Assert.AreEqual(2, this.step);
|
|
|
|
this.step = 3;
|
|
|
|
Assert.AreEqual("B", this.name);
|
|
Assert.AreEqual("B", value);
|
|
}
|
|
|
|
protected override void OnPropertyChanging(PropertyChangingEventArgs e)
|
|
{
|
|
base.OnPropertyChanging(e);
|
|
|
|
Assert.AreEqual(1, this.step);
|
|
|
|
this.step = 2;
|
|
|
|
Assert.AreEqual("A", this.name);
|
|
Assert.AreEqual(nameof(Name), e.PropertyName);
|
|
}
|
|
|
|
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
|
|
{
|
|
base.OnPropertyChanged(e);
|
|
|
|
Assert.AreEqual(3, this.step);
|
|
|
|
Assert.AreEqual("B", this.name);
|
|
Assert.AreEqual(nameof(Name), e.PropertyName);
|
|
}
|
|
}
|
|
|
|
partial class BroadcastingViewModel : ObservableRecipient
|
|
{
|
|
public BroadcastingViewModel(IMessenger messenger)
|
|
: base(messenger)
|
|
{
|
|
}
|
|
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedRecipients]
|
|
private string? name;
|
|
}
|
|
|
|
partial class RecipientWithNonBroadcastingProperty : ObservableRecipient
|
|
{
|
|
public RecipientWithNonBroadcastingProperty(IMessenger messenger)
|
|
: base(messenger)
|
|
{
|
|
}
|
|
|
|
[ObservableProperty]
|
|
private string? name;
|
|
}
|
|
|
|
[ObservableRecipient]
|
|
partial class BroadcastingViewModelWithAttribute : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedRecipients]
|
|
private string? name;
|
|
}
|
|
|
|
partial class BroadcastingViewModelWithInheritedAttribute : BroadcastingViewModelWithAttribute
|
|
{
|
|
public BroadcastingViewModelWithInheritedAttribute(IMessenger messenger)
|
|
: base(messenger)
|
|
{
|
|
}
|
|
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedRecipients]
|
|
private string? name2;
|
|
}
|
|
|
|
[NotifyPropertyChangedRecipients]
|
|
partial class BroadcastingViewModelWithClassLevelAttribute : ObservableRecipient
|
|
{
|
|
public BroadcastingViewModelWithClassLevelAttribute(IMessenger messenger)
|
|
: base(messenger)
|
|
{
|
|
}
|
|
|
|
[ObservableProperty]
|
|
private string? name;
|
|
}
|
|
|
|
partial class BroadcastingViewModelWithInheritedClassLevelAttribute : BroadcastingViewModelWithClassLevelAttribute
|
|
{
|
|
public BroadcastingViewModelWithInheritedClassLevelAttribute(IMessenger messenger)
|
|
: base(messenger)
|
|
{
|
|
}
|
|
|
|
[ObservableProperty]
|
|
private string? name2;
|
|
}
|
|
|
|
[ObservableRecipient]
|
|
[NotifyPropertyChangedRecipients]
|
|
partial class BroadcastingViewModelWithAttributeAndClassLevelAttribute : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private string? name;
|
|
}
|
|
|
|
partial class BroadcastingViewModelWithInheritedAttributeAndClassLevelAttribute : BroadcastingViewModelWithAttributeAndClassLevelAttribute
|
|
{
|
|
public BroadcastingViewModelWithInheritedAttributeAndClassLevelAttribute(IMessenger messenger)
|
|
: base(messenger)
|
|
{
|
|
}
|
|
|
|
[ObservableProperty]
|
|
private string? name2;
|
|
}
|
|
|
|
#if NET6_0_OR_GREATER
|
|
private partial class NullableRepro : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private List<string?>? nullableList;
|
|
|
|
[ObservableProperty]
|
|
private Foo<Foo<string?, int>.Bar<object?>?, StrongBox<Foo<int, string?>.Bar<object>?>?>? nullableMess;
|
|
}
|
|
|
|
private class Foo<T1, T2>
|
|
{
|
|
public class Bar<T>
|
|
{
|
|
}
|
|
}
|
|
#endif
|
|
|
|
partial class ModelWithObservablePropertyAndBaseClassInAnotherAssembly : ModelWithObservableObjectAttribute
|
|
{
|
|
[ObservableProperty]
|
|
private string? _otherProperty;
|
|
|
|
public ModelWithObservablePropertyAndBaseClassInAnotherAssembly()
|
|
{
|
|
OtherProperty = "Ok";
|
|
}
|
|
}
|
|
|
|
interface IValueHolder
|
|
{
|
|
public bool Value { get; }
|
|
}
|
|
|
|
partial class ModelWithMultipleGenericParameters<T, U> : ObservableObject, IValueHolder
|
|
{
|
|
[ObservableProperty]
|
|
private bool value;
|
|
|
|
[ObservableProperty]
|
|
private T? tValue;
|
|
|
|
[ObservableProperty]
|
|
private U? uValue;
|
|
|
|
[ObservableProperty]
|
|
private List<T>? list;
|
|
}
|
|
|
|
[ObservableObject]
|
|
partial class ModelWithCultureAwarePropertyName
|
|
{
|
|
// This starts with "i" as it's one of the characters that can change when converted to uppercase.
|
|
// For instance, when using the Turkish language pack, this would become "İnputFolder" if done wrong.
|
|
[ObservableProperty]
|
|
private int _inputFolder;
|
|
}
|
|
|
|
[ObservableRecipient]
|
|
public sealed partial class ModelWithNotifyPropertyChangedRecipientsAndDisplayAttributeLast : ObservableValidator
|
|
{
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedRecipients]
|
|
[Display(Name = "Foo bar baz")]
|
|
private object? _someProperty;
|
|
}
|
|
|
|
public abstract partial class BaseModelWithObservablePropertyAttribute : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private bool canSave;
|
|
|
|
public abstract void Save();
|
|
}
|
|
|
|
public partial class InheritedModelWithCommandUsingInheritedObservablePropertyForCanExecute : BaseModelWithObservablePropertyAttribute
|
|
{
|
|
[RelayCommand(CanExecute = nameof(CanSave))]
|
|
public override void Save()
|
|
{
|
|
}
|
|
}
|
|
|
|
public partial class ModelWithAdditionalDataAnnotationAttributes : ObservableValidator
|
|
{
|
|
[ObservableProperty]
|
|
[Display(Name = "MyProperty", ResourceType = typeof(List<double>), Prompt = "Foo bar baz")]
|
|
[Key]
|
|
[Editable(true)]
|
|
[UIHint("MyControl", "WPF", new object[] { "Foo", 42, "Bar", 3.14, "Baz", "Hello" })]
|
|
[ScaffoldColumn(true)]
|
|
private string? name;
|
|
}
|
|
|
|
public partial class ModelWithOverriddenCommandMethodFromExternalBaseModel : ModelWithObservablePropertyAndMethod
|
|
{
|
|
public bool HasSaved { get; private set; }
|
|
|
|
[RelayCommand(CanExecute = nameof(CanSave))]
|
|
public override void Save()
|
|
{
|
|
HasSaved = true;
|
|
}
|
|
}
|
|
}
|