Add property changed filtering events to Configuration

This commit is contained in:
Michael Bucari-Tovo 2023-01-06 22:56:00 -07:00
parent 5c73beff4b
commit 4725fe36d1
10 changed files with 397 additions and 194 deletions

View File

@ -81,11 +81,10 @@ namespace FileLiberator
LibraryBookDto = LibraryBook.ToDto();
cancellation =
Configuration.Instance
.SubscribeToPropertyChanged<long>(
nameof(Configuration.DownloadSpeedLimit),
(_, s) => DownloadSpeedChanged?.Invoke(this, s));
cancellation = Configuration.Instance
.ObservePropertyChanged<long>(
nameof(Configuration.DownloadSpeedLimit),
(_, s) => DownloadSpeedChanged?.Invoke(this, s));
}
}
}

View File

@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="620"
mc:Ignorable="d" d:DesignWidth="850" d:DesignHeight="620"
MinWidth="800" MinHeight="620"
x:Class="LibationAvalonia.Dialogs.SettingsDialog"
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
@ -102,7 +102,7 @@
Click="OpenLogFolderButton_Click" />
</StackPanel>
<!--
<!--
<CheckBox
Grid.Row="2"
Margin="5"
@ -282,7 +282,7 @@
Grid.Column="0"
Margin="0,5,0,0"
Text="{Binding DownloadDecryptSettings.FolderTemplateText}" />
<TextBox
Grid.Row="1"
Grid.Column="0"
@ -356,21 +356,21 @@
Margin="5"
BorderWidth="1"
Label="Temporary Files Location">
<StackPanel
Margin="5" >
<TextBlock
Margin="0,0,0,10"
Text="{Binding DownloadDecryptSettings.InProgressDescriptionText}" />
<StackPanel
Margin="5" >
<controls:DirectoryOrCustomSelectControl
SubDirectory="Libation"
Directory="{Binding DownloadDecryptSettings.InProgressDirectory, Mode=TwoWay}"
KnownDirectories="{Binding DownloadDecryptSettings.KnownDirectories}" />
<TextBlock
Margin="0,0,0,10"
Text="{Binding DownloadDecryptSettings.InProgressDescriptionText}" />
<controls:DirectoryOrCustomSelectControl
SubDirectory="Libation"
Directory="{Binding DownloadDecryptSettings.InProgressDirectory, Mode=TwoWay}"
KnownDirectories="{Binding DownloadDecryptSettings.KnownDirectories}" />
</StackPanel>
</StackPanel>
</controls:GroupBox>
<CheckBox
@ -436,6 +436,26 @@
</CheckBox>
<StackPanel Orientation="Horizontal">
<CheckBox
Margin="0,0,0,5"
IsChecked="{Binding AudioSettings.DownloadClipsBookmarks, Mode=TwoWay}">
<TextBlock
TextWrapping="Wrap"
Text="Download Clips, Notes and Bookmarks as" />
</CheckBox>
<controls:WheelComboBox
Margin="5,0,0,0"
IsEnabled="{Binding AudioSettings.DownloadClipsBookmarks}"
Items="{Binding AudioSettings.ClipBookmarkFormats}"
SelectedItem="{Binding AudioSettings.ClipBookmarkFormat}"/>
</StackPanel>
<CheckBox
Margin="0,0,0,5"
IsChecked="{Binding AudioSettings.RetainAaxFile, Mode=TwoWay}">

View File

