diff --git a/Common/AppSettings/IAppSettingsProvider.cs b/Common/AppSettings/IAppSettingsProvider.cs
index dda2e20..7ed70a1 100644
--- a/Common/AppSettings/IAppSettingsProvider.cs
+++ b/Common/AppSettings/IAppSettingsProvider.cs
@@ -12,18 +12,44 @@ public interface IAppSettingsProvider
///
/// Retrieves the app settings from the persistant location and loads them into memory.
/// Loading settings will override the currently stored settings in the memory.
+ /// If loading happens and there were already settings in the memory, a is generated.
///
void Load();
///
/// Saves the app settings to the persistant location.
+ /// If the saving does not work, logs the exception as a but does not throw an exception.
+ /// Rationale: A User is happier if the app is useable and does not remember settings than if the app always throws an exception on startup.
///
void Save();
-
+ ///
+ /// Creates a new setting or overwrites the value of an existing setting in the memory (not persistent location). Settings are identified by key.
+ /// Informs about issues using the .
+ ///
+ /// key of the setting, must not be empty or null
+ /// value of the setting, must not be empty or null
+ /// true if the setting is successfully written or overwritten
bool WriteSetting(string key, string value);
+ ///
+ /// Tries to read a setting from the memory (not persistent location). Settings are identified by key.
+ /// Informs about issues using the .
+ /// If no setting with the given is found, the is set to .
+ ///
+ /// key of the setting, must not be empty or null
+ /// the found value or an empty string if no setting is found
+ /// true when the setting exists and could be read successfully
bool TryReadSetting(string key, out string value);
+ ///
+ /// Tries to read a setting from the memory (not persistent location). Settings are identified by key.
+ /// Informs about issues using the .
+ /// If no setting with the given is found, the setting is created with the given . is also set to .
+ ///
+ /// key of the setting, must not be empty or null
+ /// the found value or an empty string if no setting is found
+ /// the default value to initialize the setting with if it does not exist yet. must not be empty or null
+ /// true when the setting could be read or was created successfully
bool TryReadSettingOrAddDefault(string key, out string value, string defaultValue);
}
diff --git a/Common/AppSettings/XmlAppSettingsProvider.cs b/Common/AppSettings/XmlAppSettingsProvider.cs
index a5e6fc0..116e05d 100644
--- a/Common/AppSettings/XmlAppSettingsProvider.cs
+++ b/Common/AppSettings/XmlAppSettingsProvider.cs
@@ -14,12 +14,17 @@ public class XmlAppSettingsProvider : IAppSettingsProvider
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));
+
+ // create file if it does not exist yet
+ if (File.Exists(settingsFilePath) == false)
+ {
+ this.Save();
+ }
}
public void Load()
@@ -35,23 +40,32 @@ public class XmlAppSettingsProvider : IAppSettingsProvider
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."));
+ 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."));
+ 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();
+ // if saving fails, just log the exception. user is happier if the app is useable but does not remember settings
+ try
+ {
+ // create file and serialize data
+ TextWriter writer = new StreamWriter(this.settingsFilePath);
+ this.serializer.Serialize(writer, this.settings);
+ writer.Close();
+ }
+ catch (Exception ex)
+ {
+ this.LogWorthyEvent?.Invoke(this, new LogEntry(){ LogLevel = LogLevel.Error, Category = nameof(XmlAppSettingsProvider),
+ Message = $"'{nameof(Save)}()' failed to create file or serialize data.", Exception = ex });
+ }
}
@@ -69,7 +83,7 @@ public class XmlAppSettingsProvider : IAppSettingsProvider
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."));
+ message: $"'{nameof(WriteSetting)}()' cancelled because {foundSettings.Count} settings with the key '{key}' were found. Expected none or exactly one."));
return false; // cancel
}
@@ -82,7 +96,7 @@ public class XmlAppSettingsProvider : IAppSettingsProvider
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}'."));
+ message: $"'{nameof(WriteSetting)}()' initialized new setting with key '{key}' and value '{value}'."));
}
// add new setting
@@ -102,7 +116,7 @@ public class XmlAppSettingsProvider : IAppSettingsProvider
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."));
+ message: $"'{nameof(TryReadSetting)}()' did find {foundSettings.Count} settings with the key '{key}'. Expected only one."));
value = string.Empty;
return false;
@@ -112,7 +126,7 @@ public class XmlAppSettingsProvider : IAppSettingsProvider
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}'."));
+ message: $"'{nameof(TryReadSetting)}()' did not find a setting with the key '{key}'."));
value = string.Empty;
return false;
@@ -128,11 +142,10 @@ public class XmlAppSettingsProvider : IAppSettingsProvider
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."));
+ message: $"'{nameof(TryReadSetting)}()' value of retrieved setting with key '{key}' was null. Returned empty string instead."));
value = string.Empty;
}
-
return true;
}
@@ -149,7 +162,7 @@ public class XmlAppSettingsProvider : IAppSettingsProvider
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."));
+ message: $"'{nameof(TryReadSettingOrAddDefault)}()' did find {foundSettings.Count} settings with the key '{key}'. Expected only one."));
value = string.Empty;
return false;
@@ -159,11 +172,10 @@ public class XmlAppSettingsProvider : IAppSettingsProvider
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}'."));
+ 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;
+ return this.WriteSetting(key, defaultValue);
}
// extract correct setting when found exactly one
@@ -176,7 +188,7 @@ public class XmlAppSettingsProvider : IAppSettingsProvider
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."));
+ message: $"'{nameof(TryReadSetting)}()' value of retrieved setting with key '{key}' was null. Returned empty string instead."));
value = string.Empty;
}
diff --git a/MultiTerm.Core/ViewModel/ShellViewModel.cs b/MultiTerm.Core/ViewModel/ShellViewModel.cs
index 2e09c1e..4257f14 100644
--- a/MultiTerm.Core/ViewModel/ShellViewModel.cs
+++ b/MultiTerm.Core/ViewModel/ShellViewModel.cs
@@ -14,21 +14,18 @@ public partial class ShellViewModel : ObservableObject
private const string defaultReceiveNewlineSeparatorAppSettingsKey = "DefaultReceiveNewlineSeparator";
private const string defaultSendNewlineSeparatorAppSettingsKey = "DefaultSendNewlineSeparator";
- [ObservableProperty]
- private string title = "ShellView Test";
-
+ #region Terminal collection
[ObservableProperty]
private ObservableCollection terminalViewModels = new();
[ObservableProperty]
private ITerminalViewModel? selectedTerminalViewModel;
+ #endregion
#region Settings Menu Bar
- // TODO Initialize from File
[ObservableProperty]
private NewlineSeparatorType defaultReceiveNewlineSeparator = NewlineSeparatorType.None;
- // TODO Initialize from File
[ObservableProperty]
private NewlineSeparatorType defaultSendNewlineSeparator = NewlineSeparatorType.None;
#endregion
@@ -36,7 +33,6 @@ public partial class ShellViewModel : ObservableObject
#region New Terminal Context Menu
[ObservableProperty]
private TerminalViewType selectedTerminalViewType = TerminalViewType.SendReceive;
-
#endregion
private readonly ITerminalViewModelFactory terminalViewModelFactory;
@@ -49,11 +45,8 @@ public partial class ShellViewModel : ObservableObject
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);
+ // intialize values from app settings
+ this.LoadFromAppSettings();
// TEMP Init
this.AppendTerminalWithSelectedViewType(ProtocolType.Serial);
@@ -94,20 +87,26 @@ public partial class ShellViewModel : ObservableObject
this.TerminalViewModels.Remove(terminalToRemove);
}
- partial void OnDefaultReceiveNewlineSeparatorChanged(NewlineSeparatorType value)
+ #region App Settings handling
+ private void LoadFromAppSettings()
{
- Console.WriteLine($"Changed to {value}");
+ // 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);
}
- partial void OnDefaultSendNewlineSeparatorChanged(NewlineSeparatorType value)
+ partial void OnDefaultReceiveNewlineSeparatorChanged(NewlineSeparatorType value)
{
- Console.WriteLine($"Changed Send to {value}");
+ // update setting in app settings
+ this.appSettings.WriteSetting(defaultReceiveNewlineSeparatorAppSettingsKey, value.ToString());
}
- [RelayCommand]
- private void TestButtonClicked()
+ partial void OnDefaultSendNewlineSeparatorChanged(NewlineSeparatorType value)
{
- this.DefaultReceiveNewlineSeparator = NewlineSeparatorType.CR_LF;
+ // update setting in app settings
+ this.appSettings.WriteSetting(defaultSendNewlineSeparatorAppSettingsKey, value.ToString());
}
-
+ #endregion
}
diff --git a/MultiTerm.Wpf/App.xaml.cs b/MultiTerm.Wpf/App.xaml.cs
index 03428b4..16a891c 100644
--- a/MultiTerm.Wpf/App.xaml.cs
+++ b/MultiTerm.Wpf/App.xaml.cs
@@ -49,9 +49,10 @@ public partial class App : Application
Application.Current.DispatcherUnhandledException += this.Application_DispatcherUnhandledException;
TaskScheduler.UnobservedTaskException += this.TaskScheduler_UnobservedTaskException;
- // register log events from AppSettingsProvider
+ // register log events from AppSettingsProvider and load saved settings
var appSettingsProvider = AppHost.Services.GetRequiredService();
appSettingsProvider.LogWorthyEvent += AppSettingsProvider_LogWorthyEvent;
+ appSettingsProvider.Load();
// instanciate startup form and show
var startupForm = AppHost.Services.GetRequiredService();
@@ -67,6 +68,10 @@ public partial class App : Application
protected override async void OnExit(ExitEventArgs e)
{
+ // save settings to persistent location
+ var appSettingsProvider = AppHost!.Services.GetRequiredService();
+ appSettingsProvider.Save();
+
// log application exit and stop logger (if still available)
logger?.LogInfo("Application exited.", nameof(App));
logger?.StopLogging();