diff --git a/Common/AppSettings/AppSetting.cs b/Common/AppSettings/AppSetting.cs
new file mode 100644
index 0000000..1ae2af8
--- /dev/null
+++ b/Common/AppSettings/AppSetting.cs
@@ -0,0 +1,24 @@
+namespace Common.AppSettings;
+
+[Serializable]
+public class AppSetting
+{
+ public string? Key { get; set; }
+ public string? Value { get; set; }
+
+ ///
+ /// Parameterless constructor for serialization.
+ ///
+ public AppSetting()
+ {
+ }
+
+ ///
+ /// Constructor with Initialization.
+ ///
+ public AppSetting(string key, string value)
+ {
+ this.Key = key;
+ this.Value = value;
+ }
+}
diff --git a/Common/AppSettings/IAppSettingsProvider.cs b/Common/AppSettings/IAppSettingsProvider.cs
new file mode 100644
index 0000000..dda2e20
--- /dev/null
+++ b/Common/AppSettings/IAppSettingsProvider.cs
@@ -0,0 +1,29 @@
+using Common.Logging;
+
+namespace Common.AppSettings;
+
+public interface IAppSettingsProvider
+{
+ ///
+ /// Any internal event inside of the App Settings Provider.
+ ///
+ event EventHandler? LogWorthyEvent;
+
+ ///
+ /// Retrieves the app settings from the persistant location and loads them into memory.
+ /// Loading settings will override the currently stored settings in the memory.
+ ///
+ void Load();
+
+ ///
+ /// Saves the app settings to the persistant location.
+ ///
+ void Save();
+
+
+ bool WriteSetting(string key, string value);
+
+ bool TryReadSetting(string key, out string value);
+
+ bool TryReadSettingOrAddDefault(string key, out string value, string defaultValue);
+}
diff --git a/Common/AppSettings/XmlAppSettingsProvider.cs b/Common/AppSettings/XmlAppSettingsProvider.cs
new file mode 100644
index 0000000..a5e6fc0
--- /dev/null
+++ b/Common/AppSettings/XmlAppSettingsProvider.cs
@@ -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 settings;
+
+ public event EventHandler? 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();
+ this.serializer = new XmlSerializer(typeof(List));
+ }
+
+ 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)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;
+ }
+}
diff --git a/Common/Helpers/EnumHelpers.cs b/Common/Helpers/EnumHelpers.cs
new file mode 100644
index 0000000..0961b0d
--- /dev/null
+++ b/Common/Helpers/EnumHelpers.cs
@@ -0,0 +1,15 @@
+namespace Common.Helpers;
+
+public static class EnumHelpers
+{
+ ///
+ /// Parse enum from string to enum type.
+ ///
+ /// type of the enum
+ /// value to parse to enum
+ /// returns enum object with value
+ public static T ParseEnum(string value)
+ {
+ return (T)Enum.Parse(typeof(T), value, true);
+ }
+}
diff --git a/Common/Logging/ILogger.cs b/Common/Logging/ILogger.cs
index 7469089..347c486 100644
--- a/Common/Logging/ILogger.cs
+++ b/Common/Logging/ILogger.cs
@@ -43,6 +43,12 @@ public interface ILogger
void SetMinimumLogLevel(LogLevel newMinimumLogLevel);
#region Logging methods
+ ///
+ /// Create a Log entry with a complete object.
+ ///
+ /// to log
+ void Log(LogEntry logEntry);
+
///
/// Create a Log entry with the level .
///
diff --git a/Common/Logging/SerilogLogger.cs b/Common/Logging/SerilogLogger.cs
index 7acb1ed..bc416d2 100644
--- a/Common/Logging/SerilogLogger.cs
+++ b/Common/Logging/SerilogLogger.cs
@@ -1,19 +1,20 @@
-using Common.Logging;
-using Serilog;
+using Serilog;
using Serilog.Core;
using Serilog.Events;
-namespace Common.Logger;
+// alias to prevent conflict own Log method
+using SerilogLoggingInstance = Serilog.Log;
+
+namespace Common.Logging;
///
/// Implements a Logger that uses the Serilog package to create log entries and distributes them into different sinks.
/// Implemented Sinks: File (rolling file), Debug ().
///
-public class SerilogLogger : Logging.ILogger
+public class SerilogLogger : ILogger
{
private readonly LoggingLevelSwitch loggingLevelSwitch;
-
public event EventHandler? NewLogEntry;
public LogLevel CurrentMinimumLogLevel { get; private set; } = LogLevel.Undefined;
@@ -38,7 +39,7 @@ public class SerilogLogger : Logging.ILogger
// create logger instance
if (logToDebug)
{
- Log.Logger = new LoggerConfiguration()
+ SerilogLoggingInstance.Logger = new LoggerConfiguration()
.WriteTo.Debug()
.WriteTo.File(logFilePath,
rollingInterval: RollingInterval.Day,
@@ -48,7 +49,7 @@ public class SerilogLogger : Logging.ILogger
}
else
{
- Log.Logger = new LoggerConfiguration()
+ SerilogLoggingInstance.Logger = new LoggerConfiguration()
.WriteTo.File(logFilePath,
rollingInterval: RollingInterval.Day,
levelSwitch: loggingLevelSwitch,
@@ -87,10 +88,12 @@ public class SerilogLogger : Logging.ILogger
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);
@@ -98,31 +101,31 @@ public class SerilogLogger : Logging.ILogger
switch (serilogEventLevel)
{
case LogEventLevel.Verbose:
- Log.Verbose(logEntry.ToString());
+ SerilogLoggingInstance.Verbose(logEntry.ToString());
break;
case LogEventLevel.Debug:
- Log.Debug(logEntry.ToString());
+ SerilogLoggingInstance.Debug(logEntry.ToString());
break;
case LogEventLevel.Information:
- Log.Information(logEntry.ToString());
+ SerilogLoggingInstance.Information(logEntry.ToString());
break;
case LogEventLevel.Warning:
- Log.Warning(logEntry.ToString());
+ SerilogLoggingInstance.Warning(logEntry.ToString());
break;
case LogEventLevel.Error:
- Log.Error(logEntry.ToString());
+ SerilogLoggingInstance.Error(logEntry.ToString());
break;
case LogEventLevel.Fatal:
- Log.Error(logEntry.ToString());
+ SerilogLoggingInstance.Error(logEntry.ToString());
break;
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)
@@ -132,41 +135,39 @@ public class SerilogLogger : Logging.ILogger
}
}
-
- #region Logging methods
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 = "")
{
- 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 = "")
{
- 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 = "")
{
- 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 = "")
{
- 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 = "")
{
- 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 = "")
{
- this.CreateLogEntry(
+ this.Log(
new LogEntry
{
Category = category,
diff --git a/MultiTerm.Core/Factories/ITerminalViewModelFactory.cs b/MultiTerm.Core/Factories/ITerminalViewModelFactory.cs
index 63b09f6..60888ff 100644
--- a/MultiTerm.Core/Factories/ITerminalViewModelFactory.cs
+++ b/MultiTerm.Core/Factories/ITerminalViewModelFactory.cs
@@ -1,4 +1,4 @@
-using MultiTerm.Core.Common;
+using MultiTerm.Core.Types;
using MultiTerm.Core.ViewModel;
namespace MultiTerm.Core.Factories
diff --git a/MultiTerm.Core/Factories/TerminalViewModelFactory.cs b/MultiTerm.Core/Factories/TerminalViewModelFactory.cs
index 3c9566c..367855f 100644
--- a/MultiTerm.Core/Factories/TerminalViewModelFactory.cs
+++ b/MultiTerm.Core/Factories/TerminalViewModelFactory.cs
@@ -1,4 +1,4 @@
-using MultiTerm.Core.Common;
+using MultiTerm.Core.Types;
using MultiTerm.Core.ViewModel;
namespace MultiTerm.Core.Factories;
diff --git a/MultiTerm.Core/Common/NewlineSeparatorType.cs b/MultiTerm.Core/Types/NewlineSeparatorType.cs
similarity index 94%
rename from MultiTerm.Core/Common/NewlineSeparatorType.cs
rename to MultiTerm.Core/Types/NewlineSeparatorType.cs
index 30030ab..c5a2687 100644
--- a/MultiTerm.Core/Common/NewlineSeparatorType.cs
+++ b/MultiTerm.Core/Types/NewlineSeparatorType.cs
@@ -1,6 +1,6 @@
using System.ComponentModel;
-namespace MultiTerm.Core.Common;
+namespace MultiTerm.Core.Types;
public enum NewlineSeparatorType
{
diff --git a/MultiTerm.Core/Common/ProtocolType.cs b/MultiTerm.Core/Types/ProtocolType.cs
similarity index 93%
rename from MultiTerm.Core/Common/ProtocolType.cs
rename to MultiTerm.Core/Types/ProtocolType.cs
index ae241e8..799cd7f 100644
--- a/MultiTerm.Core/Common/ProtocolType.cs
+++ b/MultiTerm.Core/Types/ProtocolType.cs
@@ -1,6 +1,6 @@
using System.ComponentModel;
-namespace MultiTerm.Core.Common;
+namespace MultiTerm.Core.Types;
public enum ProtocolType
{
diff --git a/MultiTerm.Core/Common/TerminalViewType.cs b/MultiTerm.Core/Types/TerminalViewType.cs
similarity index 89%
rename from MultiTerm.Core/Common/TerminalViewType.cs
rename to MultiTerm.Core/Types/TerminalViewType.cs
index 66445d9..4005741 100644
--- a/MultiTerm.Core/Common/TerminalViewType.cs
+++ b/MultiTerm.Core/Types/TerminalViewType.cs
@@ -1,6 +1,6 @@
using System.ComponentModel;
-namespace MultiTerm.Core.Common;
+namespace MultiTerm.Core.Types;
public enum TerminalViewType
{
diff --git a/MultiTerm.Core/ViewModel/ITerminalViewModel.cs b/MultiTerm.Core/ViewModel/ITerminalViewModel.cs
index c8b18c3..eedc0a8 100644
--- a/MultiTerm.Core/ViewModel/ITerminalViewModel.cs
+++ b/MultiTerm.Core/ViewModel/ITerminalViewModel.cs
@@ -1,4 +1,4 @@
-using MultiTerm.Core.Common;
+using MultiTerm.Core.Types;
namespace MultiTerm.Core.ViewModel;
diff --git a/MultiTerm.Core/ViewModel/SendReceiveViewModel.cs b/MultiTerm.Core/ViewModel/SendReceiveViewModel.cs
index 0467e90..73998e8 100644
--- a/MultiTerm.Core/ViewModel/SendReceiveViewModel.cs
+++ b/MultiTerm.Core/ViewModel/SendReceiveViewModel.cs
@@ -1,4 +1,4 @@
-using MultiTerm.Core.Common;
+using MultiTerm.Core.Types;
namespace MultiTerm.Core.ViewModel;
diff --git a/MultiTerm.Core/ViewModel/ShellViewModel.cs b/MultiTerm.Core/ViewModel/ShellViewModel.cs
index e0373a5..2e09c1e 100644
--- a/MultiTerm.Core/ViewModel/ShellViewModel.cs
+++ b/MultiTerm.Core/ViewModel/ShellViewModel.cs
@@ -1,15 +1,19 @@
-using Common.Logging;
-using Common.StartupHelpers;
+using Common.AppSettings;
+using Common.Logging;
+using Common.Helpers;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
-using MultiTerm.Core.Common;
using MultiTerm.Core.Factories;
using System.Collections.ObjectModel;
+using MultiTerm.Core.Types;
namespace MultiTerm.Core.ViewModel;
public partial class ShellViewModel : ObservableObject
{
+ private const string defaultReceiveNewlineSeparatorAppSettingsKey = "DefaultReceiveNewlineSeparator";
+ private const string defaultSendNewlineSeparatorAppSettingsKey = "DefaultSendNewlineSeparator";
+
[ObservableProperty]
private string title = "ShellView Test";
@@ -37,11 +41,20 @@ public partial class ShellViewModel : ObservableObject
private readonly ITerminalViewModelFactory terminalViewModelFactory;
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.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(settingValueReceiveNLSep);
+ this.appSettings.TryReadSettingOrAddDefault(defaultSendNewlineSeparatorAppSettingsKey, out string settingValueSendNLSep, NewlineSeparatorType.None.ToString());
+ this.DefaultSendNewlineSeparator = EnumHelpers.ParseEnum(settingValueSendNLSep);
+
// TEMP Init
this.AppendTerminalWithSelectedViewType(ProtocolType.Serial);
}
diff --git a/MultiTerm.Core/ViewModel/TerminalViewModel.cs b/MultiTerm.Core/ViewModel/TerminalViewModel.cs
index 9af35e4..9af6d0b 100644
--- a/MultiTerm.Core/ViewModel/TerminalViewModel.cs
+++ b/MultiTerm.Core/ViewModel/TerminalViewModel.cs
@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
-using MultiTerm.Core.Common;
+using MultiTerm.Core.Types;
namespace MultiTerm.Core.ViewModel;
diff --git a/MultiTerm.Wpf/App.xaml.cs b/MultiTerm.Wpf/App.xaml.cs
index dcb58aa..03428b4 100644
--- a/MultiTerm.Wpf/App.xaml.cs
+++ b/MultiTerm.Wpf/App.xaml.cs
@@ -4,9 +4,9 @@ using MultiTerm.Core.ViewModel;
using System.Windows;
using MultiTerm.Core.Helpers;
using Common.Logging;
-using Common.Logger;
using System;
using System.Threading.Tasks;
+using Common.AppSettings;
namespace MultiTerm.Wpf;
@@ -24,6 +24,7 @@ public partial class App : Application
{
services.AddSingleton();
services.AddSingleton(new SerilogLogger("C:/log/multiterm-log-.txt", true));
+ services.AddSingleton(new XmlAppSettingsProvider("C:/log/multiterm-config.xml"));
// viewmodels
services.AddSingleton();
@@ -48,6 +49,10 @@ public partial class App : Application
Application.Current.DispatcherUnhandledException += this.Application_DispatcherUnhandledException;
TaskScheduler.UnobservedTaskException += this.TaskScheduler_UnobservedTaskException;
+ // register log events from AppSettingsProvider
+ var appSettingsProvider = AppHost.Services.GetRequiredService();
+ appSettingsProvider.LogWorthyEvent += AppSettingsProvider_LogWorthyEvent;
+
// instanciate startup form and show
var startupForm = AppHost.Services.GetRequiredService();
startupForm.Show();
@@ -55,6 +60,11 @@ public partial class App : Application
base.OnStartup(e);
}
+ private void AppSettingsProvider_LogWorthyEvent(object? sender, LogEntry e)
+ {
+ logger?.Log(e);
+ }
+
protected override async void OnExit(ExitEventArgs e)
{
// log application exit and stop logger (if still available)
diff --git a/MultiTerm.Wpf/MultiTerm.Wpf.csproj b/MultiTerm.Wpf/MultiTerm.Wpf.csproj
index 458f24a..dd866f9 100644
--- a/MultiTerm.Wpf/MultiTerm.Wpf.csproj
+++ b/MultiTerm.Wpf/MultiTerm.Wpf.csproj
@@ -18,4 +18,8 @@
+
+
+
+
diff --git a/MultiTerm.Wpf/View/ShellView.xaml b/MultiTerm.Wpf/View/ShellView.xaml
index 76a4e10..6518bb2 100644
--- a/MultiTerm.Wpf/View/ShellView.xaml
+++ b/MultiTerm.Wpf/View/ShellView.xaml
@@ -9,7 +9,7 @@
xmlns:custom_controls="clr-namespace:MultiTerm.Wpf.CustomControl;assembly=MultiTerm.Wpf.CustomControl"
xmlns:vm="clr-namespace:MultiTerm.Core.ViewModel;assembly=MultiTerm.Core"
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"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="1200">
@@ -22,21 +22,21 @@
ObjectType="{x:Type sys:Enum}"
MethodName="GetValues">
-
+
-
+
-
+
@@ -111,7 +111,7 @@
+ CommandParameterType="{x:Type types:ProtocolType}">