diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml b/ILSpy/Options/DecompilerSettingsPanel.xaml index f257d8654..2078217e4 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml @@ -7,13 +7,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:options="clr-namespace:ICSharpCode.ILSpy.Options" d:DataContext="{d:DesignInstance options:DecompilerSettingsViewModel}"> - - - - - - - @@ -24,33 +17,26 @@ - - - - - - - - + - + + + + + + + + + + + + + + diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs index aec4ced54..9f542f243 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs @@ -16,12 +16,6 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -using System.ComponentModel; -using System.Linq; -using System.Reflection; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; using System.Xml.Linq; using ICSharpCode.ILSpyX.Settings; @@ -32,7 +26,7 @@ namespace ICSharpCode.ILSpy.Options /// Interaction logic for DecompilerSettingsPanel.xaml /// [ExportOptionPage(Title = nameof(Properties.Resources.Decompiler), Order = 10)] - internal partial class DecompilerSettingsPanel : UserControl, IOptionPage + internal partial class DecompilerSettingsPanel : IOptionPage { public DecompilerSettingsPanel() { @@ -59,58 +53,9 @@ namespace ICSharpCode.ILSpy.Options MainWindow.Instance.AssemblyListManager.UseDebugSymbols = newSettings.UseDebugSymbols; } - private void OnGroupChecked(object sender, RoutedEventArgs e) - { - CheckGroup((CollectionViewGroup)((CheckBox)sender).DataContext, true); - } - private void OnGroupUnchecked(object sender, RoutedEventArgs e) - { - CheckGroup((CollectionViewGroup)((CheckBox)sender).DataContext, false); - } - - void CheckGroup(CollectionViewGroup group, bool value) - { - foreach (var item in group.Items) - { - switch (item) - { - case CollectionViewGroup subGroup: - CheckGroup(subGroup, value); - break; - case CSharpDecompilerSetting setting: - setting.IsEnabled = value; - break; - } - } - } - - bool IsGroupChecked(CollectionViewGroup group) - { - bool value = true; - foreach (var item in group.Items) - { - switch (item) - { - case CollectionViewGroup subGroup: - value = value && IsGroupChecked(subGroup); - break; - case CSharpDecompilerSetting setting: - value = value && setting.IsEnabled; - break; - } - } - return value; - } - - private void OnGroupLoaded(object sender, RoutedEventArgs e) - { - CheckBox checkBox = (CheckBox)sender; - checkBox.IsChecked = IsGroupChecked((CollectionViewGroup)checkBox.DataContext); - } - public void LoadDefaults() { - MainWindow.Instance.CurrentDecompilerSettings = new Decompiler.DecompilerSettings(); + MainWindow.Instance.CurrentDecompilerSettings = new(); this.DataContext = new DecompilerSettingsViewModel(MainWindow.Instance.CurrentDecompilerSettings); } } diff --git a/ILSpy/Options/DecompilerSettingsViewModel.cs b/ILSpy/Options/DecompilerSettingsViewModel.cs index b7ce36fbe..3a574cca9 100644 --- a/ILSpy/Options/DecompilerSettingsViewModel.cs +++ b/ILSpy/Options/DecompilerSettingsViewModel.cs @@ -16,83 +16,120 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; +using TomsToolbox.Wpf; + namespace ICSharpCode.ILSpy.Options { - public class DecompilerSettingsViewModel : INotifyPropertyChanged + public sealed class DecompilerSettingsViewModel : ObservableObjectBase { - public CSharpDecompilerSetting[] Settings { get; set; } + public DecompilerSettingsGroupViewModel[] Settings { get; } public DecompilerSettingsViewModel(Decompiler.DecompilerSettings settings) { Settings = typeof(Decompiler.DecompilerSettings).GetProperties() .Where(p => p.GetCustomAttribute()?.Browsable != false) - .Select(p => new CSharpDecompilerSetting(p) { IsEnabled = (bool)p.GetValue(settings) }) + .Select(p => new DecompilerSettingsItemViewModel(p) { IsEnabled = p.GetValue(settings) is true }) .OrderBy(item => item.Category, NaturalStringComparer.Instance) - .ThenBy(item => item.Description) + .GroupBy(p => p.Category) + .Select(g => new DecompilerSettingsGroupViewModel(g.Key, g.OrderBy(i => i.Description).ToArray())) .ToArray(); } - public event PropertyChangedEventHandler PropertyChanged; - - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - public Decompiler.DecompilerSettings ToDecompilerSettings() { var settings = new Decompiler.DecompilerSettings(); - foreach (var item in Settings) + + foreach (var item in Settings.SelectMany(group => group.Settings)) { item.Property.SetValue(settings, item.IsEnabled); } + return settings; } } - public class CSharpDecompilerSetting : INotifyPropertyChanged + + public sealed class DecompilerSettingsGroupViewModel : ObservableObjectBase { - bool isEnabled; + private bool? _areAllItemsChecked; - public CSharpDecompilerSetting(PropertyInfo p) + public DecompilerSettingsGroupViewModel(string category, DecompilerSettingsItemViewModel[] settings) { - this.Property = p; - this.Category = GetResourceString(p.GetCustomAttribute()?.Category ?? Resources.Other); - this.Description = GetResourceString(p.GetCustomAttribute()?.Description ?? p.Name); - } + Settings = settings; + Category = category; - public PropertyInfo Property { get; } + _areAllItemsChecked = GetAreAllItemsChecked(Settings); - public bool IsEnabled { - get => isEnabled; + foreach (DecompilerSettingsItemViewModel viewModel in settings) + { + viewModel.PropertyChanged += Item_PropertyChanged; + } + } + + public bool? AreAllItemsChecked { + get => _areAllItemsChecked; set { - if (value != isEnabled) + SetProperty(ref _areAllItemsChecked, value); + + if (!value.HasValue) + return; + + foreach (var setting in Settings) { - isEnabled = value; - OnPropertyChanged(); + setting.IsEnabled = value.Value; } } } - public string Description { get; set; } + public string Category { get; } - public string Category { get; set; } + public DecompilerSettingsItemViewModel[] Settings { get; } - public event PropertyChangedEventHandler PropertyChanged; + private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(DecompilerSettingsItemViewModel.IsEnabled)) + { + AreAllItemsChecked = GetAreAllItemsChecked(Settings); + } + } - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + private static bool? GetAreAllItemsChecked(ICollection settings) { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + var numberOfEnabledItems = settings.Count(item => item.IsEnabled); + + if (numberOfEnabledItems == settings.Count) + return true; + + if (numberOfEnabledItems == 0) + return false; + + return null; + } + } + + public sealed class DecompilerSettingsItemViewModel(PropertyInfo property) : ObservableObjectBase + { + private bool _isEnabled; + + public PropertyInfo Property { get; } = property; + + public bool IsEnabled { + get => _isEnabled; + set => SetProperty(ref _isEnabled, value); } - static string GetResourceString(string key) + public string Description { get; set; } = GetResourceString(property.GetCustomAttribute()?.Description ?? property.Name); + + public string Category { get; set; } = GetResourceString(property.GetCustomAttribute()?.Category ?? Resources.Other); + + private static string GetResourceString(string key) { var str = !string.IsNullOrEmpty(key) ? Resources.ResourceManager.GetString(key) : null; return string.IsNullOrEmpty(key) || string.IsNullOrEmpty(str) ? key : str;