Browse Source

Consolidate all options/settings to use a consistent WFP MVVM pattern.

pull/3274/head
tom-englert 10 months ago committed by tom-englert
parent
commit
2454cd3a99
  1. 4
      ICSharpCode.ILSpyX/AssemblyListManager.cs
  2. 24
      ICSharpCode.ILSpyX/Settings/ILSpySettings.cs
  3. 42
      ICSharpCode.ILSpyX/Settings/IMiscSettings.cs
  4. 2
      ICSharpCode.ILSpyX/Settings/ISettingsFilePathProvider.cs
  5. 36
      ICSharpCode.ILSpyX/Settings/ISettingsProvider.cs
  6. 32
      ICSharpCode.ILSpyX/Settings/ISettingsSection.cs
  7. 14
      ILSpy.ReadyToRun/ReadyToRunDisassembler.cs
  8. 16
      ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml
  9. 105
      ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs
  10. 106
      ILSpy.ReadyToRun/ReadyToRunOptions.cs
  11. 20
      ILSpy/App.xaml.cs
  12. 56
      ILSpy/AssemblyTree/AssemblyListPaneModel.cs
  13. 1
      ILSpy/Commands/CheckForUpdatesCommand.cs
  14. 9
      ILSpy/Commands/SetThemeCommand.cs
  15. 4
      ILSpy/DecompilationOptions.cs
  16. 18
      ILSpy/LanguageSettings.cs
  17. 4
      ILSpy/MainWindow.xaml
  18. 12
      ILSpy/MainWindow.xaml.cs
  19. 48
      ILSpy/Options/DecompilerSettings.cs
  20. 56
      ILSpy/Options/DecompilerSettingsViewModel.cs
  21. 183
      ILSpy/Options/DisplaySettings.cs
  22. 3
      ILSpy/Options/DisplaySettingsPanel.xaml
  23. 20
      ILSpy/Options/DisplaySettingsViewModel.cs
  24. 35
      ILSpy/Options/MiscSettings.cs
  25. 11
      ILSpy/Options/MiscSettingsPanel.xaml
  26. 31
      ILSpy/Options/MiscSettingsPanel.xaml.cs
  27. 47
      ILSpy/Options/MiscSettingsViewModel.cs
  28. 5
      ILSpy/Options/OptionsDialog.xaml.cs
  29. 13
      ILSpy/Options/OptionsDialogViewModel.cs
  30. 8
      ILSpy/Search/SearchPane.xaml.cs
  31. 124
      ILSpy/SessionSettings.cs
  32. 10
      ILSpy/Themes/ThemeManager.cs
  33. 11
      ILSpy/TreeNodes/ILSpyTreeNode.cs
  34. 4
      ILSpy/Updates/NotifyOfUpdatesStrategy.cs
  35. 2
      ILSpy/Updates/UpdateSettings.cs
  36. 4
      ILSpy/Util/MessageBus.cs
  37. 157
      ILSpy/Util/SettingsService.cs
  38. 9
      ILSpy/Views/DebugSteps.xaml.cs
  39. 11
      TestPlugin/CustomOptionPage.xaml
  40. 92
      TestPlugin/CustomOptionPage.xaml.cs

4
ICSharpCode.ILSpyX/AssemblyListManager.cs

@ -59,9 +59,9 @@ namespace ICSharpCode.ILSpyX @@ -59,9 +59,9 @@ namespace ICSharpCode.ILSpyX
public bool UseDebugSymbols { get; set; }
public ObservableCollection<string> AssemblyLists { get; } = new ObservableCollection<string>();
public ObservableCollection<string> AssemblyLists { get; } = [];
public FileLoaderRegistry LoaderRegistry { get; } = new FileLoaderRegistry();
public FileLoaderRegistry LoaderRegistry { get; } = new();
/// <summary>
/// Loads an assembly list from the ILSpySettings.

24
ICSharpCode.ILSpyX/Settings/ILSpySettings.cs

@ -123,11 +123,6 @@ namespace ICSharpCode.ILSpyX.Settings @@ -123,11 +123,6 @@ namespace ICSharpCode.ILSpyX.Settings
}
}
void ISettingsProvider.Update(Action<XElement> action)
{
Update(action);
}
static string GetConfigFile()
{
if (null != SettingsFilePathProvider)
@ -148,17 +143,16 @@ namespace ICSharpCode.ILSpyX.Settings @@ -148,17 +143,16 @@ namespace ICSharpCode.ILSpyX.Settings
public MutexProtector(string name)
{
bool createdNew;
this.mutex = new Mutex(true, name, out createdNew);
if (!createdNew)
this.mutex = new Mutex(true, name, out bool createdNew);
if (createdNew)
return;
try
{
mutex.WaitOne();
}
catch (AbandonedMutexException)
{
try
{
mutex.WaitOne();
}
catch (AbandonedMutexException)
{
}
}
}

42
ICSharpCode.ILSpyX/Settings/IMiscSettings.cs

@ -1,42 +0,0 @@ @@ -1,42 +0,0 @@
// Copyright (c) 2022 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Xml.Linq;
namespace ICSharpCode.ILSpyX.Settings
{
public interface IMiscSettings
{
public bool AllowMultipleInstances { get; set; }
public bool LoadPreviousAssemblies { get; set; }
public static void Save(XElement root, IMiscSettings miscSettings)
{
var section = new XElement("MiscSettings");
section.SetAttributeValue(nameof(miscSettings.AllowMultipleInstances), miscSettings.AllowMultipleInstances);
section.SetAttributeValue(nameof(miscSettings.LoadPreviousAssemblies), miscSettings.LoadPreviousAssemblies);
XElement? existingElement = root.Element("MiscSettings");
if (existingElement != null)
existingElement.ReplaceWith(section);
else
root.Add(section);
}
}
}

2
ICSharpCode.ILSpyX/Settings/ISettingsFilePathProvider.cs

@ -16,8 +16,6 @@ @@ -16,8 +16,6 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
namespace ICSharpCode.ILSpyX.Settings
{
public interface ISettingsFilePathProvider

36
ICSharpCode.ILSpyX/Settings/ISettingsProvider.cs

@ -17,9 +17,6 @@ @@ -17,9 +17,6 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
namespace ICSharpCode.ILSpyX.Settings
@ -30,37 +27,6 @@ namespace ICSharpCode.ILSpyX.Settings @@ -30,37 +27,6 @@ namespace ICSharpCode.ILSpyX.Settings
void Update(Action<XElement> action);
public static Decompiler.DecompilerSettings LoadDecompilerSettings(ISettingsProvider settingsProvider)
{
XElement e = settingsProvider["DecompilerSettings"];
var newSettings = new Decompiler.DecompilerSettings();
var properties = typeof(Decompiler.DecompilerSettings).GetProperties()
.Where(p => p.GetCustomAttribute<BrowsableAttribute>()?.Browsable != false);
foreach (var p in properties)
{
var value = (bool?)e.Attribute(p.Name);
if (value.HasValue)
p.SetValue(newSettings, value.Value);
}
return newSettings;
}
public static void SaveDecompilerSettings(XElement root, Decompiler.DecompilerSettings newSettings)
{
var properties = typeof(Decompiler.DecompilerSettings).GetProperties()
.Where(p => p.GetCustomAttribute<BrowsableAttribute>()?.Browsable != false);
XElement section = new XElement("DecompilerSettings");
foreach (var p in properties)
{
section.SetAttributeValue(p.Name, p.GetValue(newSettings));
}
XElement? existingElement = root.Element("DecompilerSettings");
if (existingElement != null)
existingElement.ReplaceWith(section);
else
root.Add(section);
}
void SaveSettings(XElement section);
}
}

32
ICSharpCode.ILSpyX/Settings/ISettingsSection.cs

@ -1,32 +0,0 @@ @@ -1,32 +0,0 @@
// Copyright (c) 2022 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
namespace ICSharpCode.ILSpyX.Settings
{
public interface ISettingsSection<TSelf>
{
// This should be abstract, but that needs C# 11.0 (see IParseable<TSelf>)
// Keep it to be enabled in the future
public static TSelf Load(ISettingsProvider settingsProvider)
{
throw new NotImplementedException();
}
}
}

14
ILSpy.ReadyToRun/ReadyToRunDisassembler.cs

@ -51,11 +51,13 @@ namespace ICSharpCode.ILSpy.ReadyToRun @@ -51,11 +51,13 @@ namespace ICSharpCode.ILSpy.ReadyToRun
ReadyToRunMethod readyToRunMethod = runtimeFunction.Method;
WriteCommentLine(readyToRunMethod.SignatureString);
if (ReadyToRunOptions.GetIsShowGCInfo(null))
var options = SettingsService.Instance.GetSettings<ReadyToRunOptions>();
if (options.IsShowGCInfo)
{
if (readyToRunMethod.GcInfo != null)
{
string[] lines = readyToRunMethod.GcInfo.ToString().Split(Environment.NewLine);
string[] lines = readyToRunMethod.GcInfo.ToString()?.Split(Environment.NewLine) ?? [];
WriteCommentLine("GC info:");
foreach (string line in lines)
{
@ -69,12 +71,12 @@ namespace ICSharpCode.ILSpy.ReadyToRun @@ -69,12 +71,12 @@ namespace ICSharpCode.ILSpy.ReadyToRun
}
Dictionary<ulong, UnwindCode> unwindInfo = null;
if (ReadyToRunOptions.GetIsShowUnwindInfo(null) && bitness == 64)
if (options.IsShowUnwindInfo && bitness == 64)
{
unwindInfo = WriteUnwindInfo();
}
bool isShowDebugInfo = ReadyToRunOptions.GetIsShowDebugInfo(null);
bool isShowDebugInfo = options.IsShowDebugInfo;
DebugInfoHelper debugInfo = null;
if (isShowDebugInfo)
{
@ -98,7 +100,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun @@ -98,7 +100,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun
decoder.Decode(out instructions.AllocUninitializedElement());
}
string disassemblyFormat = ReadyToRunOptions.GetDisassemblyFormat(null);
string disassemblyFormat = options.DisassemblyFormat;
Formatter formatter = null;
if (disassemblyFormat.Equals(ReadyToRunOptions.intel))
{
@ -145,7 +147,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun @@ -145,7 +147,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun
}
}
}
if (ReadyToRunOptions.GetIsShowGCInfo(null))
if (options.IsShowGCInfo)
{
DecorateGCInfo(instr, baseInstrIP, readyToRunMethod.GcInfo);
}

16
ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml

