implemented creation of TerminalViewModel Tabs with DI,

therefore added TerminalViewModelFactory,
revisited StartupHelpers (mainly added AbstractFactory)
master
Jonas Arnold 3 years ago
parent b862b61599
commit f869d217ac
  1. 6
      Common/StartupHelpers/AbstractFactory.cs
  2. 20
      Common/StartupHelpers/ServiceExtensions.cs
  3. 10
      MultiTerm.Core/Factories/ITerminalViewModelFactory.cs
  4. 51
      MultiTerm.Core/Factories/TerminalViewModelFactory.cs
  5. 27
      MultiTerm.Core/Helpers/ServiceExtensions.cs
  6. 2
      MultiTerm.Core/ViewModel/ITerminalViewModel.cs
  7. 12
      MultiTerm.Core/ViewModel/SendReceiveViewModel.cs
  8. 14
      MultiTerm.Core/ViewModel/ShellViewModel.cs
  9. 7
      MultiTerm.Core/ViewModel/TerminalViewModel.cs
  10. 5
      MultiTerm.Wpf/App.xaml.cs
  11. 11
      MultiTerm.Wpf/View/ShellView.xaml

@ -7,16 +7,16 @@ public class AbstractFactory<T> : IAbstractFactory<T>
/// <summary> /// <summary>
/// Constructor for Abstract Factory. /// Constructor for Abstract Factory.
/// </summary> /// </summary>
/// <param name="factory">Function of the factory</param> /// <param name="factory">factory function</param>
public AbstractFactory(Func<T> factory) public AbstractFactory(Func<T> factory)
{ {
this.factory = factory; this.factory = factory;
} }
/// <summary> /// <summary>
/// Executes factory function and returns it. /// Executes factory function and returns instanciated object.
/// </summary> /// </summary>
/// <returns>Factory</returns> /// <returns>created object</returns>
public T Create() public T Create()
{ {
return factory(); return factory();

@ -4,6 +4,24 @@ namespace Common.StartupHelpers;
public static class ServiceExtensions public static class ServiceExtensions
{ {
/// <summary>
/// Service Extension to add an <see cref="AbstractFactory{T}"/> to Dependency Injection.
/// </summary>
/// <typeparam name="TInterface">Type of interface</typeparam>
/// <typeparam name="TImplementation">Type of implementation</typeparam>
/// <param name="services">existing service collection</param>
public static void AddAbstractFactory<TInterface, TImplementation>(this IServiceCollection services)
where TInterface : class
where TImplementation : class, TInterface
{
// add implementation to services collection
services.AddTransient<TInterface, TImplementation>();
// add factory function to services collection
services.AddSingleton<Func<TInterface>>(x => () => x.GetService<TInterface>()!);
// add factory implementation to services collection
services.AddSingleton<IAbstractFactory<TInterface>, AbstractFactory<TInterface>>();
}
/// <summary> /// <summary>
/// Adds a factory for subcontrols to be created. /// Adds a factory for subcontrols to be created.
/// For example a UserControl can be instanciated using this Service. /// For example a UserControl can be instanciated using this Service.
@ -14,7 +32,7 @@ public static class ServiceExtensions
{ {
// add to DI: the subcontrol itself // add to DI: the subcontrol itself
services.AddTransient<TSubControl>(); services.AddTransient<TSubControl>();
// add to DI: Func of that subcontrol, that will create the subcontrol // add to DI: Func of constructor of that subcontrol
services.AddSingleton<Func<TSubControl>>(x => () => x.GetService<TSubControl>()!); services.AddSingleton<Func<TSubControl>>(x => () => x.GetService<TSubControl>()!);
// add to DI: Factory for the subcontrol // add to DI: Factory for the subcontrol
services.AddSingleton<IAbstractFactory<TSubControl>, AbstractFactory<TSubControl>>(); services.AddSingleton<IAbstractFactory<TSubControl>, AbstractFactory<TSubControl>>();

@ -0,0 +1,10 @@
using MultiTerm.Core.Common;
using MultiTerm.Core.ViewModel;
namespace MultiTerm.Core.Factories
{
public interface ITerminalViewModelFactory
{
ITerminalViewModel Create(TerminalViewType viewType, ProtocolType protocolType);
}
}

@ -0,0 +1,51 @@
using MultiTerm.Core.Common;
using MultiTerm.Core.ViewModel;
namespace MultiTerm.Core.Factories;
/// <summary>
/// Can create <see cref="ITerminalViewModel"/> instances during runtime.
/// </summary>
public class TerminalViewModelFactory : ITerminalViewModelFactory
{
private readonly Func<IEnumerable<ITerminalViewModel>> factory;
/// <summary>
/// Constructor of <see cref="TerminalViewModelFactory"/>.
/// </summary>
/// <param name="factory">function that gets all registered services with Interface <see cref="ITerminalViewModel"/></param>
public TerminalViewModelFactory(Func<IEnumerable<ITerminalViewModel>> factory)
{
this.factory = factory;
}
/// <summary>
/// Creates a new <see cref="ITerminalViewModel"/>.
/// </summary>
/// <param name="viewType"><see cref="TerminalViewType"/> of the new <see cref="ITerminalViewModel"/>. Must be set at creation time for all <see cref="ITerminalViewModel"/></param>
/// <param name="protocolType"><see cref="ProtocolType"/> of the new <see cref="ITerminalViewModel"/></param>
/// <returns></returns>
public ITerminalViewModel Create(TerminalViewType viewType, ProtocolType protocolType)
{
ITerminalViewModel output;
// get all registered TerminalViewModels as IEnumerable
var registeredViewModels = factory();
// search for correct type by ViewType which must be set in constructor of all TerminalViewModel Types
try
{
output = registeredViewModels.Where(x => x.ViewType == viewType).First();
}
catch (Exception ex)
{
throw new NotImplementedException($"'{nameof(TerminalViewModelFactory)}' cannot create Terminal with {nameof(TerminalViewType)} {viewType}", ex);
}
// immediately initialize protocol type
output.ProtocolType = protocolType;
// return the filled up instance
return output;
}
}

@ -0,0 +1,27 @@
using Microsoft.Extensions.DependencyInjection;
using MultiTerm.Core.Factories;
using MultiTerm.Core.ViewModel;
namespace MultiTerm.Core.Helpers;
public static class ServiceExtensions
{
/// <summary>
/// Service Extension to add all implementations of <see cref="ITerminalViewModel"/> to Dependency Injection.
/// Also adds <see cref="TerminalViewModelFactory"/> to Dependency injection, which allows instanciation of <see cref="ITerminalViewModel"/> during runtime.
/// </summary>
/// <param name="services">existing services collection</param>
public static void AddTerminalViewModelFactory(this IServiceCollection services)
{
// add all implementations to the services collection
services.AddTransient<ITerminalViewModel, SendReceiveViewModel>();
// TODO extend
// add a function to the services collection, which is used by the TerminalViewModelFactory
// to get a list of all registered implementations of ITerminalViewModel
services.AddSingleton<Func<IEnumerable<ITerminalViewModel>>>(x => () => x.GetService<IEnumerable<ITerminalViewModel>>()!);
// add the factory itself to the services collection
services.AddSingleton<ITerminalViewModelFactory, TerminalViewModelFactory>();
}
}

@ -17,7 +17,7 @@ public interface ITerminalViewModel
/// <summary> /// <summary>
/// Type of Protocol. /// Type of Protocol.
/// </summary> /// </summary>
ProtocolType ProtocolType { get; } ProtocolType ProtocolType { get; set; }
/// <summary> /// <summary>
/// Request Closing of Terminal. /// Request Closing of Terminal.

@ -4,12 +4,14 @@ namespace MultiTerm.Core.ViewModel;
public partial class SendReceiveViewModel : TerminalViewModel public partial class SendReceiveViewModel : TerminalViewModel
{ {
public override string Title => "SendReceive Tab"; public override string Title
public override TerminalViewType ViewType => TerminalViewType.SendReceive;
public SendReceiveViewModel() : base(ProtocolType.Serial) // TODO implement Protocol initialization
{ {
get
{
return $"{ViewType} {ProtocolType}";
}
} }
public override TerminalViewType ViewType => TerminalViewType.SendReceive;
} }

@ -2,14 +2,13 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using MultiTerm.Core.Common; using MultiTerm.Core.Common;
using MultiTerm.Core.Factories;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
namespace MultiTerm.Core.ViewModel; namespace MultiTerm.Core.ViewModel;
public partial class ShellViewModel : ObservableObject public partial class ShellViewModel : ObservableObject
{ {
private readonly IAbstractFactory<SendReceiveViewModel> sendReceiveViewModelFactory;
[ObservableProperty] [ObservableProperty]
private string title = "ShellView Test"; private string title = "ShellView Test";
@ -35,17 +34,17 @@ public partial class ShellViewModel : ObservableObject
#endregion #endregion
public ShellViewModel(IAbstractFactory<SendReceiveViewModel> sendReceiveViewModelFactory) private readonly ITerminalViewModelFactory terminalViewModelFactory;
public ShellViewModel(ITerminalViewModelFactory terminalViewModelFactory)
{ {
this.sendReceiveViewModelFactory = sendReceiveViewModelFactory; this.terminalViewModelFactory = terminalViewModelFactory;
// create a new terminal
this.AppendConfiguredTerminal(this.sendReceiveViewModelFactory.Create());
} }
[RelayCommand] [RelayCommand]
private void AppendTerminalWithSelectedViewType(ProtocolType protocolType) private void AppendTerminalWithSelectedViewType(ProtocolType protocolType)
{ {
Console.WriteLine($"Type = {protocolType}"); this.AppendConfiguredTerminal(this.terminalViewModelFactory.Create(this.SelectedTerminalViewType, protocolType));
} }
@ -57,6 +56,7 @@ public partial class ShellViewModel : ObservableObject
// add to collection and register closing event handler // add to collection and register closing event handler
this.TerminalViewModels.Add(newTerminal); this.TerminalViewModels.Add(newTerminal);
newTerminal.ClosingEvent += Terminal_ClosingEvent; newTerminal.ClosingEvent += Terminal_ClosingEvent;
// set as selected // set as selected
this.SelectedTerminalViewModel = newTerminal; this.SelectedTerminalViewModel = newTerminal;
} }

@ -8,15 +8,10 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
{ {
public abstract string Title { get; } public abstract string Title { get; }
public abstract TerminalViewType ViewType { get; } public abstract TerminalViewType ViewType { get; }
public ProtocolType ProtocolType { get; private set; } public ProtocolType ProtocolType { get; set; }
public event EventHandler? ClosingEvent; public event EventHandler? ClosingEvent;
public TerminalViewModel(ProtocolType protocolType)
{
this.ProtocolType = protocolType;
}
/// <summary> /// <summary>
/// Method to override if any closing actions are required. /// Method to override if any closing actions are required.
/// Closing can be cancelled using the return value. /// Closing can be cancelled using the return value.

@ -4,6 +4,7 @@ using MultiTerm.Core.ViewModel;
using Common.StartupHelpers; using Common.StartupHelpers;
using System.Windows; using System.Windows;
using MultiTerm.Core.Common; using MultiTerm.Core.Common;
using MultiTerm.Core.Helpers;
namespace MultiTerm.Wpf; namespace MultiTerm.Wpf;
@ -22,8 +23,8 @@ public partial class App : Application
// viewmodels // viewmodels
services.AddSingleton<ShellViewModel>(); services.AddSingleton<ShellViewModel>();
// subcontrols // application specific
services.AddSubControlFactory<SendReceiveViewModel>(); services.AddTerminalViewModelFactory();
}) })
.Build(); .Build();
} }

@ -95,17 +95,6 @@
Command="{Binding Data.AppendTerminalWithSelectedViewTypeCommand, Source={StaticResource proxy}}" Command="{Binding Data.AppendTerminalWithSelectedViewTypeCommand, Source={StaticResource proxy}}"
CommandParameterType="{x:Type core_common:ProtocolType}"> CommandParameterType="{x:Type core_common:ProtocolType}">
</controls:CommandableSubMenu> </controls:CommandableSubMenu>
<!--<MenuItem Header="Protocol" IsEnabled="False"/>
<MenuItem Header="Serial"
Command="{Binding Data.AppendTerminalCommand, Source={StaticResource proxy}}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}">
</MenuItem>
<MenuItem Header="USB HID" />
<MenuItem Header="TCP" />
<MenuItem Header="UCP" />-->
</ContextMenu> </ContextMenu>
</Button.ContextMenu> </Button.ContextMenu>

Loading…
Cancel
Save