cleaned up namespaces:

Common.Logger => Common.Logging
MultiTerm.Core.Common => MultiTerm.Core.Types

added Log Method to ILogger and SerilogLogger,
implemented IAppSettingsProvider and XmlAppSettingsProvider,
added EnumHelpers in Common project
master
Jonas Arnold 3 years ago
parent c01556ba8d
commit 5312b750f5
  1. 24
      Common/AppSettings/AppSetting.cs
  2. 29
      Common/AppSettings/IAppSettingsProvider.cs
  3. 186
      Common/AppSettings/XmlAppSettingsProvider.cs
  4. 15
      Common/Helpers/EnumHelpers.cs
  5. 6
      Common/Logging/ILogger.cs
  6. 51
      Common/Logging/SerilogLogger.cs
  7. 2
      MultiTerm.Core/Factories/ITerminalViewModelFactory.cs
  8. 2
      MultiTerm.Core/Factories/TerminalViewModelFactory.cs
  9. 2
      MultiTerm.Core/Types/NewlineSeparatorType.cs
  10. 2
      MultiTerm.Core/Types/ProtocolType.cs
  11. 2
      MultiTerm.Core/Types/TerminalViewType.cs
  12. 2
      MultiTerm.Core/ViewModel/ITerminalViewModel.cs
  13. 2
      MultiTerm.Core/ViewModel/SendReceiveViewModel.cs
  14. 21
      MultiTerm.Core/ViewModel/ShellViewModel.cs
  15. 2
      MultiTerm.Core/ViewModel/TerminalViewModel.cs
  16. 12
      MultiTerm.Wpf/App.xaml.cs
  17. 4
      MultiTerm.Wpf/MultiTerm.Wpf.csproj
  18. 10
      MultiTerm.Wpf/View/ShellView.xaml

@ -0,0 +1,24 @@
namespace Common.AppSettings;
[Serializable]
public class AppSetting
{
public string? Key { get; set; }
public string? Value { get; set; }
/// <summary>
/// Parameterless constructor for serialization.
/// </summary>
public AppSetting()
{
}
/// <summary>
/// Constructor with Initialization.
/// </summary>
public AppSetting(string key, string value)
{
this.Key = key;
this.Value = value;
}
}

@ -0,0 +1,29 @@
using Common.Logging;
namespace Common.AppSettings;
public interface IAppSettingsProvider
{
/// <summary>
/// Any internal event inside of the App Settings Provider.
/// </summary>
event EventHandler<LogEntry>? LogWorthyEvent;
/// <summary>
/// Retrieves the app settings from the persistant location and loads them into memory.
/// Loading settings will override the currently stored settings in the memory.
/// </summary>
void Load();
/// <summary>
/// Saves the app settings to the persistant location.
/// </summary>
void Save();
bool WriteSetting(string key, string value);
bool TryReadSetting(string key, out string value);
bool TryReadSettingOrAddDefault(string key, out string value, string defaultValue);
}