@ -1,7 +1,11 @@ @@ -1,7 +1,11 @@
<UserControl x:Class="ICSharpCode.ILSpy.ReadyToRun.ReadyToRunOptionPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:properties="clr-namespace:ILSpy.ReadyToRun.Properties"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d:DesignHeight="500" d:DesignWidth="500" mc:Ignorable="d"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:readyToRun="clr-namespace:ICSharpCode.ILSpy.ReadyToRun"
d:DataContext="{d:DesignInstance readyToRun:ReadyToRunOptionsViewModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
@ -14,12 +18,12 @@ @@ -14,12 +18,12 @@
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Margin="3" Text="{x:Static properties:Resources.DisassemblyFormat}" />
<ComboBox Grid.Column="1" Margin="3" ItemsSource="{Binding DisassemblyFormats}" SelectedItem="{Binding DisassemblyFormat}" />
<TextBlock Grid.Row="1" Margin="3" Text="{x:Static properties:Resources.ShowStackUnwindInfo}"/>
<CheckBox Grid.Row="1" Grid.Column="1" Margin="3" IsChecked="{Binding IsShowUnwindInfo}" />
<ComboBox Grid.Row="0" Grid.Column="1" Margin="3" ItemsSource="{Binding Options.DisassemblyFormats}" SelectedItem="{Binding Options.DisassemblyFormat}" />
<TextBlock Grid.Row="1" Grid.Column="0" Margin="3" Text="{x:Static properties:Resources.ShowStackUnwindInfo}"/>
<CheckBox Grid.Row="1" Grid.Column="1" Margin="3" IsChecked="{Binding Options.IsShowUnwindInfo}" />
<TextBlock Grid.Row="2" Margin="3" Text="{x:Static properties:Resources.ShowDebugInfo}"/>
<CheckBox Grid.Row="2" Grid.Column="1" Margin="3" IsChecked="{Binding IsShowDebugInfo}" />
<CheckBox Grid.Row="2" Grid.Column="1" Margin="3" IsChecked="{Binding Options.IsShowDebugInfo}" />
<TextBlock Grid.Row="3" Margin="3" Text="{x:Static properties:Resources.ShowGCInfo}"/>
<CheckBox Grid.Row="3" Grid.Column="1" Margin="3" IsChecked="{Binding IsShowGCInfo}" />
<CheckBox Grid.Row="3" Grid.Column="1" Margin="3" IsChecked="{Binding Options.IsShowGCInfo}" />
</Grid>
</UserControl>

105
ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs

