RSCG – mvvmgen
| name | mvvmgen |
| nuget | https://www.nuget.org/packages/mvvmgen/ |
| link | https://github.com/thomasclaudiushuber/mvvmgen |
| author | Thomas Claudius Huber |
Generate MVVM boilerplate code
This is how you can use mvvmgen .
The code that you start with is
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MvvmGen.PureCodeGeneration" Version="1.4.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>
The code that you will use is
using PropChangeDemo; Person person = new (); person.FirstName = "Andrei"; Console.WriteLine (person.FirstName);
using MvvmGen;
namespace PropChangeDemo;
[ViewModel]
partial class Person
{
[Property] private string _FirstName;
}
The code that is generated is
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System;
namespace MvvmGen
{
/// <summary>
/// Specifies that a DelegateCommand property in the ViewModel should be generated for a method. Set this attribute on methods of a class that has the <see cref="ViewModelAttribute"/> set.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CommandAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="CommandAttribute"/> class.
/// </summary>
public CommandAttribute() { }
/// <summary>
/// Initializes a new instance of the <see cref="CommandAttribute"/> class.
/// </summary>
/// <param name="canExecuteMethod">The name of the method with the can-execute logic</param>
public CommandAttribute(string canExecuteMethod)
{
CanExecuteMethod = canExecuteMethod;
}
/// <summary>
/// Gets or sets the name of the method with the can-execute logic.
/// </summary>
public string? CanExecuteMethod { get; set; }
/// <summary>
/// Gets or sets the name of the command property.
/// </summary>
public string? PropertyName { get; set; }
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System;
using System.Linq;
using MvvmGen.Commands;
namespace MvvmGen
{
/// <summary>
/// Specifies a property, in which the <see cref="DelegateCommand.RaiseCanExecuteChanged"/> method of a DelegateCommand is called to refresh controls in the UI that are using the <see cref="DelegateCommand"/>. Set this one or more instances of this attribute on the execute or can-execute method of a command that you have defined with the <see cref="CommandAttribute"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class CommandInvalidateAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="CommandInvalidateAttribute"/> class.
/// </summary>
/// <param name="propertyName">The name of the property in which the DelegateCommand's <see cref="DelegateCommand.RaiseCanExecuteChanged"/> method is called</param>
/// <param name="morePropertyNames">More properties in which the DelegateCommand's <see cref="DelegateCommand.RaiseCanExecuteChanged"/> method is called</param>
public CommandInvalidateAttribute(string propertyName, params string[] morePropertyNames)
{
PropertyNames = new[] { propertyName }.Concat(morePropertyNames).ToArray();
}
/// <summary>
/// Gets the property names in which the DelegateCommand's <see cref="DelegateCommand.RaiseCanExecuteChanged"/> method is called.
/// </summary>
public string[] PropertyNames { get; }
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System;
namespace MvvmGen
{
/// <summary>
/// Specifies that a type is injected into a ViewModel. Generates a constructor parameter and initializes a property with the injected type. Set this attribute on a class that has the <see cref="ViewModelAttribute"/> set.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class InjectAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="InjectAttribute"/> class.
/// </summary>
/// <param name="type">The type that is injected into the ViewModel.</param>
public InjectAttribute(Type type)
{
Type = type;
}
/// <summary>
/// Initializes a new instance of the <see cref="InjectAttribute"/> class.
/// </summary>
/// <param name="type">The type that is injected into the ViewModel.</param>
/// <param name="propertyName">The name of the property that stores the injected type.</param>
public InjectAttribute(Type type, string propertyName)
{
Type = type;
PropertyName = propertyName;
}
/// <summary>
/// Gets the type that is injected into the ViewModel.
/// </summary>
public Type Type { get; }
/// <summary>
/// Gets or sets the name of the property that stores the injected type.
/// </summary>
public string? PropertyName { get; set; }
/// <summary>
/// Gets or sets the access modifier of the property that stores the injected type.
/// </summary>
public AccessModifier PropertyAccessModifier { get; set; }
}
public enum AccessModifier
{
Private = 1,
ProtectedInternal = 2,
Protected = 3,
Internal = 4,
Public = 5
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System;
namespace MvvmGen
{
/// <summary>
/// Specifies that a property in the ViewModel should be generated for a field. Set this attribute on a field of a class that has the <see cref="ViewModelAttribute"/> set.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class PropertyAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="PropertyAttribute"/> class.
/// </summary>
public PropertyAttribute() { }
/// <summary>
/// Initializes a new instance of the <see cref="PropertyAttribute"/> class.
/// </summary>
/// <param name="propertyName">The name of the property to generate</param>
public PropertyAttribute(string propertyName)
{
PropertyName = propertyName;
}
/// <summary>
/// Gets or sets the name of property to generate.
/// </summary>
public string? PropertyName { get; set; }
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System;
namespace MvvmGen
{
/// <summary>
/// Specifies that a method should be called in the setter of a generated property. Set this attribute a field that has the <see cref="PropertyAttribute"/> set.
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class PropertyCallMethodAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="PropertyCallMethodAttribute"/> class.
/// </summary>
/// <param name="methodName">The method to call</param>
public PropertyCallMethodAttribute(string methodName)
{
MethodName = methodName;
}
/// <summary>
/// Gets or sets the method to call.
/// </summary>
public string MethodName { get; }
/// <summary>
/// Gets or sets the method arguments that are passed to the method that is called. As the method is called in the setter of a property, you can specify for example "value" to pass the value of the property as an argument to the method.
/// </summary>
public string? MethodArgs { get; set; }
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System;
using System.ComponentModel;
using System.Linq;
namespace MvvmGen
{
/// <summary>
/// Set this attribute once or multiple times on a readonly property that depends on another property.
/// Typical case is you define a FullName property in your ViewModel that depends on FirstName and LastName.
/// You would create it like this:
/// <code>
/// [PropertyInvalidate(nameof(LastName))]
/// [PropertyInvalidate(nameof(FirstName))]
/// public string FullName => $"{FirstName} {LastName}";
/// </code>
/// Then the <see cref="INotifyPropertyChanged.PropertyChanged"/> event is raised for the FullName property in the setters of the properties FirstName and LastName.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class PropertyInvalidateAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="PropertyInvalidateAttribute"/> class.
/// </summary>
/// <param name="propertyName">The name of the property in which the <see cref="INotifyPropertyChanged.PropertyChanged"/> event is raised.</param>
/// /// <param name="morePropertyNames">More properties in which the <see cref="INotifyPropertyChanged.PropertyChanged"/> event is raised.</param>
public PropertyInvalidateAttribute(string propertyName, params string[] morePropertyNames)
{
PropertyNames = new[] { propertyName }.Concat(morePropertyNames).ToArray();
}
/// <summary>
/// Gets the property names in which the <see cref="INotifyPropertyChanged.PropertyChanged"/> event is raised.
/// </summary>
public string[] PropertyNames { get; }
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System;
using MvvmGen.Events;
namespace MvvmGen
{
/// <summary>
/// Specifies that an event should be published in the setter of a generated property. Set this attribute a field that has the <see cref="PropertyAttribute"/> set.
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class PropertyPublishEventAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="PropertyPublishEventAttribute"/> class.
/// </summary>
/// <param name="eventType">The event type to publish</param>
public PropertyPublishEventAttribute(Type eventType)
{
EventType = eventType;
}
/// <summary>
/// Gets or sets the event type to publish
/// </summary>
public Type EventType { get; }
/// <summary>
/// Gets or sets the constructor arguments that are passed to the constructor of the <see cref="EventType"/> class. As the event is published in the setter of a property, you can specify for example <code>"value"</code> to pass the value of the property as an argument to the constructor of the event, or you can specify <code>"value?.Id, value?.FirstName"</code> to pass two arguments to the event constructor.
/// </summary>
public string? EventConstructorArgs { get; set; }
/// <summary>
/// Gets or sets the name of the member that contains the <see cref="IEventAggregator"/> instance. The default value is "EventAggregator".
/// </summary>
public string EventAggregatorMemberName { get; set; } = "EventAggregator";
/// <summary>
/// Gets or sets a condition that must be met to publish the event. Pass for example in a string like <code>"value is not null"</code>
/// </summary>
public string? PublishCondition { get; set; }
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System;
namespace MvvmGen
{
/// <summary>
/// Specifies that a class is a ViewModel. With this attribute set on a class, a partial class definition is generated.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class ViewModelAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="ViewModelAttribute"/> class.
/// </summary>
public ViewModelAttribute() { }
/// <summary>
/// Initializes a new instance of the <see cref="ViewModelAttribute"/> class.
/// </summary>
/// <param name="modelType">The type of the model that is wrapped by the ViewModel. All properties of the model type will be generated in the ViewModel.</param>
public ViewModelAttribute(Type modelType)
{
ModelType = modelType;
}
/// <summary>
/// Gets or sets the type of the model that is wrapped by the ViewModel. All properties of the model type will be generated in the ViewModel.
/// </summary>
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
/// </summary>
public string? ModelPropertyName { get; set; }
/// <summary>
/// Gets or sets a comma separated list of model properties that should not be generated
/// in the ViewModel for the model that you specified with the <see cref="ModelType"/> property.
/// </summary>
public string? ModelPropertiesToIgnore { get; set; }
/// <summary>
/// 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; }
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System;
using MvvmGen.ViewModels;
namespace MvvmGen
{
/// <summary>
/// Specifies that an <see cref="IViewModelFactory{T}"/> is generated, where T is your ViewModel class. Set this attribute on a class that has the <see cref="ViewModelAttribute"/> set.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class ViewModelGenerateFactoryAttribute : Attribute
{
/// <summary>
/// Gets or sets the name of the generated factory class.
/// </summary>
public string? ClassName { get; set; }
/// <summary>
/// Gets or sets the name of the generated factory interface.
/// </summary>
public string? InterfaceName { get; set; }
/// <summary>
/// Gets or sets the return type of the Create method. By default, this will be
/// - either the ViewModel type
/// - or the generated interface by the ViewModelGenerateInterface attribute
/// But you can explicitly specify here a custom return type if needed.
/// </summary>
public Type? ReturnType { get; set; }
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System;
namespace MvvmGen
{
/// <summary>
/// Specifies that an interface is generated for your ViewModel class.
/// Set this attribute on a class that has the <see cref="ViewModelAttribute"/> set,
/// and then the generated ViewModel will automatically implement the generated interface
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class ViewModelGenerateInterfaceAttribute : Attribute
{
/// <summary>
/// Gets or sets the name of the generated interface.
/// </summary>
public string? InterfaceName { get; set; }
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System;
using System.Windows.Input;
namespace MvvmGen.Commands
{
/// <summary>
/// An <see cref="ICommand"/> implementation that works with delegates for the execute and can-execute logic
/// </summary>
public class DelegateCommand : IDelegateCommand
{
private readonly Action<object?> _execute;
private readonly Func<object?, bool>? _canExecute;
public DelegateCommand(Action<object?> execute, Func<object?, bool>? canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
/// <inheritdoc/>
public event EventHandler? CanExecuteChanged;
/// <summary>
/// Raises the <see cref="CanExecuteChanged"/> event.
/// </summary>
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
/// <inheritdoc/>
public void Execute(object? parameter) => _execute(parameter);
/// <inheritdoc/>
public bool CanExecute(object? parameter) => _canExecute == null || _canExecute(parameter);
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System.Windows.Input;
namespace MvvmGen.Commands
{
public interface IDelegateCommand : ICommand
{
void RaiseCanExecuteChanged();
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
namespace MvvmGen.Events
{
/// <summary>
/// A class to communicate between loosely coupled objects, like for example ViewModels
/// </summary>
public class EventAggregator : IEventAggregator
{
internal Dictionary<Type, List<WeakReference>> _subscribersByEvent = new();
/// <inheritdoc/>
public void Publish<TEvent>(TEvent eventToPublish)
{
if (eventToPublish is null)
{
throw new ArgumentNullException(nameof(eventToPublish));
}
lock (_subscribersByEvent)
{
if (!_subscribersByEvent.ContainsKey(typeof(TEvent)))
{
return;
}
var subscribersToRemove = new List<WeakReference>();
foreach (var subscriber in _subscribersByEvent[typeof(TEvent)])
{
if (subscriber.IsAlive)
{
var target = subscriber.Target;
if (target is not null)
{
target.GetType()
.GetMethod(nameof(IEventSubscriber<object>.OnEvent), new[] { typeof(TEvent) })
?.Invoke(target, new object[] { eventToPublish });
}
}
else
{
subscribersToRemove.Add(subscriber);
}
}
foreach (var subscriber in subscribersToRemove)
{
_subscribersByEvent[typeof(TEvent)].Remove(subscriber);
}
}
}
/// <inheritdoc/>
public void RegisterSubscriber<TSubscriber>(TSubscriber subscriber)
{
if (subscriber is null)
{
throw new ArgumentNullException(nameof(subscriber));
}
var subscriberInterfaces = typeof(TSubscriber).GetInterfaces()
.Where(t => t.IsGenericType && t.FullName?.StartsWith("MvvmGen.Events.IEventSubscriber") == true).ToList();
if (!subscriberInterfaces.Any())
{
return;
}
var weakReference = new WeakReference(subscriber);
var eventTypes = subscriberInterfaces.SelectMany(x => x.GenericTypeArguments).Distinct();
lock (_subscribersByEvent)
{
foreach (var eventType in eventTypes)
{
if (!_subscribersByEvent.ContainsKey(eventType))
{
_subscribersByEvent.Add(eventType, new());
}
if (!_subscribersByEvent[eventType].Any(x => x.IsAlive && x.Target?.Equals(subscriber) == true))
{
_subscribersByEvent[eventType].Add(weakReference);
}
}
}
}
/// <summary>
/// Unregisters an MvvmGen.Events.IEventSubscriber, so that it won't receive events anymore from the IEventAggregator instance.
/// Note that calling this method is optional for an instance of this <see cref="EventAggregator"/> class, because the
/// <see cref="EventAggregator"/> stores a subscriber internally in a <see cref="WeakReference"/>, which means
/// the subscriber can get garbage collected, even if you don't call this UnregisterSubscriber method.
/// Calling this method though will immediately unregister a subscriber, even before it got garbage collected.
/// </summary>
/// <typeparam name="TSubscriber">The subscriber type</typeparam>
/// <param name="subscriber">The subscriber instance to unregister</param>
public void UnregisterSubscriber<TSubscriber>(TSubscriber subscriber)
{
if (subscriber is null)
{
throw new ArgumentNullException(nameof(subscriber));
}
lock (_subscribersByEvent)
{
foreach (var subscribersByEvent in _subscribersByEvent)
{
var subscribersToRemove = new List<WeakReference>();
foreach (var weakReference in subscribersByEvent.Value)
{
if (!weakReference.IsAlive
|| weakReference.Target?.Equals(subscriber) == true)
{
subscribersToRemove.Add(weakReference);
}
}
foreach (var subscriberToRemove in subscribersToRemove)
{
subscribersByEvent.Value.Remove(subscriberToRemove);
}
}
}
}
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
namespace MvvmGen.Events
{
/// <summary>
/// Provides methods to communicate between loosely coupled objects.
/// </summary>
public interface IEventAggregator
{
/// <summary>
/// Publishes an event.
/// </summary>
/// <typeparam name="TEvent">The event type to publish</typeparam>
/// <param name="eventToPublish">The event instance to publish</param>
void Publish<TEvent>(TEvent eventToPublish);
/// <summary>
/// Registers an MvvmGen.Events.IEventSubscriber, so that it will receive events from the <see cref="IEventAggregator"/> instance.
/// </summary>
/// <typeparam name="TSubscriber">The subscriber type</typeparam>
/// <param name="subscriber">The subscriber instance to register</param>
void RegisterSubscriber<TSubscriber>(TSubscriber subscriber);
/// <summary>
/// Unregisters an <see cref="IEventSubscriber{TEvent}"/>, so that it won't receive events anymore from the <see cref="IEventAggregator"/> instance.
/// </summary>
/// <typeparam name="TSubscriber">The subscriber type</typeparam>
/// <param name="subscriber">The subscriber instance to unregister</param>
void UnregisterSubscriber<TSubscriber>(TSubscriber subscriber);
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
namespace MvvmGen.Events
{
/// <summary>
/// Subscribes to an event of an <see cref="IEventAggregator"/>.
/// </summary>
/// <typeparam name="TEvent">The event to subscribe</typeparam>
public interface IEventSubscriber<TEvent>
{
/// <summary>
/// Gets called when the event was published
/// </summary>
/// <param name="eventData">The event instance</param>
void OnEvent(TEvent eventData);
}
/// <summary>
/// Subscribes to events of an <see cref="IEventAggregator"/>.
/// </summary>
/// <typeparam name="TEvent1">The first event to subscribe</typeparam>
/// <typeparam name="TEvent2">The second event to subscribe</typeparam>
public interface IEventSubscriber<TEvent1, TEvent2> : IEventSubscriber<TEvent1>
{
/// <summary>
/// Gets called when the event was published
/// </summary>
/// <param name="eventData">The event instance</param>
void OnEvent(TEvent2 eventData);
}
/// <summary>
/// Subscribes to events of an <see cref="IEventAggregator"/>.
/// </summary>
/// <typeparam name="TEvent1">The first event to subscribe</typeparam>
/// <typeparam name="TEvent2">The second event to subscribe</typeparam>
/// <typeparam name="TEvent3">The third event to subscribe</typeparam>
public interface IEventSubscriber<TEvent1, TEvent2, TEvent3> : IEventSubscriber<TEvent1, TEvent2>
{
/// <summary>
/// Gets called when the event was published
/// </summary>
/// <param name="eventData">The event instance</param>
void OnEvent(TEvent3 eventData);
}
/// <summary>
/// Subscribes to events of an <see cref="IEventAggregator"/>.
/// </summary>
/// <typeparam name="TEvent1">The first event to subscribe</typeparam>
/// <typeparam name="TEvent2">The second event to subscribe</typeparam>
/// <typeparam name="TEvent3">The third event to subscribe</typeparam>
/// <typeparam name="TEvent4">The fourth event to subscribe</typeparam>
public interface IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4> : IEventSubscriber<TEvent1, TEvent2, TEvent3>
{
/// <summary>
/// Gets called when the event was published
/// </summary>
/// <param name="eventData">The event instance</param>
void OnEvent(TEvent4 eventData);
}
/// <summary>
/// Subscribes to events of an <see cref="IEventAggregator"/>.
/// </summary>
/// <typeparam name="TEvent1">The first event to subscribe</typeparam>
/// <typeparam name="TEvent2">The second event to subscribe</typeparam>
/// <typeparam name="TEvent3">The third event to subscribe</typeparam>
/// <typeparam name="TEvent4">The fourth event to subscribe</typeparam>
/// <typeparam name="TEvent5">The fifth event to subscribe</typeparam>
public interface IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4, TEvent5> : IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4>
{
/// <summary>
/// Gets called when the event was published
/// </summary>
/// <param name="eventData">The event instance</param>
void OnEvent(TEvent5 eventData);
}
/// <summary>
/// Subscribes to events of an <see cref="IEventAggregator"/>.
/// </summary>
/// <typeparam name="TEvent1">The first event to subscribe</typeparam>
/// <typeparam name="TEvent2">The second event to subscribe</typeparam>
/// <typeparam name="TEvent3">The third event to subscribe</typeparam>
/// <typeparam name="TEvent4">The fourth event to subscribe</typeparam>
/// <typeparam name="TEvent5">The fifth event to subscribe</typeparam>
/// <typeparam name="TEvent6">The sixth event to subscribe</typeparam>
public interface IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4, TEvent5, TEvent6> : IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4, TEvent5>
{
/// <summary>
/// Gets called when the event was published
/// </summary>
/// <param name="eventData">The event instance</param>
void OnEvent(TEvent6 eventData);
}
/// <summary>
/// Subscribes to events of an <see cref="IEventAggregator"/>.
/// </summary>
/// <typeparam name="TEvent1">The first event to subscribe</typeparam>
/// <typeparam name="TEvent2">The second event to subscribe</typeparam>
/// <typeparam name="TEvent3">The third event to subscribe</typeparam>
/// <typeparam name="TEvent4">The fourth event to subscribe</typeparam>
/// <typeparam name="TEvent5">The fifth event to subscribe</typeparam>
/// <typeparam name="TEvent6">The sixth event to subscribe</typeparam>
/// <typeparam name="TEvent7">The seventh event to subscribe</typeparam>
public interface IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4, TEvent5, TEvent6, TEvent7> : IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4, TEvent5, TEvent6>
{
/// <summary>
/// Gets called when the event was published
/// </summary>
/// <param name="eventData">The event instance</param>
void OnEvent(TEvent7 eventData);
}
/// <summary>
/// Subscribes to events of an <see cref="IEventAggregator"/>.
/// </summary>
/// <typeparam name="TEvent1">The first event to subscribe</typeparam>
/// <typeparam name="TEvent2">The second event to subscribe</typeparam>
/// <typeparam name="TEvent3">The third event to subscribe</typeparam>
/// <typeparam name="TEvent4">The fourth event to subscribe</typeparam>
/// <typeparam name="TEvent5">The fifth event to subscribe</typeparam>
/// <typeparam name="TEvent6">The sixth event to subscribe</typeparam>
/// <typeparam name="TEvent7">The seventh event to subscribe</typeparam>
/// <typeparam name="TEvent8">The eighth event to subscribe</typeparam>
public interface IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4, TEvent5, TEvent6, TEvent7, TEvent8> : IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4, TEvent5, TEvent6, TEvent7>
{
/// <summary>
/// Gets called when the event was published
/// </summary>
/// <param name="eventData">The event instance</param>
void OnEvent(TEvent8 eventData);
}
/// <summary>
/// Subscribes to events of an <see cref="IEventAggregator"/>.
/// </summary>
/// <typeparam name="TEvent1">The first event to subscribe</typeparam>
/// <typeparam name="TEvent2">The second event to subscribe</typeparam>
/// <typeparam name="TEvent3">The third event to subscribe</typeparam>
/// <typeparam name="TEvent4">The fourth event to subscribe</typeparam>
/// <typeparam name="TEvent5">The fifth event to subscribe</typeparam>
/// <typeparam name="TEvent6">The sixth event to subscribe</typeparam>
/// <typeparam name="TEvent7">The seventh event to subscribe</typeparam>
/// <typeparam name="TEvent8">The eighth event to subscribe</typeparam>
/// <typeparam name="TEvent9">The ninth event to subscribe</typeparam>
public interface IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4, TEvent5, TEvent6, TEvent7, TEvent8, TEvent9> : IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4, TEvent5, TEvent6, TEvent7, TEvent8>
{
/// <summary>
/// Gets called when the event was published
/// </summary>
/// <param name="eventData">The event instance</param>
void OnEvent(TEvent9 eventData);
}
/// <summary>
/// Subscribes to events of an <see cref="IEventAggregator"/>.
/// </summary>
/// <typeparam name="TEvent1">The first event to subscribe</typeparam>
/// <typeparam name="TEvent2">The second event to subscribe</typeparam>
/// <typeparam name="TEvent3">The third event to subscribe</typeparam>
/// <typeparam name="TEvent4">The fourth event to subscribe</typeparam>
/// <typeparam name="TEvent5">The fifth event to subscribe</typeparam>
/// <typeparam name="TEvent6">The sixth event to subscribe</typeparam>
/// <typeparam name="TEvent7">The seventh event to subscribe</typeparam>
/// <typeparam name="TEvent8">The eighth event to subscribe</typeparam>
/// <typeparam name="TEvent9">The ninth event to subscribe</typeparam>
/// <typeparam name="TEvent10">The tenth event to subscribe</typeparam>
public interface IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4, TEvent5, TEvent6, TEvent7, TEvent8, TEvent9, TEvent10> : IEventSubscriber<TEvent1, TEvent2, TEvent3, TEvent4, TEvent5, TEvent6, TEvent7, TEvent8, TEvent9>
{
/// <summary>
/// Gets called when the event was published
/// </summary>
/// <param name="eventData">The event instance</param>
void OnEvent(TEvent10 eventData);
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
namespace MvvmGen.ViewModels
{
/// <summary>
/// Represents a factory that can create ViewModel instances with a parameterless <see cref="Create"/> method.
/// You get a generated implementation of this interface by setting the <see cref="ViewModelGenerateFactoryAttribute"/> on your ViewModel.
/// </summary>
public interface IViewModelFactory<out T>
{
/// <summary>
/// Creates and returns a ViewModel instance.
/// </summary>
/// <returns>The created ViewModel instance</returns>
T Create();
}
}
// ***********************************************************************
// ⚡ MvvmGen => https://github.com/thomasclaudiushuber/mvvmgen
// Copyright © by Thomas Claudius Huber
// Licensed under the MIT license => See LICENSE file in repository root
// ***********************************************************************
#nullable enable
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MvvmGen.ViewModels
{
/// <summary>
/// A base class for view models
/// </summary>
public class ViewModelBase : INotifyPropertyChanged
{
/// <inheritdoc cref="INotifyPropertyChanged.PropertyChanged"/>
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// Raises the <see cref="PropertyChanged"/> event
/// </summary>
/// <param name="e">A <see cref="PropertyChangedEventArgs"/> that contains the name of the changed property.</param>
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
/// <summary>
/// Raises the <see cref="PropertyChanged"/> event
/// </summary>
/// <param name="propertyName">(optional) The name of the changed property.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
InvalidateCommands(propertyName);
}
/// <summary>
/// Invalidates the commands for the changed propertyName
/// </summary>
/// <param name="propertyName">The name of the changed property.</param>
protected virtual void InvalidateCommands(string? propertyName) { }
}
}
// <auto-generated>
// This code was generated for you by
// ⚡ MvvmGen, a tool created by Thomas Claudius Huber (https://www.thomasclaudiushuber.com)
// Generator version: 1.4.0
// </auto-generated>
#nullable enable
using MvvmGen.Commands;
using MvvmGen.Events;
using MvvmGen.ViewModels;
namespace PropChangeDemo
{
partial class Person : global::MvvmGen.ViewModels.ViewModelBase
{
public Person()
{
this.OnInitialize();
}
partial void OnInitialize();
public string FirstName
{
get => _FirstName;
set
{
if (_FirstName != value)
{
_FirstName = value;
OnPropertyChanged("FirstName");
}
}
}
}
}
// <auto-generated>
// Auto-generated by PropertyChanged.SourceGenerator 1.0.8.0
// </auto-generated>
namespace PropertyChanged.SourceGenerator
{
/// <summary>
/// Specifies the accessibility of a generated property getter
/// </summary>
internal enum Getter
{
Public = 6,
ProtectedInternal = 5,
Internal = 4,
Protected = 3,
PrivateProtected = 2,
Private = 1,
}
/// <summary>
/// Specifies the accessibility of a generated property getter
/// </summary>
internal enum Setter
{
Public = 6,
ProtectedInternal = 5,
Internal = 4,
Protected = 3,
PrivateProtected = 2,
Private = 1,
}
/// <summary>
/// Instruct PropertyChanged.SourceGenerator to generate a property which implements INPC using this backing field
/// </summary>
[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false)]
[global::System.Diagnostics.Conditional("DEBUG")]
internal class NotifyAttribute : global::System.Attribute
{
/// <summary>
/// Generate a property whose name is derived from the name of this field, with a public getter and setter
/// </summary>
public NotifyAttribute() { }
/// <summary>
/// Generate a property with the given name, and optionally the given getter and setter accessibilities
/// </summary>
/// <param name="name">Name of the generated property</param>
/// <param name="get">Accessibility of the generated getter</param>
/// <param name="set">Accessibility of the generated setter</param>
public NotifyAttribute(string name, Getter get = Getter.Public, Setter set = Setter.Public) { }
/// <summary>
/// Generate a property whose name is derived from the name of this field, with the given getter and optionally setter accessibilities
/// </summary>
/// <param name="get">Accessibility of the generated getter</param>
/// <param name="set">Accessibility of the generated setter</param>
public NotifyAttribute(Getter get, Setter set = Setter.Public) { }
/// <summary>
/// Generate a property whose name is derived from the name of this field, with a public getter and the given setter accessibility
/// </summary>
/// <param name="set">Accessibility of the generated setter</param>
public NotifyAttribute(Setter set) { }
/// <summary>
/// If <c>true</c>, the generated property will be <c>virtual</c>.
/// </summary>
public bool IsVirtual { get; set; }
}
/// <summary>
/// Instruct PropertyChanged.SourceGenerator to also raise INPC notifications for the named properties whenever the property this is applied to changes
/// </summary>
[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = true)]
[global::System.Diagnostics.Conditional("DEBUG")]
internal class AlsoNotifyAttribute : global::System.Attribute
{
/// <summary>
/// Raise INPC notifications for the given properties when the property generated for this backing field changes
/// </summary>
/// <param name="otherProperties">Other properties to raise INPC notifications for</param>
public AlsoNotifyAttribute(params string[] otherProperties) { }
}
/// <summary>
/// Instruct PropertyChanged.SourceGenerator to raise INPC notifications for this property whenever one of the named generated properties is changed
/// </summary>
[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false)]
[global::System.Diagnostics.Conditional("DEBUG")]
internal class DependsOnAttribute : global::System.Attribute
{
/// <summary>
/// Raise an INPC notification for this property whenever one of the named properties is changed
/// </summary>
/// <param name="dependsOn">Other properties this property depends on</param>
public DependsOnAttribute(params string[] dependsOn) { }
}
/// <summary>
/// Instruct PropertyChanged.SourceGenerator to assign true to this boolean property whenver any generated member changes
/// </summary>
[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = true)]
[global::System.Diagnostics.Conditional("DEBUG")]
internal class IsChangedAttribute : global::System.Attribute
{
}
/// <summary>
/// Specifies an attribute which will be added to the generated property for this backing field
/// </summary>
/// <remarks>
/// The string passed to this attribute will be placed verbatim into the generated code. All types must therefore by fully-qualified.
/// </remarks>
[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = true)]
[global::System.Diagnostics.Conditional("DEBUG")]
internal class PropertyAttributeAttribute : global::System.Attribute
{
/// <summary>
/// Specify an attribute which iwll be added to the generated property for this backing field
/// </summary>
/// <param name="attribute">An attribute to place on the generated property</param>
public PropertyAttributeAttribute(string attribute) { }
}
}
// <auto-generated>
// Auto-generated by PropertyChanged.SourceGenerator 1.0.8.0
// </auto-generated>
namespace PropertyChanged.SourceGenerator.Internal
{
internal static class EventArgsCache
{
private static global::System.ComponentModel.PropertyChangedEventArgs _PropertyChanged_FirstName;
public static global::System.ComponentModel.PropertyChangedEventArgs PropertyChanged_FirstName => _PropertyChanged_FirstName ??= new global::System.ComponentModel.PropertyChangedEventArgs(@"FirstName");
}
}
// <auto-generated>
// Auto-generated by PropertyChanged.SourceGenerator 1.0.8.0
// </auto-generated>
#nullable enable
namespace PropChangeDemo
{
partial class Person : global::System.ComponentModel.INotifyPropertyChanged
{
/// <inheritdoc />
public event global::System.ComponentModel.PropertyChangedEventHandler? PropertyChanged;
public string? FirstName
{
get => this._FirstName;
set
{
if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(value, this._FirstName))
{
this._FirstName = value;
this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_FirstName);
}
}
}
/// <summary>
/// Raises the PropertyChanged event
/// </summary>
/// <param name="eventArgs">The EventArgs to use to raise the event</param>
protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
{
this.PropertyChanged?.Invoke(this, eventArgs);
}
}
}
Code and pdf at
https://ignatandrei.github.io/RSCG_Examples/v2/docs/mvvmgen