@ -0,0 +1,186 @@
using Common.Logging;
using System.Xml.Serialization;
namespace Common.AppSettings;
public class XmlAppSettingsProvider : IAppSettingsProvider
{
private readonly string settingsFilePath;
private readonly XmlSerializer serializer;
private List<AppSetting> settings;
public event EventHandler<LogEntry>? LogWorthyEvent;
public XmlAppSettingsProvider(string settingsFilePath)
{
// check for file path validity
// TODO create file if it does not exist yet
if (String.IsNullOrEmpty(settingsFilePath)) { throw new ArgumentNullException(nameof(settingsFilePath)); }
this.settingsFilePath = settingsFilePath;
this.settings = new List<AppSetting>();
this.serializer = new XmlSerializer(typeof(List<AppSetting>));
}
public void Load()
{
// read file by creating a stream. this will throw an exception when the file does not exist.
using var fileStream = new FileStream(this.settingsFilePath, FileMode.Open);
// deserialize and cast to List if not null
var deserializedObj = this.serializer.Deserialize(fileStream);
if(deserializedObj != null)
{
// create log entry if there were already some settings loaded
if(this.settings.Count > 0)
{
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Warn, category: nameof(XmlAppSettingsProvider),
message: $"'{nameof(Load)}'() deserialization overwrote previously loaded settings."));
}
this.settings = (List<AppSetting>)deserializedObj;
}
else
{
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Warn, category: nameof(XmlAppSettingsProvider),
message: $"'{nameof(Load)}'() deserialization resulted in null object. Did not change loaded settings."));
}
}
public void Save()
{
// TODO write log entries for when something goes wrong
TextWriter writer = new StreamWriter(this.settingsFilePath);
this.serializer.Serialize(writer, this.settings);
writer.Close();
}
public bool WriteSetting(string key, string value)
{
// guard empty key or value
if(String.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
if (String.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value));
// get all settings with this key
var foundSettings = this.settings.FindAll(x => x.Key == key);
// check if the key already exists multiple times
if(foundSettings.Count > 1)
{
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Error, category: nameof(XmlAppSettingsProvider),
message: $"'{nameof(WriteSetting)}'() cancelled because {foundSettings.Count} settings with the key '{key}' were found. Expected none or exactly one."));
return false; // cancel
}
// if exactly one is found => remove existing app setting
else if(foundSettings.Count == 1)
{
this.settings.Remove(this.settings.Find(x => x.Key == key)!);
}
// none existing yet => inform via event
else if(foundSettings.Count == 0)
{
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Info, category: nameof(XmlAppSettingsProvider),
message: $"'{nameof(WriteSetting)}'() initialized new setting with key '{key}' and value '{value}'."));
}
// add new setting
this.settings.Add(new AppSetting(key, value));
return true;
}
public bool TryReadSetting(string key, out string value)
{
// guard empty key
if (String.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
// get all settings with this key
var foundSettings = this.settings.FindAll(x => x.Key == key);
// too many values found
if (foundSettings.Count > 1)
{
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Error, category: nameof(XmlAppSettingsProvider),
message: $"'{nameof(TryReadSetting)}'() did find {foundSettings.Count} settings with the key '{key}'. Expected only one."));
value = string.Empty;
return false;
}
// no value found
if (foundSettings.Count == 0)
{
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Error, category: nameof(XmlAppSettingsProvider),
message: $"'{nameof(TryReadSetting)}'() did not find a setting with the key '{key}'."));
value = string.Empty;
return false;
}
// extract correct setting when found exactly one
var setting = foundSettings[0];
if(setting.Value != null)
{
value = setting.Value;
}
// if value is null it was probably an empty string or similar => set it to empty string
else
{
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Warn, category: nameof(XmlAppSettingsProvider),
message: $"'{nameof(TryReadSetting)}'() value of retrieved setting with key '{key}' was null. Returned empty string instead."));
value = string.Empty;
}
return true;
}
public bool TryReadSettingOrAddDefault(string key, out string value, string defaultValue)
{
// guard empty key or defaultValue
if (String.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
if (String.IsNullOrEmpty(defaultValue)) throw new ArgumentNullException(nameof(defaultValue));
// get all settings with this key
var foundSettings = this.settings.FindAll(x => x.Key == key);
// too many values found
if (foundSettings.Count > 1)
{
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Error, category: nameof(XmlAppSettingsProvider),
message: $"'{nameof(TryReadSettingOrAddDefault)}'() did find {foundSettings.Count} settings with the key '{key}'. Expected only one."));
value = string.Empty;
return false;
}
// no value found => initialize with the default value
if (foundSettings.Count == 0)
{
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Info, category: nameof(XmlAppSettingsProvider),
message: $"'{nameof(TryReadSettingOrAddDefault)}'() did not find a setting with the key '{key}'. Adding setting with value '{defaultValue}'."));
this.WriteSetting(key, defaultValue);
value = defaultValue;
return true;
}
// extract correct setting when found exactly one
var setting = foundSettings[0];
if (setting.Value != null)
{
value = setting.Value;
}
// if value is null it was probably an empty string or similar => set it to empty string
else
{
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Warn, category: nameof(XmlAppSettingsProvider),
message: $"'{nameof(TryReadSetting)}'() value of retrieved setting with key '{key}' was null. Returned empty string instead."));
value = string.Empty;
}
return true;
}
}