@ -16,114 +16,47 @@ @@ -16,114 +16,47 @@
// 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.ComponentModel.Composition;
using System.Windows.Controls;
using System.Xml.Linq;
using ICSharpCode.ILSpy.Options;
using ICSharpCode.ILSpyX.Settings;
using ICSharpCode.ILSpy.Util;
using TomsToolbox.Wpf;
using TomsToolbox.Wpf.Composition.Mef;
namespace ICSharpCode.ILSpy.ReadyToRun
{
[ExportOptionPage(Order = 40)]
[DataTemplate(typeof(ReadyToRunOptionsViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
partial class ReadyToRunOptionPage : UserControl, IOptionPage
partial class ReadyToRunOptionPage
{
public ReadyToRunOptionPage()
{
InitializeComponent();
}
public string Title => global::ILSpy.ReadyToRun.Properties.Resources.ReadyToRun;
public void Load(ILSpySettings spySettings)
{
Options s = new Options();
s.DisassemblyFormat = ReadyToRunOptions.GetDisassemblyFormat(spySettings);
s.IsShowUnwindInfo = ReadyToRunOptions.GetIsShowUnwindInfo(spySettings);
s.IsShowDebugInfo = ReadyToRunOptions.GetIsShowDebugInfo(spySettings);
s.IsShowGCInfo = ReadyToRunOptions.GetIsShowGCInfo(spySettings);
this.DataContext = s;
}
public void LoadDefaults()
{
this.DataContext = new Options();
}
public void Save(XElement root)
{
Options s = (Options)this.DataContext;
ReadyToRunOptions.SetDisassemblyOptions(root, s.DisassemblyFormat, s.IsShowUnwindInfo, s.IsShowDebugInfo, s.IsShowGCInfo);
}
}
internal class Options : INotifyPropertyChanged
[ExportOptionPage(Order = 40)]
[PartCreationPolicy(CreationPolicy.NonShared)]
class ReadyToRunOptionsViewModel : ObservableObject, IOptionPage
{
public string[] DisassemblyFormats {
get {
return ReadyToRunOptions.disassemblyFormats;
}
}
private bool isShowUnwindInfo;
public bool IsShowUnwindInfo {
get {
return isShowUnwindInfo;
}
set {
isShowUnwindInfo = value;
OnPropertyChanged(nameof(IsShowUnwindInfo));
}
}
private bool isShowDebugInfo;
private ReadyToRunOptions options;
public bool IsShowDebugInfo {
get {
return isShowDebugInfo;
}
set {
isShowDebugInfo = value;
OnPropertyChanged(nameof(IsShowDebugInfo));
}
public ReadyToRunOptions Options {
get => options;
set => SetProperty(ref options, value);
}
private bool isShowGCInfo;
public bool IsShowGCInfo {
get {
return isShowGCInfo;
}
set {
isShowGCInfo = value;
OnPropertyChanged(nameof(IsShowGCInfo));
}
}
private string disassemblyFormat;
public string Title => global::ILSpy.ReadyToRun.Properties.Resources.ReadyToRun;
public string DisassemblyFormat {
get { return disassemblyFormat; }
set {
if (disassemblyFormat != value)
{
disassemblyFormat = value;
OnPropertyChanged(nameof(DisassemblyFormat));
}
}
public void Load(SettingsSnapshot snapshot)
{
Options = snapshot.GetSettings<ReadyToRunOptions>();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
public void LoadDefaults()
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
Options.LoadFromSection(new("empty"));
}
}
}

106
ILSpy.ReadyToRun/ReadyToRunOptions.cs

@ -19,102 +19,72 @@ @@ -19,102 +19,72 @@
using System.Xml.Linq;
using ICSharpCode.ILSpy.Util;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.Settings;
using TomsToolbox.Wpf;
namespace ICSharpCode.ILSpy.ReadyToRun
{
internal class ReadyToRunOptions
internal partial class ReadyToRunOptions : ObservableObject, ISettingsSection
{
private static readonly XNamespace ns = "http://www.ilspy.net/ready-to-run";
internal static string intel = "Intel";
internal static string gas = "AT & T";
internal static string[] disassemblyFormats = new string[] { intel, gas };
public static string GetDisassemblyFormat(ILSpySettings settings)
{
settings ??= SettingsService.Instance.SpySettings;
internal static string[] disassemblyFormats = [intel, gas];
XElement e = settings[ns + "ReadyToRunOptions"];
XAttribute a = e.Attribute("DisassemblyFormat");
if (a == null)
{
return ReadyToRunOptions.intel;
}
else
{
return (string)a;
public string[] DisassemblyFormats {
get {
return disassemblyFormats;
}
}
public static bool GetIsShowUnwindInfo(ILSpySettings settings)
{
settings ??= SettingsService.Instance.SpySettings;
XElement e = settings[ns + "ReadyToRunOptions"];
XAttribute a = e.Attribute("IsShowUnwindInfo");
if (a == null)
{
return false;
}
else
{
return (bool)a;
}
private bool isShowUnwindInfo;
public bool IsShowUnwindInfo {
get => isShowUnwindInfo;
set => SetProperty(ref isShowUnwindInfo, value);
}
public static bool GetIsShowDebugInfo(ILSpySettings settings)
{
settings ??= SettingsService.Instance.SpySettings;
private bool isShowDebugInfo;
public bool IsShowDebugInfo {
get => isShowDebugInfo;
set => SetProperty(ref isShowDebugInfo, value);
}
XElement e = settings[ns + "ReadyToRunOptions"];
XAttribute a = e.Attribute("IsShowDebugInfo");
private bool isShowGCInfo;
public bool IsShowGCInfo {
get => isShowGCInfo;
set => SetProperty(ref isShowGCInfo, value);
}
if (a == null)
{
return true;
}
else
{
return (bool)a;
}
private string disassemblyFormat;
public string DisassemblyFormat {
get => disassemblyFormat;
set => SetProperty(ref disassemblyFormat, value);
}
public static bool GetIsShowGCInfo(ILSpySettings settings)
public XName SectionName { get; } = ns + "ReadyToRunOptions";
public void LoadFromSection(XElement e)
{
settings ??= SettingsService.Instance.SpySettings;
XAttribute format = e.Attribute("DisassemblyFormat");
DisassemblyFormat = format == null ? intel : (string)format;
XElement e = settings[ns + "ReadyToRunOptions"];
XAttribute a = e.Attribute("IsShowGCInfo");
XAttribute unwind = e.Attribute("IsShowUnwindInfo");
IsShowUnwindInfo = unwind != null && (bool)unwind;
if (a == null)
{
return false;
}
else
{
return (bool)a;
}
XAttribute debug = e.Attribute("IsShowDebugInfo");
IsShowDebugInfo = debug == null || (bool)debug;
XAttribute showGc = e.Attribute("IsShowGCInfo");
IsShowGCInfo = showGc != null && (bool)showGc;
}
public static void SetDisassemblyOptions(XElement root, string disassemblyFormat, bool isShowUnwindInfo, bool isShowDebugInfo, bool isShowGCInfo)
public void SaveToSection(XElement section)
{
XElement section = new XElement(ns + "ReadyToRunOptions");
section.SetAttributeValue("DisassemblyFormat", disassemblyFormat);
section.SetAttributeValue("IsShowUnwindInfo", isShowUnwindInfo);
section.SetAttributeValue("IsShowDebugInfo", isShowDebugInfo);
section.SetAttributeValue("IsShowGCInfo", isShowGCInfo);
XElement existingElement = root.Element(ns + "ReadyToRunOptions");
if (existingElement != null)
{
existingElement.ReplaceWith(section);
}
else
{
root.Add(section);
}
}
}
}

20
ILSpy/App.xaml.cs

@ -32,9 +32,7 @@ using System.Windows.Threading; @@ -32,9 +32,7 @@ using System.Windows.Threading;
using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Options;
using ICSharpCode.ILSpyX.Analyzers;
using ICSharpCode.ILSpyX.Settings;
using Medo.Application;
@ -66,11 +64,17 @@ namespace ICSharpCode.ILSpy @@ -66,11 +64,17 @@ namespace ICSharpCode.ILSpy
public App()
{
ILSpySettings.SettingsFilePathProvider = new ILSpySettingsFilePathProvider();
var cmdArgs = Environment.GetCommandLineArgs().Skip(1);
CommandLineArguments = CommandLineArguments.Create(cmdArgs);
bool forceSingleInstance = (CommandLineArguments.SingleInstance ?? true)
&& !SettingsService.Instance.MiscSettings.AllowMultipleInstances;
if (forceSingleInstance)
{
SingleInstance.Attach(); // will auto-exit for second instance
SingleInstance.NewInstanceDetected += SingleInstance_NewInstanceDetected;
}
SharpTreeNode.SetImagesProvider(new WpfWindowsTreeNodeImagesProvider());
InitializeComponent();
@ -85,14 +89,6 @@ namespace ICSharpCode.ILSpy @@ -85,14 +89,6 @@ namespace ICSharpCode.ILSpy
TaskScheduler.UnobservedTaskException += DotNet40_UnobservedTaskException;
InitializeMef().GetAwaiter().GetResult();
bool forceSingleInstance = (CommandLineArguments.SingleInstance ?? true)
&& !SettingsService.Instance.MiscSettings.AllowMultipleInstances;
if (forceSingleInstance)
{
SingleInstance.Attach(); // will auto-exit for second instance
SingleInstance.NewInstanceDetected += SingleInstance_NewInstanceDetected;
}
// Register the export provider so that it can be accessed from WPF/XAML components.
ExportProviderLocator.Register(ExportProvider);
// Add data templates registered via MEF.

56
ILSpy/AssemblyTree/AssemblyListPaneModel.cs

@ -77,30 +77,40 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -77,30 +77,40 @@ namespace ICSharpCode.ILSpy.AssemblyTree
ShortcutKey = new KeyGesture(Key.F6);
MessageBus<NavigateToReferenceEventArgs>.Subscribers += JumpToReference;
MessageBus<SessionSettingsChangedEventArgs>.Subscribers += (sender, e) => SessionSettings_PropertyChanged(sender, e);
MessageBus<LanguageSettingsChangedEventArgs>.Subscribers += (sender, e) => LanguageSettings_PropertyChanged(sender, e);
MessageBus<SettingsChangedEventArgs>.Subscribers += (sender, e) => Settings_PropertyChanged(sender, e);
var selectionChangeThrottle = new DispatcherThrottle(DispatcherPriority.Background, TreeView_SelectionChanged);
SelectedItems.CollectionChanged += (_, _) => selectionChangeThrottle.Tick();
}
private void SessionSettings_PropertyChanged(object sender, PropertyChangedEventArgs e)
private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var sessionSettings = SettingsService.Instance.SessionSettings;
switch (e.PropertyName)
if (sender is SessionSettings sessionSettings)
{
case nameof(SessionSettings.ActiveAssemblyList):
ShowAssemblyList(sessionSettings.ActiveAssemblyList);
break;
case nameof(SessionSettings.Theme):
// update syntax highlighting and force reload (AvalonEdit does not automatically refresh on highlighting change)
DecompilerTextView.RegisterHighlighting();
DecompileSelectedNodes(DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState);
break;
case nameof(SessionSettings.CurrentCulture):
MessageBox.Show(Properties.Resources.SettingsChangeRestartRequired, "ILSpy");
break;
switch (e.PropertyName)
{
case nameof(SessionSettings.ActiveAssemblyList):
ShowAssemblyList(sessionSettings.ActiveAssemblyList);
break;
case nameof(SessionSettings.Theme):
// update syntax highlighting and force reload (AvalonEdit does not automatically refresh on highlighting change)
DecompilerTextView.RegisterHighlighting();
DecompileSelectedNodes(
DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState);
break;
case nameof(SessionSettings.CurrentCulture):
MessageBox.Show(Properties.Resources.SettingsChangeRestartRequired, "ILSpy");
break;
}
}
else if (sender is LanguageSettings)
{
switch (e.PropertyName)
{
case nameof(LanguageSettings.Language) or nameof(LanguageSettings.LanguageVersion):
DecompileSelectedNodes(recordHistory: false);
break;
}
}
}
@ -136,7 +146,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -136,7 +146,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
/// Called on startup or when passed arguments via WndProc from a second instance.
/// In the format case, spySettings is non-null; in the latter it is null.
/// </summary>
public void HandleCommandLineArgumentsAfterShowList(CommandLineArguments args, ILSpySettings spySettings = null)
public void HandleCommandLineArgumentsAfterShowList(CommandLineArguments args, ISettingsProvider spySettings = null)
{
var sessionSettings = SettingsService.Instance.SessionSettings;
@ -172,7 +182,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -172,7 +182,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
});
}
public async void NavigateOnLaunch(string navigateTo, string[] activeTreeViewPath, ILSpySettings spySettings, List<LoadedAssembly> relevantAssemblies)
public async void NavigateOnLaunch(string navigateTo, string[] activeTreeViewPath, ISettingsProvider spySettings, List<LoadedAssembly> relevantAssemblies)
{
var initialSelection = SelectedItem;
if (navigateTo != null)
@ -458,14 +468,6 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -458,14 +468,6 @@ namespace ICSharpCode.ILSpy.AssemblyTree
AssemblyList.OpenAssembly(asm.Location);
}
void LanguageSettings_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName is nameof(LanguageSettings.Language) or nameof(LanguageSettings.LanguageVersion))
{
DecompileSelectedNodes(recordHistory: false);
}
}
public AssemblyTreeNode FindAssemblyNode(LoadedAssembly asm)
{
return assemblyListTreeNode.FindAssemblyNode(asm);

1
ILSpy/Commands/CheckForUpdatesCommand.cs

@ -20,7 +20,6 @@ @@ -20,7 +20,6 @@
using System.ComponentModel.Composition;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpyX.Settings;
namespace ICSharpCode.ILSpy
{

9
ILSpy/Commands/SetThemeCommand.cs

@ -6,7 +6,14 @@ namespace ICSharpCode.ILSpy.Commands @@ -6,7 +6,14 @@ namespace ICSharpCode.ILSpy.Commands
public override void Execute(object parameter)
{
if (parameter is string theme)
SettingsService.Instance.SessionSettings.Theme = theme;
{
var snapshot = SettingsService.Instance.CreateSnapshot();
var sessionSettings = snapshot.GetSettings<SessionSettings>();
sessionSettings.Theme = theme;
snapshot.Save();
}
}
}
}

4
ILSpy/DecompilationOptions.cs

@ -23,6 +23,8 @@ using ICSharpCode.Decompiler; @@ -23,6 +23,8 @@ using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.Options;
using ICSharpCode.ILSpyX;
using DecompilerSettings = ICSharpCode.ILSpy.Options.DecompilerSettings;
namespace ICSharpCode.ILSpy
{
/// <summary>
@ -87,7 +89,9 @@ namespace ICSharpCode.ILSpy @@ -87,7 +89,9 @@ namespace ICSharpCode.ILSpy
{
if (!Enum.TryParse(version?.Version, out Decompiler.CSharp.LanguageVersion languageVersion))
languageVersion = Decompiler.CSharp.LanguageVersion.Latest;
var newSettings = this.DecompilerSettings = settings.Clone();
newSettings.SetLanguageVersion(languageVersion);
newSettings.ExpandMemberDefinitions = displaySettings.ExpandMemberDefinitions;
newSettings.ExpandUsingDeclarations = displaySettings.ExpandUsingDeclarations;

18
ILSpy/LanguageSettings.cs

@ -25,12 +25,14 @@ using System.Xml.Linq; @@ -25,12 +25,14 @@ using System.Xml.Linq;
using ICSharpCode.ILSpyX;
using TomsToolbox.Wpf;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Represents the filters applied to the tree view.
/// </summary>
public class LanguageSettings : INotifyPropertyChanged
public class LanguageSettings : ObservableObject
{
/// <summary>
/// This dictionary is necessary to remember language versions across language changes. For example,
@ -172,20 +174,6 @@ namespace ICSharpCode.ILSpy @@ -172,20 +174,6 @@ namespace ICSharpCode.ILSpy
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
var args = new PropertyChangedEventArgs(propertyName);
PropertyChanged(this, args);
MessageBus.Send(this, new LanguageSettingsChangedEventArgs(args));
}
}
// This class has been initially called FilterSettings, but then has been Hijacked to store language settings as well.
// While the filter settings were some sort of local, the language settings are global. This is a bit of a mess.
// There has been a lot of workarounds cloning the FilterSettings to pass them down to the tree nodes, without messing up the global language settings.

4
ILSpy/MainWindow.xaml

@ -29,8 +29,6 @@ @@ -29,8 +29,6 @@
<ContentPresenter Content="{Binding Content}" />
</DataTemplate>
<toms:BindingRelay x:Key="WindowBinding" DataContext="{Binding}" />
</Window.Resources>
<b:Interaction.Behaviors>
@ -93,7 +91,7 @@ @@ -93,7 +91,7 @@
<Setter.Value>
<MultiBinding Converter="{x:Static toms:BinaryOperationConverter.Equality}" Mode="OneWay">
<Binding />
<Binding Source="{StaticResource WindowBinding}" Path="DataContext.SessionSettings.Theme" />
<Binding Path="DataContext.SessionSettings.Theme" RelativeSource="{RelativeSource FindAncestor, AncestorType=Window}"/>
</MultiBinding>
</Setter.Value>
</Setter>

12
ILSpy/MainWindow.xaml.cs

@ -34,6 +34,7 @@ using ICSharpCode.ILSpy.AssemblyTree; @@ -34,6 +34,7 @@ using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Search;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.Themes;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.Updates;
using ICSharpCode.ILSpyX.FileLoaders;
@ -70,6 +71,7 @@ namespace ICSharpCode.ILSpy @@ -70,6 +71,7 @@ namespace ICSharpCode.ILSpy
instance = this;
var sessionSettings = SettingsService.Instance.SessionSettings;
ThemeManager.Current.Theme = sessionSettings.Theme;
// Make sure Images are initialized on the UI thread.
this.Icon = Images.ILSpyIcon;
@ -165,7 +167,7 @@ namespace ICSharpCode.ILSpy @@ -165,7 +167,7 @@ namespace ICSharpCode.ILSpy
#region Update Check
string updateAvailableDownloadUrl;
public async Task ShowMessageIfUpdatesAvailableAsync(ILSpySettings spySettings, bool forceCheck = false)
public async Task ShowMessageIfUpdatesAvailableAsync(ISettingsProvider spySettings, bool forceCheck = false)
{
string downloadUrl;
if (forceCheck)
@ -337,14 +339,18 @@ namespace ICSharpCode.ILSpy @@ -337,14 +339,18 @@ namespace ICSharpCode.ILSpy
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
var sessionSettings = SettingsService.Instance.SessionSettings;
var snapshot = SettingsService.Instance.CreateSnapshot();
var sessionSettings = snapshot.GetSettings<SessionSettings>();
sessionSettings.ActiveAssemblyList = AssemblyTreeModel.AssemblyList.ListName;
sessionSettings.ActiveTreeViewPath = AssemblyTreeModel.SelectedPath;
sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(AssemblyTreeModel.SelectedItem);
sessionSettings.WindowBounds = this.RestoreBounds;
sessionSettings.DockLayout.Serialize(new XmlLayoutSerializer(dockManager));
sessionSettings.Save();
snapshot.Save();
}
private static string GetAutoLoadedAssemblyNode(SharpTreeNode node)

48
ILSpy/Options/DecompilerSettings.cs

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
using System.ComponentModel;
using ICSharpCode.ILSpyX.Settings;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
#nullable enable
namespace ICSharpCode.ILSpy.Options
{
public class DecompilerSettings : Decompiler.DecompilerSettings, ISettingsSection
{
static readonly PropertyInfo[] properties = typeof(Decompiler.DecompilerSettings).GetProperties()
.Where(p => p.GetCustomAttribute<BrowsableAttribute>()?.Browsable != false)
.ToArray();
public XName SectionName => "DecompilerSettings";
public void SaveToSection(XElement section)
{
foreach (var p in properties)
{
section.SetAttributeValue(p.Name, p.GetValue(this));
}
}
public void LoadFromSection(XElement section)
{
foreach (var p in properties)
{
var value = (bool?)section.Attribute(p.Name);
if (value.HasValue)
p.SetValue(this, value.Value);
}
}
public new DecompilerSettings Clone()
{
var section = new XElement("DecompilerSettings");
SaveToSection(section);
var newSettings = new DecompilerSettings();
newSettings.LoadFromSection(section);
return newSettings;
}
}
}

56
ILSpy/Options/DecompilerSettingsViewModel.cs

@ -21,12 +21,9 @@ using System.ComponentModel; @@ -21,12 +21,9 @@ using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpyX.Settings;
using TomsToolbox.Wpf;
@ -36,56 +33,46 @@ namespace ICSharpCode.ILSpy.Options @@ -36,56 +33,46 @@ namespace ICSharpCode.ILSpy.Options
[PartCreationPolicy(CreationPolicy.NonShared)]
public sealed class DecompilerSettingsViewModel : ObservableObjectBase, IOptionPage
{
private DecompilerSettingsGroupViewModel[] settings;
private static readonly PropertyInfo[] propertyInfos = typeof(Decompiler.DecompilerSettings).GetProperties()
.Where(p => p.GetCustomAttribute<BrowsableAttribute>()?.Browsable != false)
.ToArray();
public string Title => Resources.Decompiler;
private DecompilerSettingsGroupViewModel[] settings;
public DecompilerSettingsGroupViewModel[] Settings {
get => settings;
set => SetProperty(ref settings, value);
}
public void Load(ILSpySettings spySettings)
private DecompilerSettings decompilerSettings;
public void Load(SettingsSnapshot snapshot)
{
Load(ISettingsProvider.LoadDecompilerSettings(spySettings));
decompilerSettings = snapshot.GetSettings<DecompilerSettings>();
LoadSettings();
}
private void Load(DecompilerSettings decompilerSettings)
private void LoadSettings()
{
this.Settings = typeof(Decompiler.DecompilerSettings).GetProperties()
.Where(p => p.GetCustomAttribute<BrowsableAttribute>()?.Browsable != false)
.Select(p => new DecompilerSettingsItemViewModel(p) { IsEnabled = p.GetValue(decompilerSettings) is true })
this.Settings = propertyInfos
.Select(p => new DecompilerSettingsItemViewModel(p, decompilerSettings))
.OrderBy(item => item.Category, NaturalStringComparer.Instance)
.GroupBy(p => p.Category)
.Select(g => new DecompilerSettingsGroupViewModel(g.Key, g.OrderBy(i => i.Description).ToArray()))
.ToArray();
}
public void Save(XElement root)
{
var newSettings = ToDecompilerSettings();
ISettingsProvider.SaveDecompilerSettings(root, newSettings);
SettingsService.Instance.DecompilerSettings = newSettings;
SettingsService.Instance.AssemblyListManager.ApplyWinRTProjections = newSettings.ApplyWindowsRuntimeProjections;
SettingsService.Instance.AssemblyListManager.UseDebugSymbols = newSettings.UseDebugSymbols;
}
public void LoadDefaults()
{
Load(new DecompilerSettings());
}
private DecompilerSettings ToDecompilerSettings()
{
var newSettings = new DecompilerSettings();
var defaults = new Decompiler.DecompilerSettings();
foreach (var item in Settings.SelectMany(group => group.Settings))
foreach (var propertyInfo in propertyInfos)
{
item.Property.SetValue(newSettings, item.IsEnabled);
propertyInfo.SetValue(decompilerSettings, propertyInfo.GetValue(defaults));
}
return newSettings;
LoadSettings();
}
}
@ -147,15 +134,20 @@ namespace ICSharpCode.ILSpy.Options @@ -147,15 +134,20 @@ namespace ICSharpCode.ILSpy.Options
}
}
public sealed class DecompilerSettingsItemViewModel(PropertyInfo property) : ObservableObjectBase
public sealed class DecompilerSettingsItemViewModel(PropertyInfo property, DecompilerSettings decompilerSettings) : ObservableObjectBase
{
private bool isEnabled;
private bool isEnabled = property.GetValue(decompilerSettings) is true;
public PropertyInfo Property { get; } = property;
public bool IsEnabled {
get => isEnabled;
set => SetProperty(ref isEnabled, value);
set {
if (SetProperty(ref isEnabled, value))
{
property.SetValue(decompilerSettings, value);
}
}
}
public string Description { get; set; } = GetResourceString(property.GetCustomAttribute<DescriptionAttribute>()?.Description ?? property.Name);

183
ILSpy/Options/DisplaySettings.cs

@ -19,9 +19,6 @@ @@ -19,9 +19,6 @@
using System.Windows.Media;
using System.Xml.Linq;
using ICSharpCode.ILSpy.Themes;
using ICSharpCode.ILSpyX.Settings;
using TomsToolbox.Wpf;
namespace ICSharpCode.ILSpy.Options
@ -29,264 +26,176 @@ namespace ICSharpCode.ILSpy.Options @@ -29,264 +26,176 @@ namespace ICSharpCode.ILSpy.Options
/// <summary>
/// Description of DisplaySettings.
/// </summary>
public class DisplaySettings : ObservableObject
public class DisplaySettings : ObservableObject, ISettingsSection
{
public DisplaySettings()
{
this.theme = ThemeManager.Current.DefaultTheme;
this.selectedFont = new FontFamily("Consolas");
this.selectedFontSize = 10.0 * 4 / 3;
this.sortResults = true;
this.indentationUseTabs = true;
this.indentationSize = 4;
this.indentationTabSize = 4;
this.highlightMatchingBraces = true;
}
string theme;
public string Theme {
get => theme;
set => SetProperty(ref theme, value);
}
FontFamily selectedFont;
public FontFamily SelectedFont {
get => selectedFont;
set => SetProperty(ref selectedFont, value);
}
double selectedFontSize;
public double SelectedFontSize {
get => selectedFontSize;
set => SetProperty(ref selectedFontSize, value);
}
bool showLineNumbers;
public bool ShowLineNumbers {
get => showLineNumbers;
set => SetProperty(ref showLineNumbers, value);
}
bool showMetadataTokens;
public bool ShowMetadataTokens {
get => showMetadataTokens;
set => SetProperty(ref showMetadataTokens, value);
}
bool showMetadataTokensInBase10;
public bool ShowMetadataTokensInBase10 {
get => showMetadataTokensInBase10;
set => SetProperty(ref showMetadataTokensInBase10, value);
}
bool enableWordWrap;
public bool EnableWordWrap {
get => enableWordWrap;
set => SetProperty(ref enableWordWrap, value);
}
bool sortResults;
public bool SortResults {
get => sortResults;
set => SetProperty(ref sortResults, value);
}
bool foldBraces;
public bool FoldBraces {
get => foldBraces;
set => SetProperty(ref foldBraces, value);
}
bool expandMemberDefinitions;
public bool ExpandMemberDefinitions {
get => expandMemberDefinitions;
set => SetProperty(ref expandMemberDefinitions, value);
}
bool expandUsingDeclarations;
public bool ExpandUsingDeclarations {
get => expandUsingDeclarations;
set => SetProperty(ref expandUsingDeclarations, value);
}
bool showDebugInfo;
public bool ShowDebugInfo {
get => showDebugInfo;
set => SetProperty(ref showDebugInfo, value);
}
bool indentationUseTabs;
public bool IndentationUseTabs {
get => indentationUseTabs;
set => SetProperty(ref indentationUseTabs, value);
}
int indentationTabSize;
public int IndentationTabSize {
get => indentationTabSize;
set => SetProperty(ref indentationTabSize, value);
}
int indentationSize;
public int IndentationSize {
get => indentationSize;
set => SetProperty(ref indentationSize, value);
}
bool highlightMatchingBraces;
public bool HighlightMatchingBraces {
get => highlightMatchingBraces;
set => SetProperty(ref highlightMatchingBraces, value);
}
bool highlightCurrentLine;
public bool HighlightCurrentLine {
get => highlightCurrentLine;
set => SetProperty(ref highlightCurrentLine, value);
}
bool hideEmptyMetadataTables;
public bool HideEmptyMetadataTables {
get => hideEmptyMetadataTables;
set => SetProperty(ref hideEmptyMetadataTables, value);
}
bool useNestedNamespaceNodes;
public bool UseNestedNamespaceNodes {
get => useNestedNamespaceNodes;
set => SetProperty(ref useNestedNamespaceNodes, value);
}
private bool styleWindowTitleBar;
public bool StyleWindowTitleBar {
get => styleWindowTitleBar;
set => SetProperty(ref styleWindowTitleBar, value);
}
private bool showRawOffsetsAndBytesBeforeInstruction;
public bool ShowRawOffsetsAndBytesBeforeInstruction {
get => showRawOffsetsAndBytesBeforeInstruction;
set => SetProperty(ref showRawOffsetsAndBytesBeforeInstruction, value);
}
public void CopyValues(DisplaySettings s)
{
this.Theme = s.Theme;
this.SelectedFont = s.selectedFont;
this.SelectedFontSize = s.selectedFontSize;
this.ShowLineNumbers = s.showLineNumbers;
this.ShowMetadataTokens = s.showMetadataTokens;
this.ShowMetadataTokensInBase10 = s.showMetadataTokensInBase10;
this.ShowDebugInfo = s.showDebugInfo;
this.EnableWordWrap = s.enableWordWrap;
this.SortResults = s.sortResults;
this.FoldBraces = s.foldBraces;
this.ExpandMemberDefinitions = s.expandMemberDefinitions;
this.ExpandUsingDeclarations = s.expandUsingDeclarations;
this.IndentationUseTabs = s.indentationUseTabs;
this.IndentationTabSize = s.indentationTabSize;
this.IndentationSize = s.indentationSize;
this.HighlightMatchingBraces = s.highlightMatchingBraces;
this.HighlightCurrentLine = s.highlightCurrentLine;
this.HideEmptyMetadataTables = s.hideEmptyMetadataTables;
this.UseNestedNamespaceNodes = s.useNestedNamespaceNodes;
this.ShowRawOffsetsAndBytesBeforeInstruction = s.showRawOffsetsAndBytesBeforeInstruction;
this.StyleWindowTitleBar = s.styleWindowTitleBar;
}
public XName SectionName => "DisplaySettings";
public static DisplaySettings Load(ILSpySettings settings, SessionSettings sessionSettings = null)
public void LoadFromSection(XElement section)
{
XElement e = settings["DisplaySettings"];
var s = new DisplaySettings {
SelectedFont = new FontFamily((string)e.Attribute("Font") ?? "Consolas"),
SelectedFontSize = (double?)e.Attribute("FontSize") ?? 10.0 * 4 / 3,
ShowLineNumbers = (bool?)e.Attribute("ShowLineNumbers") ?? false,
ShowMetadataTokens = (bool?)e.Attribute("ShowMetadataTokens") ?? false,
ShowMetadataTokensInBase10 = (bool?)e.Attribute("ShowMetadataTokensInBase10") ?? false,
ShowDebugInfo = (bool?)e.Attribute("ShowDebugInfo") ?? false,
EnableWordWrap = (bool?)e.Attribute("EnableWordWrap") ?? false,
SortResults = (bool?)e.Attribute("SortResults") ?? true,
FoldBraces = (bool?)e.Attribute("FoldBraces") ?? false,
ExpandMemberDefinitions = (bool?)e.Attribute("ExpandMemberDefinitions") ?? false,
ExpandUsingDeclarations = (bool?)e.Attribute("ExpandUsingDeclarations") ?? false,
IndentationUseTabs = (bool?)e.Attribute("IndentationUseTabs") ?? true,
IndentationSize = (int?)e.Attribute("IndentationSize") ?? 4,
IndentationTabSize = (int?)e.Attribute("IndentationTabSize") ?? 4,
HighlightMatchingBraces = (bool?)e.Attribute("HighlightMatchingBraces") ?? true,
HighlightCurrentLine = (bool?)e.Attribute("HighlightCurrentLine") ?? false,
HideEmptyMetadataTables = (bool?)e.Attribute("HideEmptyMetadataTables") ?? true,
UseNestedNamespaceNodes = (bool?)e.Attribute("UseNestedNamespaceNodes") ?? false,
ShowRawOffsetsAndBytesBeforeInstruction = (bool?)e.Attribute("ShowRawOffsetsAndBytesBeforeInstruction") ?? false,
StyleWindowTitleBar = (bool?)e.Attribute("StyleWindowTitleBar") ?? false,
Theme = (sessionSettings ?? SettingsService.Instance.SessionSettings).Theme
};
return s;
}
public void Save(XElement root)
SelectedFont = new FontFamily((string)section.Attribute("Font") ?? "Consolas");
SelectedFontSize = (double?)section.Attribute("FontSize") ?? 10.0 * 4 / 3;
ShowLineNumbers = (bool?)section.Attribute("ShowLineNumbers") ?? false;
ShowMetadataTokens = (bool?)section.Attribute("ShowMetadataTokens") ?? false;
ShowMetadataTokensInBase10 = (bool?)section.Attribute("ShowMetadataTokensInBase10") ?? false;
ShowDebugInfo = (bool?)section.Attribute("ShowDebugInfo") ?? false;
EnableWordWrap = (bool?)section.Attribute("EnableWordWrap") ?? false;
SortResults = (bool?)section.Attribute("SortResults") ?? true;
FoldBraces = (bool?)section.Attribute("FoldBraces") ?? false;
ExpandMemberDefinitions = (bool?)section.Attribute("ExpandMemberDefinitions") ?? false;
ExpandUsingDeclarations = (bool?)section.Attribute("ExpandUsingDeclarations") ?? false;
IndentationUseTabs = (bool?)section.Attribute("IndentationUseTabs") ?? true;
IndentationSize = (int?)section.Attribute("IndentationSize") ?? 4;
IndentationTabSize = (int?)section.Attribute("IndentationTabSize") ?? 4;
HighlightMatchingBraces = (bool?)section.Attribute("HighlightMatchingBraces") ?? true;
HighlightCurrentLine = (bool?)section.Attribute("HighlightCurrentLine") ?? false;
HideEmptyMetadataTables = (bool?)section.Attribute("HideEmptyMetadataTables") ?? true;
UseNestedNamespaceNodes = (bool?)section.Attribute("UseNestedNamespaceNodes") ?? false;
ShowRawOffsetsAndBytesBeforeInstruction = (bool?)section.Attribute("ShowRawOffsetsAndBytesBeforeInstruction") ?? false;
StyleWindowTitleBar = (bool?)section.Attribute("StyleWindowTitleBar") ?? false;
}
public void SaveToSection(XElement section)
{
var s = this;
var section = new XElement("DisplaySettings");
section.SetAttributeValue("Font", s.SelectedFont.Source);
section.SetAttributeValue("FontSize", s.SelectedFontSize);
section.SetAttributeValue("ShowLineNumbers", s.ShowLineNumbers);
section.SetAttributeValue("ShowMetadataTokens", s.ShowMetadataTokens);
section.SetAttributeValue("ShowMetadataTokensInBase10", s.ShowMetadataTokensInBase10);
section.SetAttributeValue("ShowDebugInfo", s.ShowDebugInfo);
section.SetAttributeValue("EnableWordWrap", s.EnableWordWrap);
section.SetAttributeValue("SortResults", s.SortResults);
section.SetAttributeValue("FoldBraces", s.FoldBraces);
section.SetAttributeValue("ExpandMemberDefinitions", s.ExpandMemberDefinitions);
section.SetAttributeValue("ExpandUsingDeclarations", s.ExpandUsingDeclarations);
section.SetAttributeValue("IndentationUseTabs", s.IndentationUseTabs);
section.SetAttributeValue("IndentationSize", s.IndentationSize);
section.SetAttributeValue("IndentationTabSize", s.IndentationTabSize);
section.SetAttributeValue("HighlightMatchingBraces", s.HighlightMatchingBraces);
section.SetAttributeValue("HighlightCurrentLine", s.HighlightCurrentLine);
section.SetAttributeValue("HideEmptyMetadataTables", s.HideEmptyMetadataTables);
section.SetAttributeValue("UseNestedNamespaceNodes", s.UseNestedNamespaceNodes);
section.SetAttributeValue("ShowRawOffsetsAndBytesBeforeInstruction", s.ShowRawOffsetsAndBytesBeforeInstruction);
section.SetAttributeValue("StyleWindowTitleBar", s.StyleWindowTitleBar);
SettingsService.Instance.SessionSettings.Theme = s.Theme;
var sessionSettings = SettingsService.Instance.SessionSettings.ToXml();
SettingsService.Instance.DisplaySettings.CopyValues(s);
Update(section);
Update(sessionSettings);
void Update(XElement element)
{
var existingElement = root.Element(element.Name);
if (existingElement != null)
existingElement.ReplaceWith(element);
else
root.Add(element);
}
section.SetAttributeValue("Font", SelectedFont.Source);
section.SetAttributeValue("FontSize", SelectedFontSize);
section.SetAttributeValue("ShowLineNumbers", ShowLineNumbers);
section.SetAttributeValue("ShowMetadataTokens", ShowMetadataTokens);
section.SetAttributeValue("ShowMetadataTokensInBase10", ShowMetadataTokensInBase10);
section.SetAttributeValue("ShowDebugInfo", ShowDebugInfo);
section.SetAttributeValue("EnableWordWrap", EnableWordWrap);
section.SetAttributeValue("SortResults", SortResults);
section.SetAttributeValue("FoldBraces", FoldBraces);
section.SetAttributeValue("ExpandMemberDefinitions", ExpandMemberDefinitions);
section.SetAttributeValue("ExpandUsingDeclarations", ExpandUsingDeclarations);
section.SetAttributeValue("IndentationUseTabs", IndentationUseTabs);
section.SetAttributeValue("IndentationSize", IndentationSize);
section.SetAttributeValue("IndentationTabSize", IndentationTabSize);
section.SetAttributeValue("HighlightMatchingBraces", HighlightMatchingBraces);
section.SetAttributeValue("HighlightCurrentLine", HighlightCurrentLine);
section.SetAttributeValue("HideEmptyMetadataTables", HideEmptyMetadataTables);
section.SetAttributeValue("UseNestedNamespaceNodes", UseNestedNamespaceNodes);
section.SetAttributeValue("ShowRawOffsetsAndBytesBeforeInstruction", ShowRawOffsetsAndBytesBeforeInstruction);
section.SetAttributeValue("StyleWindowTitleBar", StyleWindowTitleBar);
}
}
}

3
ILSpy/Options/DisplaySettingsPanel.xaml

@ -3,7 +3,6 @@ @@ -3,7 +3,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:local="clr-namespace:ICSharpCode.ILSpy.Options"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes"
@ -12,7 +11,7 @@ @@ -12,7 +11,7 @@
<StackPanel Orientation="Vertical">
<DockPanel>
<Label DockPanel.Dock="Left" Content="{x:Static properties:Resources.DisplaySettingsPanel_Theme}" />
<ComboBox ItemsSource="{x:Static themes:ThemeManager.AllThemes}" SelectedItem="{Binding Settings.Theme}" VerticalContentAlignment="Center" Margin="0,0,8,0" />
<ComboBox ItemsSource="{x:Static themes:ThemeManager.AllThemes}" SelectedItem="{Binding SessionSettings.Theme}" VerticalContentAlignment="Center" Margin="0,0,8,0" />
</DockPanel>
<GroupBox Header="{x:Static properties:Resources.Font}">
<Grid>

20
ILSpy/Options/DisplaySettingsViewModel.cs

@ -8,6 +8,7 @@ using System.Threading.Tasks; @@ -8,6 +8,7 @@ using System.Threading.Tasks;
using System.Windows;
using TomsToolbox.Wpf;
using ICSharpCode.ILSpy.Themes;
namespace ICSharpCode.ILSpy.Options
{
@ -17,6 +18,7 @@ namespace ICSharpCode.ILSpy.Options @@ -17,6 +18,7 @@ namespace ICSharpCode.ILSpy.Options
{
private DisplaySettings settings = new();
private FontFamily[] fontFamilies;
private SessionSettings sessionSettings;
public DisplaySettingsViewModel()
{
@ -40,6 +42,11 @@ namespace ICSharpCode.ILSpy.Options @@ -40,6 +42,11 @@ namespace ICSharpCode.ILSpy.Options
set => SetProperty(ref settings, value);
}
public SessionSettings SessionSettings {
get => sessionSettings;
set => SetProperty(ref sessionSettings, value);
}
public FontFamily[] FontFamilies {
get => fontFamilies;
set => SetProperty(ref fontFamilies, value);
@ -47,9 +54,10 @@ namespace ICSharpCode.ILSpy.Options @@ -47,9 +54,10 @@ namespace ICSharpCode.ILSpy.Options
public int[] FontSizes { get; } = Enumerable.Range(6, 24 - 6 + 1).ToArray();
public void Load(ILSpySettings spySettings)
public void Load(SettingsSnapshot snapshot)
{
Settings = DisplaySettings.Load(spySettings);
Settings = snapshot.GetSettings<DisplaySettings>();
SessionSettings = snapshot.GetSettings<SessionSettings>();
}
static bool IsSymbolFont(FontFamily fontFamily)
@ -77,14 +85,10 @@ namespace ICSharpCode.ILSpy.Options @@ -77,14 +85,10 @@ namespace ICSharpCode.ILSpy.Options
.ToArray();
}
public void Save(XElement root)
{
Settings.Save(root);
}
public void LoadDefaults()
{
Settings = new();
Settings.LoadFromSection(new XElement("empty"));
SessionSettings.Theme = ThemeManager.Current.DefaultTheme;
}
}
}

35
ICSharpCode.ILSpyX/Settings/MiscSettings.cs → ILSpy/Options/MiscSettings.cs

@ -19,25 +19,38 @@ @@ -19,25 +19,38 @@
using System;
using System.Xml.Linq;
using TomsToolbox.Wpf;
namespace ICSharpCode.ILSpyX.Settings
{
public class MiscSettings : IMiscSettings, ISettingsSection<MiscSettings>
public class MiscSettings : ObservableObject, ISettingsSection
{
public MiscSettings()
{
private bool allowMultipleInstances;
private bool loadPreviousAssemblies = true;
public bool AllowMultipleInstances {
get => allowMultipleInstances;
set => SetProperty(ref allowMultipleInstances, value);
}
public bool LoadPreviousAssemblies {
get => loadPreviousAssemblies;
set => SetProperty(ref loadPreviousAssemblies, value);
}
public bool AllowMultipleInstances { get; set; }
public bool LoadPreviousAssemblies { get; set; } = true;
public XName SectionName => "MiscSettings";
public static MiscSettings Load(ISettingsProvider settingsProvider)
public void LoadFromSection(XElement e)
{
XElement e = settingsProvider["MiscSettings"];
var s = new MiscSettings();
s.AllowMultipleInstances = (bool?)e.Attribute(nameof(s.AllowMultipleInstances)) ?? false;
s.LoadPreviousAssemblies = (bool?)e.Attribute(nameof(s.LoadPreviousAssemblies)) ?? true;
AllowMultipleInstances = (bool?)e.Attribute(nameof(AllowMultipleInstances)) ?? false;
LoadPreviousAssemblies = (bool?)e.Attribute(nameof(LoadPreviousAssemblies)) ?? true;
}
return s;
public void SaveToSection(XElement section)
{
section.SetAttributeValue(nameof(AllowMultipleInstances), AllowMultipleInstances);
section.SetAttributeValue(nameof(LoadPreviousAssemblies), LoadPreviousAssemblies);
}
}
}

11
ILSpy/Options/MiscSettingsPanel.xaml

@ -2,16 +2,17 @@ @@ -2,16 +2,17 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
mc:Ignorable="d"
xmlns:options="clr-namespace:ICSharpCode.ILSpy.Options"
d:DataContext="{d:DesignInstance options:MiscSettingsViewModel}"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Orientation="Vertical">
<GroupBox Header="{x:Static properties:Resources.Misc}">
<StackPanel Margin="10">
<CheckBox IsChecked="{Binding AllowMultipleInstances}" Content="{x:Static properties:Resources.AllowMultipleInstances}" />
<CheckBox IsChecked="{Binding LoadPreviousAssemblies}" Content="{x:Static properties:Resources.LoadAssembliesThatWereLoadedInTheLastInstance}"/>
<Button Command="{Binding AddRemoveShellIntegrationCommand}" Content="{Binding AddRemoveShellIntegrationText}" Margin="3" />
<CheckBox IsChecked="{Binding Settings.AllowMultipleInstances}" Content="{x:Static properties:Resources.AllowMultipleInstances}" />
<CheckBox IsChecked="{Binding Settings.LoadPreviousAssemblies}" Content="{x:Static properties:Resources.LoadAssembliesThatWereLoadedInTheLastInstance}"/>
<Button Command="{Binding AddRemoveShellIntegrationCommand}" Content="{Binding AddRemoveShellIntegrationText, FallbackValue=temp}" Margin="3" />
</StackPanel>
</GroupBox>
</StackPanel>

31
ILSpy/Options/MiscSettingsPanel.xaml.cs

@ -20,45 +20,20 @@ using System.ComponentModel.Composition; @@ -20,45 +20,20 @@ using System.ComponentModel.Composition;
using System.Windows.Controls;
using System.Xml.Linq;
using ICSharpCode.ILSpyX.Settings;
using TomsToolbox.Wpf.Composition.Mef;
namespace ICSharpCode.ILSpy.Options
{
/// <summary>
/// Interaction logic for MiscSettingsPanel.xaml
/// </summary>
[ExportOptionPage(Order = 30)]
[DataTemplate(typeof(MiscSettingsViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class MiscSettingsPanel : UserControl, IOptionPage
public partial class MiscSettingsPanel
{
public MiscSettingsPanel()
{
InitializeComponent();
}
public string Title => Properties.Resources.Misc;
public void Load(ILSpySettings spySettings)
{
this.DataContext = new MiscSettingsViewModel(SettingsService.Instance.MiscSettings);
}
public void Save(XElement root)
{
if (DataContext is not IMiscSettings miscSettings)
return;
IMiscSettings.Save(root, miscSettings);
SettingsService.Instance.MiscSettings = new() {
AllowMultipleInstances = miscSettings.AllowMultipleInstances,
LoadPreviousAssemblies = miscSettings.LoadPreviousAssemblies
};
}
public void LoadDefaults()
{
this.DataContext = new MiscSettingsViewModel(new());
}
}
}

47
ILSpy/Options/MiscSettingsViewModel.cs

@ -16,12 +16,12 @@ @@ -16,12 +16,12 @@
// 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.ComponentModel.Composition;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;
using System.Xml.Linq;
using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpyX.Settings;
@ -32,31 +32,14 @@ using TomsToolbox.Wpf; @@ -32,31 +32,14 @@ using TomsToolbox.Wpf;
namespace ICSharpCode.ILSpy.Options
{
public class MiscSettingsViewModel : ObservableObject, IMiscSettings
[ExportOptionPage(Order = 30)]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class MiscSettingsViewModel : ObservableObject, IOptionPage
{
bool allowMultipleInstances;
bool loadPreviousAssemblies = true;
public MiscSettingsViewModel(MiscSettings s)
{
AllowMultipleInstances = s.AllowMultipleInstances;
LoadPreviousAssemblies = s.LoadPreviousAssemblies;
}
/// <summary>
/// Allow multiple instances.
/// </summary>
public bool AllowMultipleInstances {
get => allowMultipleInstances;
set => SetProperty(ref allowMultipleInstances, value);
}
/// <summary>
/// Load assemblies that were loaded in the previous instance
/// </summary>
public bool LoadPreviousAssemblies {
get => loadPreviousAssemblies;
set => SetProperty(ref loadPreviousAssemblies, value);
private MiscSettings settings;
public MiscSettings Settings {
get => settings;
set => SetProperty(ref settings, value);
}
public ICommand AddRemoveShellIntegrationCommand => new DelegateCommand(() => AppEnvironment.IsWindows, AddRemoveShellIntegration);
@ -105,5 +88,17 @@ namespace ICSharpCode.ILSpy.Options @@ -105,5 +88,17 @@ namespace ICSharpCode.ILSpy.Options
return RegistryEntriesExist() ? Properties.Resources.RemoveShellIntegration : Properties.Resources.AddShellIntegration;
}
}
public string Title => Properties.Resources.Misc;
public void Load(SettingsSnapshot settings)
{
Settings = settings.GetSettings<MiscSettings>();
}
public void LoadDefaults()
{
Settings.LoadFromSection(new XElement("dummy"));
}
}
}

5
ILSpy/Options/OptionsDialog.xaml.cs

@ -45,8 +45,9 @@ namespace ICSharpCode.ILSpy.Options @@ -45,8 +45,9 @@ namespace ICSharpCode.ILSpy.Options
public interface IOptionPage
{
string Title { get; }
void Load(ILSpySettings spySettings);
void Save(XElement root);
void Load(SettingsSnapshot settings);
void LoadDefaults();
}

13
ILSpy/Options/OptionsDialogViewModel.cs

@ -20,6 +20,8 @@ namespace ICSharpCode.ILSpy.Options @@ -20,6 +20,8 @@ namespace ICSharpCode.ILSpy.Options
{
private IOptionPage? selectedPage;
private SettingsSnapshot snapshot;
private readonly IOptionPage[] optionPages = App.ExportProvider.GetExports<IOptionPage, IOptionsMetadata>("OptionPages")
.OrderBy(page => page.Metadata?.Order)
.Select(item => item.Value)
@ -28,11 +30,11 @@ namespace ICSharpCode.ILSpy.Options @@ -28,11 +30,11 @@ namespace ICSharpCode.ILSpy.Options
public OptionsDialogViewModel()
{
var settings = SettingsService.Instance.SpySettings;
this.snapshot = SettingsService.Instance.CreateSnapshot();
foreach (var optionPage in optionPages)
{
optionPage.Load(settings);
optionPage.Load(this.snapshot);
}
OptionPages = optionPages.Select(page => new OptionsItemViewModel(page)).ToArray();
@ -60,12 +62,7 @@ namespace ICSharpCode.ILSpy.Options @@ -60,12 +62,7 @@ namespace ICSharpCode.ILSpy.Options
private void Commit()
{
SettingsService.Instance.SpySettings.Update(root => {
foreach (var optionPage in optionPages)
{
optionPage.Save(root);
}
});
snapshot.Save();
}
}
}

8
ILSpy/Search/SearchPane.xaml.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Text.RegularExpressions;
@ -66,7 +67,7 @@ namespace ICSharpCode.ILSpy.Search @@ -66,7 +67,7 @@ namespace ICSharpCode.ILSpy.Search
ContextMenuProvider.Add(listBox);
MessageBus<CurrentAssemblyListChangedEventArgs>.Subscribers += (sender, e) => CurrentAssemblyList_Changed();
MessageBus<LanguageSettingsChangedEventArgs>.Subscribers += (sender, e) => LanguageSettings_PropertyChanged();
MessageBus<SettingsChangedEventArgs>.Subscribers += (sender, e) => Settings_PropertyChanged(sender, e);
CompositionTarget.Rendering += UpdateResults;
}
@ -84,8 +85,11 @@ namespace ICSharpCode.ILSpy.Search @@ -84,8 +85,11 @@ namespace ICSharpCode.ILSpy.Search
}
}
void LanguageSettings_PropertyChanged()
void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (sender is not LanguageSettings)
return;
UpdateFilter();
}

124
ILSpy/SessionSettings.cs

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
@ -29,7 +30,6 @@ using System.Xml.Linq; @@ -29,7 +30,6 @@ using System.Xml.Linq;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Themes;
using ICSharpCode.ILSpyX.Search;
using ICSharpCode.ILSpyX.Settings;
namespace ICSharpCode.ILSpy
{
@ -37,58 +37,37 @@ namespace ICSharpCode.ILSpy @@ -37,58 +37,37 @@ namespace ICSharpCode.ILSpy
/// Per-session setting:
/// Loaded at startup; saved at exit.
/// </summary>
public sealed class SessionSettings : INotifyPropertyChanged
public sealed class SessionSettings : ISettingsSection
{
public SessionSettings(ILSpySettings spySettings)
{
XElement doc = spySettings["SessionSettings"];
XElement filterSettings = doc.Element("FilterSettings");
if (filterSettings == null)
filterSettings = new XElement("FilterSettings");
this.LanguageSettings = new LanguageSettings(filterSettings);
this.ActiveAssemblyList = (string)doc.Element("ActiveAssemblyList");
XElement activeTreeViewPath = doc.Element("ActiveTreeViewPath");
if (activeTreeViewPath != null)
{
this.ActiveTreeViewPath = activeTreeViewPath.Elements().Select(e => Unescape((string)e)).ToArray();
}
this.ActiveAutoLoadedAssembly = (string)doc.Element("ActiveAutoLoadedAssembly");
this.WindowState = FromString((string)doc.Element("WindowState"), WindowState.Normal);
this.WindowBounds = FromString((string)doc.Element("WindowBounds"), DefaultWindowBounds);
this.SelectedSearchMode = FromString((string)doc.Element("SelectedSearchMode"), SearchMode.TypeAndMember);
this.Theme = FromString((string)doc.Element(nameof(Theme)), ThemeManager.Current.DefaultTheme);
var culture = (string)doc.Element(nameof(CurrentCulture));
this.CurrentCulture = string.IsNullOrEmpty(culture) ? null : culture;
public XName SectionName => "SessionSettings";
this.DockLayout = new DockLayoutSettings(doc.Element("DockLayout"));
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null)
public void LoadFromSection(XElement section)
{
var args = new PropertyChangedEventArgs(propertyName);
PropertyChanged?.Invoke(this, args);
MessageBus.Send(this, new SessionSettingsChangedEventArgs(args));
XElement filterSettings = section.Element("FilterSettings") ?? new XElement("FilterSettings");
LanguageSettings = new(filterSettings);
LanguageSettings.PropertyChanged += (sender, e) => PropertyChanged?.Invoke(sender, e);
ActiveAssemblyList = (string)section.Element("ActiveAssemblyList");
ActiveTreeViewPath = section.Element("ActiveTreeViewPath")?.Elements().Select(e => Unescape((string)e)).ToArray();
ActiveAutoLoadedAssembly = (string)section.Element("ActiveAutoLoadedAssembly");
WindowState = FromString((string)section.Element("WindowState"), WindowState.Normal);
WindowBounds = FromString((string)section.Element("WindowBounds"), DefaultWindowBounds);
SelectedSearchMode = FromString((string)section.Element("SelectedSearchMode"), SearchMode.TypeAndMember);
Theme = FromString((string)section.Element(nameof(Theme)), ThemeManager.Current.DefaultTheme);
var culture = (string)section.Element(nameof(CurrentCulture));
CurrentCulture = string.IsNullOrEmpty(culture) ? null : culture;
DockLayout = new(section.Element("DockLayout"));
}
public LanguageSettings LanguageSettings { get; }
public LanguageSettings LanguageSettings { get; set; }
public SearchMode SelectedSearchMode { get; set; }
private string theme;
public string Theme {
get => ThemeManager.Current.Theme;
set {
ThemeManager.Current.Theme = value;
OnPropertyChanged();
}
get => theme;
set => SetProperty(ref theme, value);
}
public string[] ActiveTreeViewPath;
@ -120,56 +99,49 @@ namespace ICSharpCode.ILSpy @@ -120,56 +99,49 @@ namespace ICSharpCode.ILSpy
public WindowState WindowState;
public Rect WindowBounds;
internal static Rect DefaultWindowBounds = new Rect(10, 10, 750, 550);
internal static Rect DefaultWindowBounds = new(10, 10, 750, 550);
public DockLayoutSettings DockLayout { get; }
public DockLayoutSettings DockLayout { get; set; }
public XElement ToXml()
public void SaveToSection(XElement section)
{
XElement doc = new XElement("SessionSettings");
doc.Add(this.LanguageSettings.SaveAsXml());
section.RemoveAll();
section.Add(this.LanguageSettings.SaveAsXml());
if (this.ActiveAssemblyList != null)
{
doc.Add(new XElement("ActiveAssemblyList", this.ActiveAssemblyList));
section.Add(new XElement("ActiveAssemblyList", this.ActiveAssemblyList));
}
if (this.ActiveTreeViewPath != null)
{
doc.Add(new XElement("ActiveTreeViewPath", ActiveTreeViewPath.Select(p => new XElement("Node", Escape(p)))));
section.Add(new XElement("ActiveTreeViewPath", ActiveTreeViewPath.Select(p => new XElement("Node", Escape(p)))));
}
if (this.ActiveAutoLoadedAssembly != null)
{
doc.Add(new XElement("ActiveAutoLoadedAssembly", this.ActiveAutoLoadedAssembly));
section.Add(new XElement("ActiveAutoLoadedAssembly", this.ActiveAutoLoadedAssembly));
}
doc.Add(new XElement("WindowState", ToString(this.WindowState)));
doc.Add(new XElement("WindowBounds", ToString(this.WindowBounds)));
doc.Add(new XElement("SelectedSearchMode", ToString(this.SelectedSearchMode)));
doc.Add(new XElement(nameof(Theme), ToString(this.Theme)));
section.Add(new XElement("WindowState", ToString(this.WindowState)));
section.Add(new XElement("WindowBounds", ToString(this.WindowBounds)));
section.Add(new XElement("SelectedSearchMode", ToString(this.SelectedSearchMode)));
section.Add(new XElement(nameof(Theme), ToString(this.Theme)));
if (this.CurrentCulture != null)
{
doc.Add(new XElement(nameof(CurrentCulture), this.CurrentCulture));
section.Add(new XElement(nameof(CurrentCulture), this.CurrentCulture));
}
var dockLayoutElement = new XElement("DockLayout");
if (DockLayout.Valid)
{
dockLayoutElement.Add(DockLayout.SaveAsXml());
}
doc.Add(dockLayoutElement);
return doc;
section.Add(dockLayoutElement);
}
public void Save()
{
var doc = ToXml();
SettingsService.Instance.SpySettings.SaveSettings(doc);
}
static Regex regex = new Regex("\\\\x(?<num>[0-9A-f]{4})");
static Regex regex = new("\\\\x(?<num>[0-9A-f]{4})");
private string activeAssemblyList;
static string Escape(string p)
{
StringBuilder sb = new StringBuilder();
StringBuilder sb = new();
foreach (char ch in p)
{
if (char.IsLetterOrDigit(ch))
@ -205,5 +177,21 @@ namespace ICSharpCode.ILSpy @@ -205,5 +177,21 @@ namespace ICSharpCode.ILSpy
TypeConverter c = TypeDescriptor.GetConverter(typeof(T));
return c.ConvertToInvariantString(obj);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new(propertyName));
}
private bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
}

10
ILSpy/Themes/ThemeManager.cs

@ -20,6 +20,7 @@ @@ -20,6 +20,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
@ -42,6 +43,7 @@ namespace ICSharpCode.ILSpy.Themes @@ -42,6 +43,7 @@ namespace ICSharpCode.ILSpy.Themes
private ThemeManager()
{
Application.Current.Resources.MergedDictionaries.Add(_themeDictionaryContainer);
MessageBus<SettingsChangedEventArgs>.Subscribers += (sender, e) => Settings_Changed(sender, e);
}
public string DefaultTheme => "Light";
@ -199,5 +201,13 @@ namespace ICSharpCode.ILSpy.Themes @@ -199,5 +201,13 @@ namespace ICSharpCode.ILSpy.Themes
var b = (byte)((b1 + m) * 255f);
return (r, g, b);
}
private void Settings_Changed(object? sender, PropertyChangedEventArgs e)
{
if (sender is not SessionSettings settings || e.PropertyName != nameof(SessionSettings.Theme))
return;
Theme = settings.Theme;
}
}
}