@ -10,6 +10,7 @@ using Dinah.Core;
using System.Linq;
using FileManager;
using System.IO;
using Avalonia.Collections;
namespace LibationAvalonia.Dialogs
{
@ -381,6 +382,7 @@ namespace LibationAvalonia.Dialogs
public class AudioSettings : ViewModels.ViewModelBase, ISettingsDisplay
{
private bool _downloadClipsBookmarks;
private bool _splitFilesByChapter;
private bool _allowLibationFixup;
private bool _lameTargetBitrate;
@ -401,6 +403,8 @@ namespace LibationAvalonia.Dialogs
AllowLibationFixup = config.AllowLibationFixup;
DownloadCoverArt = config.DownloadCoverArt;
RetainAaxFile = config.RetainAaxFile;
DownloadClipsBookmarks = config.DownloadClipsBookmarks;
ClipBookmarkFormat = config.ClipsBookmarksFileFormat;
SplitFilesByChapter = config.SplitFilesByChapter;
MergeOpeningAndEndCredits = config.MergeOpeningAndEndCredits;
StripAudibleBrandAudio = config.StripAudibleBrandAudio;
@ -421,6 +425,8 @@ namespace LibationAvalonia.Dialogs
config.AllowLibationFixup = AllowLibationFixup;
config.DownloadCoverArt = DownloadCoverArt;
config.RetainAaxFile = RetainAaxFile;
config.DownloadClipsBookmarks = DownloadClipsBookmarks;
config.ClipsBookmarksFileFormat = ClipBookmarkFormat;
config.SplitFilesByChapter = SplitFilesByChapter;
config.MergeOpeningAndEndCredits = MergeOpeningAndEndCredits;
config.StripAudibleBrandAudio = StripAudibleBrandAudio;
@ -437,6 +443,7 @@ namespace LibationAvalonia.Dialogs
return Task.FromResult(true);
}
public AvaloniaList<Configuration.ClipBookmarkFormat> ClipBookmarkFormats { get; } = new(Enum<Configuration.ClipBookmarkFormat>.GetValues());
public string CreateCueSheetText { get; } = Configuration.GetDescription(nameof(Configuration.CreateCueSheet));
public string AllowLibationFixupText { get; } = Configuration.GetDescription(nameof(Configuration.AllowLibationFixup));
public string DownloadCoverArtText { get; } = Configuration.GetDescription(nameof(Configuration.DownloadCoverArt));
@ -450,6 +457,8 @@ namespace LibationAvalonia.Dialogs
public bool CreateCueSheet { get; set; }
public bool DownloadCoverArt { get; set; }
public bool RetainAaxFile { get; set; }
public bool DownloadClipsBookmarks { get => _downloadClipsBookmarks; set => this.RaiseAndSetIfChanged(ref _downloadClipsBookmarks, value); }
public Configuration.ClipBookmarkFormat ClipBookmarkFormat { get; set; }
public bool MergeOpeningAndEndCredits { get; set; }
public bool StripAudibleBrandAudio { get; set; }
public bool StripUnabridged { get; set; }

View File

@ -55,11 +55,7 @@ namespace LibationAvalonia.Views
AccountsSettingsPersister.Saved += accountsPostSave;
// when autoscan setting is changed, update menu checkbox and run autoscan
Configuration.Instance.PropertyChanged += (_, e) =>
{
if (e.PropertyName == nameof(Configuration.Instance.AutoScan))
startAutoScan();
};
Configuration.Instance.PropertyChanged += startAutoScan;
}
private List<(string AccountId, string LocaleName)> preSaveDefaultAccounts;
@ -83,9 +79,11 @@ namespace LibationAvalonia.Views
startAutoScan();
}
[PropertyChangeFilter(nameof(Configuration.AutoScan))]
private void startAutoScan(object sender = null, EventArgs e = null)
{
if (Configuration.Instance.AutoScan)
_viewModel.AutoScanChecked = Configuration.Instance.AutoScan;
if (_viewModel.AutoScanChecked)
autoScanTimer.PerformNow();
else
autoScanTimer.Stop();

View File

@ -36,7 +36,7 @@ namespace LibationFileManager
}
set
{
PropertyChanging?.Invoke(this, new PropertyChangingEventArgsEx(nameof(LogLevel), LogLevel, value));
OnPropertyChanging(nameof(LogLevel), LogLevel, value);
var valueWasChanged = persistentDictionary.SetWithJsonPath("Serilog", "MinimumLevel", value.ToString());
if (!valueWasChanged)
{
@ -46,7 +46,7 @@ namespace LibationFileManager
configuration.Reload();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgsEx(nameof(LogLevel), value));
OnPropertyChanged(nameof(LogLevel), value);
Log.Logger.Information("Updated LogLevel MinimumLevel. {@DebugInfo}", new
{

View File

@ -28,9 +28,9 @@ namespace LibationFileManager
var existing = getExistingValue(propertyName);
if (existing?.Equals(newValue) is true) return;
PropertyChanging?.Invoke(this, new PropertyChangingEventArgsEx(propertyName, existing, newValue));
OnPropertyChanging(propertyName, existing, newValue);
persistentDictionary.SetNonString(propertyName, newValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgsEx(propertyName, newValue));
OnPropertyChanged(propertyName, newValue);
}
public void SetString(string newValue, [CallerMemberName] string propertyName = "")
@ -38,9 +38,9 @@ namespace LibationFileManager
var existing = getExistingValue(propertyName);
if (existing?.Equals(newValue) is true) return;
PropertyChanging?.Invoke(this, new PropertyChangingEventArgsEx(propertyName, existing, newValue));
OnPropertyChanging(propertyName, existing, newValue);
persistentDictionary.SetString(propertyName, newValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgsEx(propertyName, newValue));
OnPropertyChanged(propertyName, newValue);
}
private object getExistingValue(string propertyName)

View File

@ -1,158 +1,9 @@
using Dinah.Core;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections.Generic;
namespace LibationFileManager
{
public partial class Configuration : INotifyPropertyChanging, INotifyPropertyChanged
public partial class Configuration : PropertyChangeFilter
{
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
private readonly Dictionary<string, List<MulticastDelegate>> propertyChangedActions = new();
private readonly Dictionary<string, List<MulticastDelegate>> propertyChangingActions = new();
/// <summary>
/// Clear all subscriptions to Property<b>Changed</b> for <paramref name="propertyName"/>
/// </summary>
public void ClearChangedSubscriptions(string propertyName)
{
if (propertyChangedActions.ContainsKey(propertyName)
&& propertyChangedActions[propertyName] is not null)
propertyChangedActions[propertyName].Clear();
}
/// <summary>
/// Clear all subscriptions to Property<b>Changing</b> for <paramref name="propertyName"/>
/// </summary>
public void ClearChangingSubscriptions(string propertyName)
{
if (propertyChangingActions.ContainsKey(propertyName)
&& propertyChangingActions[propertyName] is not null)
propertyChangingActions[propertyName].Clear();
}
/// <summary>
/// Add an action to be executed when a property's value has changed
/// </summary>
/// <typeparam name="T">The <paramref name="propertyName"/>'s <see cref="Type"/></typeparam>
/// <param name="propertyName">Name of the property whose change triggers the <paramref name="action"/></param>
/// <param name="action">Action to be executed with parameters: <paramref name="propertyName"/> and <strong>NewValue</strong></param>
/// <returns>A reference to an interface that allows observers to stop receiving notifications before the provider has finished sending them.</returns>
public IDisposable SubscribeToPropertyChanged<T>(string propertyName, Action<string, T> action)
{
validateSubscriber<T>(propertyName, action);
if (!propertyChangedActions.ContainsKey(propertyName))
propertyChangedActions.Add(propertyName, new List<MulticastDelegate>());
var actionlist = propertyChangedActions[propertyName];
if (!actionlist.Contains(action))
actionlist.Add(action);
return new Unsubscriber(actionlist, action);
}
/// <summary>
/// Add an action to be executed when a property's value is changing
/// </summary>
/// <typeparam name="T">The <paramref name="propertyName"/>'s <see cref="Type"/></typeparam>
/// <param name="propertyName">Name of the property whose change triggers the <paramref name="action"/></param>
/// <param name="action">Action to be executed with parameters: <paramref name="propertyName"/>, <b>OldValue</b>, and <b>NewValue</b></param>
/// <returns>A reference to an interface that allows observers to stop receiving notifications before the provider has finished sending them.</returns>
public IDisposable SubscribeToPropertyChanging<T>(string propertyName, Action<string, T, T> action)
{
validateSubscriber<T>(propertyName, action);
if (!propertyChangingActions.ContainsKey(propertyName))
propertyChangingActions.Add(propertyName, new List<MulticastDelegate>());
var actionlist = propertyChangingActions[propertyName];
if (!actionlist.Contains(action))
actionlist.Add(action);
return new Unsubscriber(actionlist, action);
}
private void validateSubscriber<T>(string propertyName, MulticastDelegate action)
{
ArgumentValidator.EnsureNotNullOrWhiteSpace(propertyName, nameof(propertyName));
ArgumentValidator.EnsureNotNull(action, nameof(action));
var propertyInfo = GetType().GetProperty(propertyName);
if (propertyInfo is null)
throw new MissingMemberException($"{nameof(Configuration)}.{propertyName} does not exist.");
if (propertyInfo.PropertyType != typeof(T))
throw new InvalidCastException($"{nameof(Configuration)}.{propertyName} is {propertyInfo.PropertyType}, but parameter is {typeof(T)}.");
}
private void Configuration_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e is PropertyChangedEventArgsEx args && propertyChangedActions.ContainsKey(args.PropertyName))
{
foreach (var action in propertyChangedActions[args.PropertyName])
{
action.DynamicInvoke(args.PropertyName, args.NewValue);
}
}
}
private void Configuration_PropertyChanging(object sender, PropertyChangingEventArgs e)
{
if (e is PropertyChangingEventArgsEx args && propertyChangingActions.ContainsKey(args.PropertyName))
{
foreach (var action in propertyChangingActions[args.PropertyName])
{
action.DynamicInvoke(args.PropertyName, args.OldValue, args.NewValue);
}
}
}
private class PropertyChangingEventArgsEx : PropertyChangingEventArgs
{
public object OldValue { get; }
public object NewValue { get; }
public PropertyChangingEventArgsEx(string propertyName, object oldValue, object newValue) : base(propertyName)
{
OldValue = oldValue;
NewValue = newValue;
}
}
private class PropertyChangedEventArgsEx : PropertyChangedEventArgs
{
public object NewValue { get; }
public PropertyChangedEventArgsEx(string propertyName, object newValue) : base(propertyName)
{
NewValue = newValue;
}
}
private class Unsubscriber : IDisposable
{
private List<MulticastDelegate> _observers;
private MulticastDelegate _observer;
internal Unsubscriber(List<MulticastDelegate> observers, MulticastDelegate observer)
{
_observers = observers;
_observer = observer;
}
public void Dispose()
{
if (_observers.Contains(_observer))
_observers.Remove(_observer);
}
}
/*
* Use this type in the getter for any Dictionary<TKey, TValue> settings,
* and be sure to clone it before returning. This allows Configuration to

View File

@ -32,11 +32,7 @@ namespace LibationFileManager
#region singleton stuff
public static Configuration Instance { get; } = new Configuration();
private Configuration()
{
PropertyChanging += Configuration_PropertyChanging;
PropertyChanged += Configuration_PropertyChanged;
}
private Configuration() { }
#endregion
}
}

View File

@ -0,0 +1,331 @@
using Dinah.Core;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace LibationFileManager
{
#region Useage
/*
* USEAGE
*************************
* *
* Event Filter Mode *
* *
*************************
propertyChangeFilter.PropertyChanged += MyPropertiesChanged;
[PropertyChangeFilter("MyProperty1")]
[PropertyChangeFilter("MyProperty2")]
void MyPropertiesChanged(object sender, PropertyChangedEventArgsEx e)
{
// Only properties whose names match either "MyProperty1"
// or "MyProperty2" will fire this event handler.
}
******
* OR *
******
propertyChangeFilter.PropertyChanged +=
[PropertyChangeFilter("MyProperty1")]
[PropertyChangeFilter("MyProperty2")]
(_, _) =>
{
// Only properties whose names match either "MyProperty1"
// or "MyProperty2" will fire this event handler.
};
*************************
* *
* Observable Mode *
* *
*************************
using var cancellation = propertyChangeFilter.ObservePropertyChanging<int>("MyProperty", MyPropertyChanging);
void MyPropertyChanging(string propertyName, int oldValue, int newValue)
{
// Only the property whose name match
// "MyProperty" will fire this method.
}
//The observer is delisted when cancellation is disposed
******
* OR *
******
using var cancellation = propertyChangeFilter.ObservePropertyChanged<bool>("MyProperty", (_, s) =>
{
// Only the property whose name match
// "MyProperty" will fire this action.
});
//The observer is delisted when cancellation is disposed
*/
#endregion
public abstract class PropertyChangeFilter
{
private readonly Dictionary<string, List<Delegate>> propertyChangedActions = new();
private readonly Dictionary<string, List<Delegate>> propertyChangingActions = new();
private readonly List<KeyValuePair<PropertyChangedEventHandlerEx, PropertyChangedEventHandlerEx>> changedFilters = new();
private readonly List<KeyValuePair<PropertyChangingEventHandlerEx, PropertyChangingEventHandlerEx>> changingFilters = new();
public PropertyChangeFilter()
{
PropertyChanging += Configuration_PropertyChanging;
PropertyChanged += Configuration_PropertyChanged;
}
#region Events
protected void OnPropertyChanged(string propertyName, object newValue)
=> _propertyChanged?.Invoke(this, new(propertyName, newValue));
protected void OnPropertyChanging(string propertyName, object oldValue, object newValue)
=> _propertyChanging?.Invoke(this, new(propertyName, oldValue, newValue));
private PropertyChangedEventHandlerEx _propertyChanged;
private PropertyChangingEventHandlerEx _propertyChanging;
public event PropertyChangedEventHandlerEx PropertyChanged
{
add
{
var attributes = Attribute.GetCustomAttributes(value.Method, typeof(PropertyChangeFilterAttribute)) as PropertyChangeFilterAttribute[];
if (attributes.Any())
{
var matches = attributes.Select(a => a.PropertyName).ToArray();
void filterer(object s, PropertyChangedEventArgsEx e)
{
if (e.PropertyName.In(matches)) value(s, e);
}
changedFilters.Add(new(value, filterer));
_propertyChanged += filterer;
}
else
_propertyChanged += value;
}
remove
{
var del = changedFilters.LastOrDefault(d => d.Key == value);
if (del.Key is null)
_propertyChanged -= value;
else
{
_propertyChanged -= del.Value;
changedFilters.Remove(del);
}
}
}
public event PropertyChangingEventHandlerEx PropertyChanging
{
add
{
var attributes = Attribute.GetCustomAttributes(value.Method, typeof(PropertyChangeFilterAttribute)) as PropertyChangeFilterAttribute[];
if (attributes.Any())
{
var matches = attributes.Select(a => a.PropertyName).ToArray();
void filterer(object s, PropertyChangingEventArgsEx e)
{
if (e.PropertyName.In(matches)) value(s, e);
}
changingFilters.Add(new(value, filterer));
_propertyChanging += filterer;
}
else
_propertyChanging += value;
}
remove
{
var del = changingFilters.LastOrDefault(d => d.Key == value);
if (del.Key is null)
_propertyChanging -= value;
else
{
_propertyChanging -= del.Value;
changingFilters.Remove(del);
}
}
}
#endregion
#region Observables
/// <summary>
/// Clear all subscriptions to Property<b>Changed</b> for <paramref name="propertyName"/>
/// </summary>
public void ClearChangedSubscriptions(string propertyName)
{
if (propertyChangedActions.ContainsKey(propertyName)
&& propertyChangedActions[propertyName] is not null)
propertyChangedActions[propertyName].Clear();
}
/// <summary>
/// Clear all subscriptions to Property<b>Changing</b> for <paramref name="propertyName"/>
/// </summary>
public void ClearChangingSubscriptions(string propertyName)
{
if (propertyChangingActions.ContainsKey(propertyName)
&& propertyChangingActions[propertyName] is not null)
propertyChangingActions[propertyName].Clear();
}
/// <summary>
/// Add an action to be executed when a property's value has changed
/// </summary>
/// <typeparam name="T">The <paramref name="propertyName"/>'s <see cref="Type"/></typeparam>
/// <param name="propertyName">Name of the property whose change triggers the <paramref name="action"/></param>
/// <param name="action">Action to be executed with parameters: <paramref name="propertyName"/> and <strong>NewValue</strong></param>
/// <returns>A reference to an interface that allows observers to stop receiving notifications before the provider has finished sending them.</returns>
public IDisposable ObservePropertyChanged<T>(string propertyName, Action<string, T> action)
{
validateSubscriber<T>(propertyName, action);
if (!propertyChangedActions.ContainsKey(propertyName))
propertyChangedActions.Add(propertyName, new List<Delegate>());
var actionlist = propertyChangedActions[propertyName];
if (!actionlist.Contains(action))
actionlist.Add(action);
return new Unsubscriber(actionlist, action);
}
/// <summary>
/// Add an action to be executed when a property's value is changing
/// </summary>
/// <typeparam name="T">The <paramref name="propertyName"/>'s <see cref="Type"/></typeparam>
/// <param name="propertyName">Name of the property whose change triggers the <paramref name="action"/></param>
/// <param name="action">Action to be executed with parameters: <paramref name="propertyName"/>, <b>OldValue</b>, and <b>NewValue</b></param>
/// <returns>A reference to an interface that allows observers to stop receiving notifications before the provider has finished sending them.</returns>
public IDisposable ObservePropertyChanging<T>(string propertyName, Action<string, T, T> action)
{
validateSubscriber<T>(propertyName, action);
if (!propertyChangingActions.ContainsKey(propertyName))
propertyChangingActions.Add(propertyName, new List<Delegate>());
var actionlist = propertyChangingActions[propertyName];
if (!actionlist.Contains(action))
actionlist.Add(action);
return new Unsubscriber(actionlist, action);
}
private void validateSubscriber<T>(string propertyName, Delegate action)
{
ArgumentValidator.EnsureNotNullOrWhiteSpace(propertyName, nameof(propertyName));
ArgumentValidator.EnsureNotNull(action, nameof(action));
var propertyInfo = GetType().GetProperty(propertyName);
if (propertyInfo is null)
throw new MissingMemberException($"{nameof(Configuration)}.{propertyName} does not exist.");
if (propertyInfo.PropertyType != typeof(T))
throw new InvalidCastException($"{nameof(Configuration)}.{propertyName} is {propertyInfo.PropertyType}, but parameter is {typeof(T)}.");
}
private void Configuration_PropertyChanged(object sender, PropertyChangedEventArgsEx e)
{
if (propertyChangedActions.ContainsKey(e.PropertyName))
{
foreach (var action in propertyChangedActions[e.PropertyName])
{
action.DynamicInvoke(e.PropertyName, e.NewValue);
}
}
}
private void Configuration_PropertyChanging(object sender, PropertyChangingEventArgsEx e)
{
if (propertyChangingActions.ContainsKey(e.PropertyName))
{
foreach (var action in propertyChangingActions[e.PropertyName])
{
action.DynamicInvoke(e.PropertyName, e.OldValue, e.NewValue);
}
}
}
private class Unsubscriber : IDisposable
{
private List<Delegate> _observers;
private Delegate _observer;
internal Unsubscriber(List<Delegate> observers, Delegate observer)
{
_observers = observers;
_observer = observer;
}
public void Dispose()
{
if (_observers.Contains(_observer))
_observers.Remove(_observer);
}
}
#endregion
}
public delegate void PropertyChangedEventHandlerEx(object sender, PropertyChangedEventArgsEx e);
public delegate void PropertyChangingEventHandlerEx(object sender, PropertyChangingEventArgsEx e);
public class PropertyChangedEventArgsEx : PropertyChangedEventArgs
{
public object NewValue { get; }
public PropertyChangedEventArgsEx(string propertyName, object newValue) : base(propertyName)
{
NewValue = newValue;
}
}
public class PropertyChangingEventArgsEx : PropertyChangingEventArgs
{
public object OldValue { get; }
public object NewValue { get; }
public PropertyChangingEventArgsEx(string propertyName, object oldValue, object newValue) : base(propertyName)
{
OldValue = oldValue;
NewValue = newValue;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class PropertyChangeFilterAttribute : Attribute
{
public string PropertyName { get; }
public PropertyChangeFilterAttribute(string propertyName)
{
PropertyName = propertyName;
}
}
}

View File

@ -59,14 +59,13 @@ namespace LibationWinForms
Configuration.Instance.PropertyChanged += Configuration_PropertyChanged;
}
private void Configuration_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
[PropertyChangeFilter(nameof(Configuration.AutoScan))]
private void Configuration_PropertyChanged(object sender, PropertyChangedEventArgsEx e)
{
if (e.PropertyName == nameof(Configuration.Instance.AutoScan))
{
// when autoscan setting is changed, update menu checkbox and run autoscan
updateAutoScanLibraryToolStripMenuItem(sender, e);
startAutoScan(sender, e);
}
// when autoscan setting is changed, update menu checkbox and run autoscan
updateAutoScanLibraryToolStripMenuItem(sender, e);
startAutoScan(sender, e);
}
private List<(string AccountId, string LocaleName)> preSaveDefaultAccounts;