@ -0,0 +1,15 @@
namespace Common.Helpers;
public static class EnumHelpers
{
/// <summary>
/// Parse enum from string to enum type.
/// </summary>
/// <typeparam name="T">type of the enum</typeparam>
/// <param name="value">value to parse to enum</param>
/// <returns>returns enum object with value</returns>
public static T ParseEnum<T>(string value)
{
return (T)Enum.Parse(typeof(T), value, true);
}
}

@ -43,6 +43,12 @@ public interface ILogger
void SetMinimumLogLevel(LogLevel newMinimumLogLevel); void SetMinimumLogLevel(LogLevel newMinimumLogLevel);
#region Logging methods #region Logging methods
/// <summary>
/// Create a Log entry with a complete <see cref="LogEntry"/> object.
/// </summary>
/// <param name="logEntry"><see cref="LogEntry"/> to log</param>
void Log(LogEntry logEntry);
/// <summary> /// <summary>
/// Create a Log entry with the level <see cref="Logging.LogLevel.Trace"/>. /// Create a Log entry with the level <see cref="Logging.LogLevel.Trace"/>.
/// </summary> /// </summary>

@ -1,19 +1,20 @@
using Common.Logging; using Serilog;
using Serilog;
using Serilog.Core; using Serilog.Core;
using Serilog.Events; using Serilog.Events;
namespace Common.Logger; // alias to prevent conflict own Log method
using SerilogLoggingInstance = Serilog.Log;
namespace Common.Logging;
/// <summary> /// <summary>
/// Implements a Logger that uses the Serilog package to create log entries and distributes them into different sinks. /// Implements a Logger that uses the Serilog package to create log entries and distributes them into different sinks.
/// Implemented Sinks: File (rolling file), Debug (<see cref="System.Diagnostics.Debug"/>). /// Implemented Sinks: File (rolling file), Debug (<see cref="System.Diagnostics.Debug"/>).
/// </summary> /// </summary>
public class SerilogLogger : Logging.ILogger public class SerilogLogger : ILogger
{ {
private readonly LoggingLevelSwitch loggingLevelSwitch; private readonly LoggingLevelSwitch loggingLevelSwitch;
public event EventHandler<NewLogEntryEventArgs>? NewLogEntry; public event EventHandler<NewLogEntryEventArgs>? NewLogEntry;
public LogLevel CurrentMinimumLogLevel { get; private set; } = LogLevel.Undefined; public LogLevel CurrentMinimumLogLevel { get; private set; } = LogLevel.Undefined;
@ -38,7 +39,7 @@ public class SerilogLogger : Logging.ILogger
// create logger instance // create logger instance
if (logToDebug) if (logToDebug)
{ {
Log.Logger = new LoggerConfiguration() SerilogLoggingInstance.Logger = new LoggerConfiguration()
.WriteTo.Debug() .WriteTo.Debug()
.WriteTo.File(logFilePath, .WriteTo.File(logFilePath,
rollingInterval: RollingInterval.Day, rollingInterval: RollingInterval.Day,
@ -48,7 +49,7 @@ public class SerilogLogger : Logging.ILogger
} }
else else
{ {
Log.Logger = new LoggerConfiguration() SerilogLoggingInstance.Logger = new LoggerConfiguration()
.WriteTo.File(logFilePath, .WriteTo.File(logFilePath,
rollingInterval: RollingInterval.Day, rollingInterval: RollingInterval.Day,
levelSwitch: loggingLevelSwitch, levelSwitch: loggingLevelSwitch,
@ -87,10 +88,12 @@ public class SerilogLogger : Logging.ILogger
public void StopLogging() public void StopLogging()
{ {
Log.CloseAndFlush(); SerilogLoggingInstance.CloseAndFlush();
} }
private void CreateLogEntry(LogEntry logEntry)
#region Logging methods
public void Log(LogEntry logEntry)
{ {
var serilogEventLevel = this.ConvertGenericToSerilogLogLevel(logEntry.LogLevel); var serilogEventLevel = this.ConvertGenericToSerilogLogLevel(logEntry.LogLevel);
@ -98,31 +101,31 @@ public class SerilogLogger : Logging.ILogger
switch (serilogEventLevel) switch (serilogEventLevel)
{ {
case LogEventLevel.Verbose: case LogEventLevel.Verbose:
Log.Verbose(logEntry.ToString()); SerilogLoggingInstance.Verbose(logEntry.ToString());
break; break;
case LogEventLevel.Debug: case LogEventLevel.Debug:
Log.Debug(logEntry.ToString()); SerilogLoggingInstance.Debug(logEntry.ToString());
break; break;
case LogEventLevel.Information: case LogEventLevel.Information:
Log.Information(logEntry.ToString()); SerilogLoggingInstance.Information(logEntry.ToString());
break; break;
case LogEventLevel.Warning: case LogEventLevel.Warning:
Log.Warning(logEntry.ToString()); SerilogLoggingInstance.Warning(logEntry.ToString());
break; break;
case LogEventLevel.Error: case LogEventLevel.Error:
Log.Error(logEntry.ToString()); SerilogLoggingInstance.Error(logEntry.ToString());
break; break;
case LogEventLevel.Fatal: case LogEventLevel.Fatal:
Log.Error(logEntry.ToString()); SerilogLoggingInstance.Error(logEntry.ToString());
break; break;
default: default:
throw new NotImplementedException($"'{nameof(CreateLogEntry)}()' does not contain an implementation for {nameof(LogEventLevel)} {serilogEventLevel}."); throw new NotImplementedException($"'{nameof(Log)}()' does not contain an implementation for {nameof(LogEventLevel)} {serilogEventLevel}.");
} }
// Raise event (only if the log is wanted due to the configured log level) // Raise event (only if the log is wanted due to the configured log level)
@ -132,41 +135,39 @@ public class SerilogLogger : Logging.ILogger
} }
} }
#region Logging methods
public void LogTrace(string message, string category = "") public void LogTrace(string message, string category = "")
{ {
this.CreateLogEntry(new LogEntry { Category = category, LogLevel = LogLevel.Trace, Message = message }); this.Log(new LogEntry { Category = category, LogLevel = LogLevel.Trace, Message = message });
} }
public void LogDebug(string message, string category = "") public void LogDebug(string message, string category = "")
{ {
this.CreateLogEntry(new LogEntry { Category = category, LogLevel = LogLevel.Debug, Message = message }); this.Log(new LogEntry { Category = category, LogLevel = LogLevel.Debug, Message = message });
} }
public void LogInfo(string message, string category = "") public void LogInfo(string message, string category = "")
{ {
this.CreateLogEntry(new LogEntry { Category = category, LogLevel = LogLevel.Info, Message = message }); this.Log(new LogEntry { Category = category, LogLevel = LogLevel.Info, Message = message });
} }
public void LogWarn(string message, string category = "") public void LogWarn(string message, string category = "")
{ {
this.CreateLogEntry(new LogEntry { Category = category, LogLevel = LogLevel.Warn, Message = message }); this.Log(new LogEntry { Category = category, LogLevel = LogLevel.Warn, Message = message });
} }
public void LogError(string message, string category = "") public void LogError(string message, string category = "")
{ {
this.CreateLogEntry(new LogEntry { Category = category, LogLevel = LogLevel.Error, Message = message }); this.Log(new LogEntry { Category = category, LogLevel = LogLevel.Error, Message = message });
} }
public void LogException(Exception exception, string category = "") public void LogException(Exception exception, string category = "")
{ {
this.CreateLogEntry(new LogEntry { Category = category, LogLevel = LogLevel.Error, Exception = exception }); this.Log(new LogEntry { Category = category, LogLevel = LogLevel.Error, Exception = exception });
} }
public void LogException(Exception exception, string message, string category = "") public void LogException(Exception exception, string message, string category = "")
{ {
this.CreateLogEntry( this.Log(
new LogEntry new LogEntry
{ {
Category = category, Category = category,

@ -1,4 +1,4 @@
using MultiTerm.Core.Common; using MultiTerm.Core.Types;
using MultiTerm.Core.ViewModel; using MultiTerm.Core.ViewModel;
namespace MultiTerm.Core.Factories namespace MultiTerm.Core.Factories

@ -1,4 +1,4 @@
using MultiTerm.Core.Common; using MultiTerm.Core.Types;
using MultiTerm.Core.ViewModel; using MultiTerm.Core.ViewModel;
namespace MultiTerm.Core.Factories; namespace MultiTerm.Core.Factories;

@ -1,6 +1,6 @@
using System.ComponentModel; using System.ComponentModel;
namespace MultiTerm.Core.Common; namespace MultiTerm.Core.Types;
public enum NewlineSeparatorType public enum NewlineSeparatorType
{ {

@ -1,6 +1,6 @@
using System.ComponentModel; using System.ComponentModel;
namespace MultiTerm.Core.Common; namespace MultiTerm.Core.Types;
public enum ProtocolType public enum ProtocolType
{ {

@ -1,6 +1,6 @@
using System.ComponentModel; using System.ComponentModel;
namespace MultiTerm.Core.Common; namespace MultiTerm.Core.Types;
public enum TerminalViewType public enum TerminalViewType
{ {

@ -1,4 +1,4 @@
using MultiTerm.Core.Common; using MultiTerm.Core.Types;
namespace MultiTerm.Core.ViewModel; namespace MultiTerm.Core.ViewModel;

@ -1,4 +1,4 @@
using MultiTerm.Core.Common; using MultiTerm.Core.Types;
namespace MultiTerm.Core.ViewModel; namespace MultiTerm.Core.ViewModel;

@ -1,15 +1,19 @@
using Common.Logging; using Common.AppSettings;
using Common.StartupHelpers; using Common.Logging;
using Common.Helpers;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using MultiTerm.Core.Common;
using MultiTerm.Core.Factories; using MultiTerm.Core.Factories;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using MultiTerm.Core.Types;
namespace MultiTerm.Core.ViewModel; namespace MultiTerm.Core.ViewModel;
public partial class ShellViewModel : ObservableObject public partial class ShellViewModel : ObservableObject
{ {
private const string defaultReceiveNewlineSeparatorAppSettingsKey = "DefaultReceiveNewlineSeparator";
private const string defaultSendNewlineSeparatorAppSettingsKey = "DefaultSendNewlineSeparator";
[ObservableProperty] [ObservableProperty]
private string title = "ShellView Test"; private string title = "ShellView Test";
@ -37,11 +41,20 @@ public partial class ShellViewModel : ObservableObject
private readonly ITerminalViewModelFactory terminalViewModelFactory; private readonly ITerminalViewModelFactory terminalViewModelFactory;
private readonly ILogger logger; private readonly ILogger logger;
private readonly IAppSettingsProvider appSettings;
public ShellViewModel(ITerminalViewModelFactory terminalViewModelFactory, ILogger logger) public ShellViewModel(ITerminalViewModelFactory terminalViewModelFactory, ILogger logger, IAppSettingsProvider appSettings)
{ {
this.terminalViewModelFactory = terminalViewModelFactory; this.terminalViewModelFactory = terminalViewModelFactory;
this.logger = logger; this.logger = logger;
this.appSettings = appSettings;
// initialize newline separators from persistent settings
this.appSettings.TryReadSettingOrAddDefault(defaultReceiveNewlineSeparatorAppSettingsKey, out string settingValueReceiveNLSep, NewlineSeparatorType.None.ToString());
this.DefaultReceiveNewlineSeparator = EnumHelpers.ParseEnum<NewlineSeparatorType>(settingValueReceiveNLSep);
this.appSettings.TryReadSettingOrAddDefault(defaultSendNewlineSeparatorAppSettingsKey, out string settingValueSendNLSep, NewlineSeparatorType.None.ToString());
this.DefaultSendNewlineSeparator = EnumHelpers.ParseEnum<NewlineSeparatorType>(settingValueSendNLSep);
// TEMP Init // TEMP Init
this.AppendTerminalWithSelectedViewType(ProtocolType.Serial); this.AppendTerminalWithSelectedViewType(ProtocolType.Serial);
} }

@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using MultiTerm.Core.Common; using MultiTerm.Core.Types;
namespace MultiTerm.Core.ViewModel; namespace MultiTerm.Core.ViewModel;

@ -4,9 +4,9 @@ using MultiTerm.Core.ViewModel;
using System.Windows; using System.Windows;
using MultiTerm.Core.Helpers; using MultiTerm.Core.Helpers;
using Common.Logging; using Common.Logging;
using Common.Logger;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Common.AppSettings;
namespace MultiTerm.Wpf; namespace MultiTerm.Wpf;
@ -24,6 +24,7 @@ public partial class App : Application
{ {
services.AddSingleton<MainWindow>(); services.AddSingleton<MainWindow>();
services.AddSingleton<ILogger>(new SerilogLogger("C:/log/multiterm-log-.txt", true)); services.AddSingleton<ILogger>(new SerilogLogger("C:/log/multiterm-log-.txt", true));
services.AddSingleton<IAppSettingsProvider>(new XmlAppSettingsProvider("C:/log/multiterm-config.xml"));
// viewmodels // viewmodels
services.AddSingleton<ShellViewModel>(); services.AddSingleton<ShellViewModel>();
@ -48,6 +49,10 @@ public partial class App : Application
Application.Current.DispatcherUnhandledException += this.Application_DispatcherUnhandledException; Application.Current.DispatcherUnhandledException += this.Application_DispatcherUnhandledException;
TaskScheduler.UnobservedTaskException += this.TaskScheduler_UnobservedTaskException; TaskScheduler.UnobservedTaskException += this.TaskScheduler_UnobservedTaskException;
// register log events from AppSettingsProvider
var appSettingsProvider = AppHost.Services.GetRequiredService<IAppSettingsProvider>();
appSettingsProvider.LogWorthyEvent += AppSettingsProvider_LogWorthyEvent;
// instanciate startup form and show // instanciate startup form and show
var startupForm = AppHost.Services.GetRequiredService<MainWindow>(); var startupForm = AppHost.Services.GetRequiredService<MainWindow>();
startupForm.Show(); startupForm.Show();
@ -55,6 +60,11 @@ public partial class App : Application
base.OnStartup(e); base.OnStartup(e);
} }
private void AppSettingsProvider_LogWorthyEvent(object? sender, LogEntry e)
{
logger?.Log(e);
}
protected override async void OnExit(ExitEventArgs e) protected override async void OnExit(ExitEventArgs e)
{ {
// log application exit and stop logger (if still available) // log application exit and stop logger (if still available)

@ -18,4 +18,8 @@
<ProjectReference Include="..\MultiTerm.Wpf.CustomControl\MultiTerm.Wpf.CustomControl.csproj" /> <ProjectReference Include="..\MultiTerm.Wpf.CustomControl\MultiTerm.Wpf.CustomControl.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Assets\" />
</ItemGroup>
</Project> </Project>

@ -9,7 +9,7 @@
xmlns:custom_controls="clr-namespace:MultiTerm.Wpf.CustomControl;assembly=MultiTerm.Wpf.CustomControl" xmlns:custom_controls="clr-namespace:MultiTerm.Wpf.CustomControl;assembly=MultiTerm.Wpf.CustomControl"
xmlns:vm="clr-namespace:MultiTerm.Core.ViewModel;assembly=MultiTerm.Core" xmlns:vm="clr-namespace:MultiTerm.Core.ViewModel;assembly=MultiTerm.Core"
xmlns:v="clr-namespace:MultiTerm.Wpf.View" xmlns:v="clr-namespace:MultiTerm.Wpf.View"
xmlns:core_common="clr-namespace:MultiTerm.Core.Common;assembly=MultiTerm.Core" xmlns:types="clr-namespace:MultiTerm.Core.Types;assembly=MultiTerm.Core"
xmlns:helpers="clr-namespace:MultiTerm.Wpf.Helpers" xmlns:helpers="clr-namespace:MultiTerm.Wpf.Helpers"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="1200"> d:DesignHeight="600" d:DesignWidth="1200">
@ -22,21 +22,21 @@
ObjectType="{x:Type sys:Enum}" ObjectType="{x:Type sys:Enum}"
MethodName="GetValues"> MethodName="GetValues">
<ObjectDataProvider.MethodParameters> <ObjectDataProvider.MethodParameters>
<x:Type TypeName="core_common:NewlineSeparatorType" /> <x:Type TypeName="types:NewlineSeparatorType" />
</ObjectDataProvider.MethodParameters> </ObjectDataProvider.MethodParameters>
</ObjectDataProvider> </ObjectDataProvider>
<ObjectDataProvider x:Key="TerminalViewTypeValues" <ObjectDataProvider x:Key="TerminalViewTypeValues"
ObjectType="{x:Type sys:Enum}" ObjectType="{x:Type sys:Enum}"
MethodName="GetValues"> MethodName="GetValues">
<ObjectDataProvider.MethodParameters> <ObjectDataProvider.MethodParameters>
<x:Type TypeName="core_common:TerminalViewType" /> <x:Type TypeName="types:TerminalViewType" />
</ObjectDataProvider.MethodParameters> </ObjectDataProvider.MethodParameters>
</ObjectDataProvider> </ObjectDataProvider>
<ObjectDataProvider x:Key="ProtocolTypeValues" <ObjectDataProvider x:Key="ProtocolTypeValues"
ObjectType="{x:Type sys:Enum}" ObjectType="{x:Type sys:Enum}"
MethodName="GetValues"> MethodName="GetValues">
<ObjectDataProvider.MethodParameters> <ObjectDataProvider.MethodParameters>
<x:Type TypeName="core_common:ProtocolType" /> <x:Type TypeName="types:ProtocolType" />
</ObjectDataProvider.MethodParameters> </ObjectDataProvider.MethodParameters>
</ObjectDataProvider> </ObjectDataProvider>
</UserControl.Resources> </UserControl.Resources>
@ -111,7 +111,7 @@
<controls:CommandableSubMenu Title="Protocol" <controls:CommandableSubMenu Title="Protocol"
OptionsSource="{Binding Source={StaticResource ProtocolTypeValues}}" OptionsSource="{Binding Source={StaticResource ProtocolTypeValues}}"
Command="{Binding Data.AppendTerminalWithSelectedViewTypeCommand, Source={StaticResource proxy}}" Command="{Binding Data.AppendTerminalWithSelectedViewTypeCommand, Source={StaticResource proxy}}"
CommandParameterType="{x:Type core_common:ProtocolType}"> CommandParameterType="{x:Type types:ProtocolType}">
</controls:CommandableSubMenu> </controls:CommandableSubMenu>
</ContextMenu> </ContextMenu>
</Button.ContextMenu> </Button.ContextMenu>

Loading…
Cancel
Save