11
ILSpy/TreeNodes/ILSpyTreeNode.cs

@ -40,9 +40,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -40,9 +40,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
bool childrenNeedFiltering;
public ILSpyTreeNode()
protected ILSpyTreeNode()
{
MessageBus<LanguageSettingsChangedEventArgs>.Subscribers += LanguageSettings_Changed;
MessageBus<SettingsChangedEventArgs>.Subscribers += (sender, e) => Settings_Changed(sender, e);
}
LanguageSettings LanguageSettings => SettingsService.Instance.SessionSettings.LanguageSettings;
@ -127,8 +127,13 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -127,8 +127,13 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
protected virtual void LanguageSettings_Changed(object sender, EventArgs e)
protected virtual void Settings_Changed(object sender, PropertyChangedEventArgs e)
{
if (sender is not ILSpy.LanguageSettings)
return;
if (e.PropertyName is not (nameof(LanguageSettings.Language) or nameof(LanguageSettings.LanguageVersion)))
return;
RaisePropertyChanged(nameof(Text));
if (IsVisible)
{

4
ILSpy/Updates/NotifyOfUpdatesStrategy.cs

@ -61,7 +61,7 @@ namespace ICSharpCode.ILSpy.Updates @@ -61,7 +61,7 @@ namespace ICSharpCode.ILSpy.Updates
/// Returns the download URL if an update is available.
/// Returns null if no update is available, or if no check was performed.
/// </summary>
public static async Task<string> CheckForUpdatesIfEnabledAsync(ILSpySettings spySettings)
public static async Task<string> CheckForUpdatesIfEnabledAsync(ISettingsProvider spySettings)
{
UpdateSettings s = new UpdateSettings(spySettings);
@ -87,7 +87,7 @@ namespace ICSharpCode.ILSpy.Updates @@ -87,7 +87,7 @@ namespace ICSharpCode.ILSpy.Updates
}
}
public static Task<string> CheckForUpdatesAsync(ILSpySettings spySettings)
public static Task<string> CheckForUpdatesAsync(ISettingsProvider spySettings)
{
UpdateSettings s = new UpdateSettings(spySettings);
return CheckForUpdateInternal(s);

2
ILSpy/Updates/UpdateSettings.cs

@ -26,7 +26,7 @@ namespace ICSharpCode.ILSpy.Updates @@ -26,7 +26,7 @@ namespace ICSharpCode.ILSpy.Updates
{
sealed class UpdateSettings : INotifyPropertyChanged
{
public UpdateSettings(ILSpySettings spySettings)
public UpdateSettings(ISettingsProvider spySettings)
{
XElement s = spySettings["UpdateSettings"];
this.automaticUpdateCheckEnabled = (bool?)s.Element("AutomaticUpdateCheckEnabled") ?? true;

4
ILSpy/Util/MessageBus.cs

@ -52,9 +52,7 @@ namespace ICSharpCode.ILSpy.Util @@ -52,9 +52,7 @@ namespace ICSharpCode.ILSpy.Util
public class CurrentAssemblyListChangedEventArgs(NotifyCollectionChangedEventArgs e) : WrappedEventArgs<NotifyCollectionChangedEventArgs>(e);
public class LanguageSettingsChangedEventArgs(PropertyChangedEventArgs e) : WrappedEventArgs<PropertyChangedEventArgs>(e);
public class SessionSettingsChangedEventArgs(PropertyChangedEventArgs e) : WrappedEventArgs<PropertyChangedEventArgs>(e);
public class SettingsChangedEventArgs(PropertyChangedEventArgs e) : WrappedEventArgs<PropertyChangedEventArgs>(e);
public class NavigateToReferenceEventArgs(object reference, bool inNewTabPage = false) : EventArgs
{

157
ILSpy/Util/SettingsService.cs

@ -1,4 +1,7 @@ @@ -1,4 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Xml.Linq;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.Options;
@ -6,36 +9,102 @@ using ICSharpCode.ILSpy.ViewModels; @@ -6,36 +9,102 @@ using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.Settings;
using DecompilerSettings = ICSharpCode.ILSpy.Options.DecompilerSettings;
#nullable enable
namespace ICSharpCode.ILSpy.Util
{
public class SettingsService
public interface ISettingsSection : INotifyPropertyChanged
{
public static readonly SettingsService Instance = new();
XName SectionName { get; }
void LoadFromSection(XElement section);
void SaveToSection(XElement section);
}
public abstract class SettingsServiceBase
{
protected readonly ConcurrentDictionary<Type, ISettingsSection> sections = new();
public ISettingsProvider SpySettings;
protected SettingsServiceBase(ISettingsProvider spySettings)
{
SpySettings = spySettings;
}
public T GetSettings<T>() where T : ISettingsSection, new()
{
return (T)sections.GetOrAdd(typeof(T), _ => {
T section = new T();
var sectionElement = SpySettings[section.SectionName];
section.LoadFromSection(sectionElement);
section.PropertyChanged += Section_PropertyChanged;
private SettingsService()
return section;
});
}
protected virtual void Section_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
}
}
public class SettingsSnapshot : SettingsServiceBase
{
private readonly SettingsService parent;
public SettingsSnapshot(SettingsService parent) : base(parent.SpySettings)
{
SpySettings = ILSpySettings.Load();
SessionSettings = new(SpySettings);
DecompilerSettings = ISettingsProvider.LoadDecompilerSettings(SpySettings);
DisplaySettings = DisplaySettings.Load(SpySettings, SessionSettings);
MiscSettings = MiscSettings.Load(SpySettings);
AssemblyListManager = new(SpySettings) {
ApplyWinRTProjections = DecompilerSettings.ApplyWindowsRuntimeProjections,
UseDebugSymbols = DecompilerSettings.UseDebugSymbols
};
this.parent = parent;
}
public ILSpySettings SpySettings { get; }
public void Save()
{
SpySettings.Update(root => {
foreach (var section in sections.Values)
{
var element = SpySettings[section.SectionName];
section.SaveToSection(element);
public SessionSettings SessionSettings { get; }
var existingElement = root.Element(section.SectionName);
if (existingElement != null)
existingElement.ReplaceWith(element);
else
root.Add(element);
}
});
public DecompilerSettings DecompilerSettings { get; set; }
parent.Reload();
}
}
public DisplaySettings DisplaySettings { get; }
public class SettingsService : SettingsServiceBase
{
public static readonly SettingsService Instance = new();
public MiscSettings MiscSettings { get; set; }
private SettingsService() : base(LoadSettings())
{
}
public AssemblyListManager AssemblyListManager { get; }
public SessionSettings SessionSettings => GetSettings<SessionSettings>();
public DecompilerSettings DecompilerSettings => GetSettings<DecompilerSettings>();
public DisplaySettings DisplaySettings => GetSettings<DisplaySettings>();
public MiscSettings MiscSettings => GetSettings<MiscSettings>();
private AssemblyListManager? assemblyListManager;
public AssemblyListManager AssemblyListManager => assemblyListManager ??= new(SpySettings) {
ApplyWinRTProjections = DecompilerSettings.ApplyWindowsRuntimeProjections,
UseDebugSymbols = DecompilerSettings.UseDebugSymbols
};
public DecompilationOptions CreateDecompilationOptions(TabPageModel tabPage)
{
@ -56,5 +125,57 @@ namespace ICSharpCode.ILSpy.Util @@ -56,5 +125,57 @@ namespace ICSharpCode.ILSpy.Util
return AssemblyListManager.CreateList(AssemblyListManager.DefaultListName);
}
}
private bool reloading;
public void Reload()
{
reloading = true;
try
{
SpySettings = ILSpySettings.Load();
foreach (var section in sections.Values)
{
var element = SpySettings[section.SectionName];
section.LoadFromSection(element);
}
}
finally
{
reloading = false;
}
}
public SettingsSnapshot CreateSnapshot()
{
return new(this);
}
protected override void Section_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
base.Section_PropertyChanged(sender, e);
if (!reloading)
{
throw new InvalidOperationException("Settings are read only, use a snapshot to modify.");
}
if (sender is DecompilerSettings decompilerSettings && assemblyListManager != null)
{
assemblyListManager.ApplyWinRTProjections = decompilerSettings.ApplyWindowsRuntimeProjections;
assemblyListManager.UseDebugSymbols = decompilerSettings.UseDebugSymbols;
}
MessageBus.Send(sender, new SettingsChangedEventArgs(e));
}
private static ILSpySettings LoadSettings()
{
ILSpySettings.SettingsFilePathProvider = new ILSpySettingsFilePathProvider();
return ILSpySettings.Load();
}
}
}

9
ILSpy/Views/DebugSteps.xaml.cs

@ -33,7 +33,7 @@ namespace ICSharpCode.ILSpy @@ -33,7 +33,7 @@ namespace ICSharpCode.ILSpy
InitializeComponent();
#if DEBUG
MessageBus<LanguageSettingsChangedEventArgs>.Subscribers += (sender, e) => LanguageSettings_PropertyChanged(sender, e);
MessageBus<SettingsChangedEventArgs>.Subscribers += (sender, e) => Settings_PropertyChanged(sender, e);
MessageBus<AssemblyTreeSelectionChangedEventArgs>.Subscribers += SelectionChanged;
writingOptions.PropertyChanged += WritingOptions_PropertyChanged;
@ -60,10 +60,13 @@ namespace ICSharpCode.ILSpy @@ -60,10 +60,13 @@ namespace ICSharpCode.ILSpy
});
}
private void LanguageSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
private void Settings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
#if DEBUG
if (e.PropertyName == "Language")
if (sender is not LanguageSettings)
return;
if (e.PropertyName == nameof(LanguageSettings.Language))
{
if (language != null)
{

11
TestPlugin/CustomOptionPage.xaml

@ -1,8 +1,13 @@ @@ -1,8 +1,13 @@
<UserControl x:Class="TestPlugin.CustomOptionPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d:DesignHeight="500" d:DesignWidth="500"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
xmlns:testPlugin="clr-namespace:TestPlugin"
d:DataContext="{d:DesignInstance testPlugin:CustomOptionsViewModel}"
>
<StackPanel>
<CheckBox IsChecked="{Binding UselessOption1}">Useless option 1</CheckBox>
<Slider Minimum="0" Maximum="100" Value="{Binding UselessOption2}"/>
<CheckBox IsChecked="{Binding Options.UselessOption1}">Useless option 1</CheckBox>
<Slider Minimum="0" Maximum="100" Value="{Binding Options.UselessOption2}"/>
</StackPanel>
</UserControl>

92
TestPlugin/CustomOptionPage.xaml.cs

@ -1,100 +1,80 @@ @@ -1,100 +1,80 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Windows.Controls;
using System.Xml.Linq;
using ICSharpCode.ILSpy.Options;
using ICSharpCode.ILSpyX.Settings;
using ICSharpCode.ILSpy.Util;
using TomsToolbox.Wpf;
using TomsToolbox.Wpf.Composition.Mef;
namespace TestPlugin
{
[ExportOptionPage(Order = 0)]
[DataTemplate(typeof(CustomOptionsViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
partial class CustomOptionPage : UserControl, IOptionPage
partial class CustomOptionPage
{
static readonly XNamespace ns = "http://www.ilspy.net/testplugin";
public CustomOptionPage()
{
InitializeComponent();
}
}
[ExportOptionPage(Order = 0)]
class CustomOptionsViewModel : ObservableObject, IOptionPage
{
private Options options;
public string Title => "TestPlugin";
public void Load(ILSpySettings spySettings)
{
// For loading options, use ILSpySetting's indexer.
// If the specified section does exist, the indexer will return a new empty element.
XElement e = spySettings[ns + "CustomOptions"];
// Now load the options from the XML document:
Options s = new Options();
s.UselessOption1 = (bool?)e.Attribute("useless1") ?? s.UselessOption1;
s.UselessOption2 = (double?)e.Attribute("useless2") ?? s.UselessOption2;
this.DataContext = s;
public Options Options {
get => options;
set => SetProperty(ref options, value);
}
public void LoadDefaults()
public void Load(SettingsSnapshot snapshot)
{
this.DataContext = new Options();
this.Options = snapshot.GetSettings<Options>();
}
public void Save(XElement root)
public void LoadDefaults()
{
Options s = (Options)this.DataContext;
// Save the options back into XML:
XElement section = new XElement(ns + "CustomOptions");
section.SetAttributeValue("useless1", s.UselessOption1);
section.SetAttributeValue("useless2", s.UselessOption2);
// Replace the existing section in the settings file, or add a new section,
// if required.
XElement existingElement = root.Element(ns + "CustomOptions");
if (existingElement != null)
existingElement.ReplaceWith(section);
else
root.Add(section);
Options.LoadFromSection(new XElement("dummy"));
}
}
class Options : INotifyPropertyChanged
class Options : ObservableObject, ISettingsSection
{
static readonly XNamespace ns = "http://www.ilspy.net/testplugin";
bool uselessOption1;
public bool UselessOption1 {
get { return uselessOption1; }
set {
if (uselessOption1 != value)
{
uselessOption1 = value;
OnPropertyChanged("UselessOption1");
}
}
get => uselessOption1;
set => SetProperty(ref uselessOption1, value);
}
double uselessOption2;
public double UselessOption2 {
get { return uselessOption2; }
set {
if (uselessOption2 != value)
{
uselessOption2 = value;
OnPropertyChanged("UselessOption2");
}
}
get => uselessOption2;
set => SetProperty(ref uselessOption2, value);
}
public event PropertyChangedEventHandler PropertyChanged;
public XName SectionName { get; } = ns + "CustomOptions";
public void LoadFromSection(XElement e)
{
UselessOption1 = (bool?)e.Attribute("useless1") ?? false;
UselessOption2 = (double?)e.Attribute("useless2") ?? 50.0;
}
protected virtual void OnPropertyChanged(string propertyName)
public void SaveToSection(XElement section)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
section.SetAttributeValue("useless1", UselessOption1);
section.SetAttributeValue("useless2", UselessOption2);
}
}
}
Loading…
Cancel
Save