diff --git a/ILSpy/Analyzers/AnalyzerTreeView.cs b/ILSpy/Analyzers/AnalyzerTreeView.cs index b81763990..261e52b18 100644 --- a/ILSpy/Analyzers/AnalyzerTreeView.cs +++ b/ILSpy/Analyzers/AnalyzerTreeView.cs @@ -35,6 +35,8 @@ namespace ICSharpCode.ILSpy.Analyzers /// public class AnalyzerTreeView : SharpTreeView { + FilterSettings filterSettings; + public AnalyzerTreeView() { this.ShowRoot = false; @@ -42,7 +44,21 @@ namespace ICSharpCode.ILSpy.Analyzers this.BorderThickness = new Thickness(0); ContextMenuProvider.Add(this); MainWindow.Instance.CurrentAssemblyListChanged += MainWindow_Instance_CurrentAssemblyListChanged; - MainWindow.Instance.SessionSettings.FilterSettings.PropertyChanged += FilterSettings_PropertyChanged; + DockWorkspace.Instance.PropertyChanged += DockWorkspace_PropertyChanged; + filterSettings = MainWindow.Instance.SessionSettings.FilterSettings; + filterSettings.PropertyChanged += FilterSettings_PropertyChanged; + } + + private void DockWorkspace_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(DockWorkspace.Instance.ActiveTabPage): + filterSettings.PropertyChanged -= FilterSettings_PropertyChanged; + filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; + filterSettings.PropertyChanged += FilterSettings_PropertyChanged; + break; + } } private void FilterSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) diff --git a/ILSpy/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs index ede66930e..19156ab79 100644 --- a/ILSpy/Docking/DockWorkspace.cs +++ b/ILSpy/Docking/DockWorkspace.cs @@ -94,8 +94,6 @@ namespace ICSharpCode.ILSpy.Docking if (_activeTabPage != value) { _activeTabPage = value; - this.sessionSettings.FilterSettings.Language = value.Language; - this.sessionSettings.FilterSettings.LanguageVersion = value.LanguageVersion; var state = value.GetState(); if (state != null) { @@ -173,23 +171,6 @@ namespace ICSharpCode.ILSpy.Docking internal void LoadSettings(SessionSettings sessionSettings) { this.sessionSettings = sessionSettings; - sessionSettings.FilterSettings.PropertyChanged += FilterSettings_PropertyChanged; - } - - private void FilterSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "Language") - { - ActiveTabPage.Language = sessionSettings.FilterSettings.Language; - if (sessionSettings.FilterSettings.Language.HasLanguageVersions) - { - sessionSettings.FilterSettings.LanguageVersion = ActiveTabPage.LanguageVersion; - } - } - else if (e.PropertyName == "LanguageVersion") - { - ActiveTabPage.LanguageVersion = sessionSettings.FilterSettings.LanguageVersion; - } } internal void CloseAllTabs() diff --git a/ILSpy/FilterSettings.cs b/ILSpy/FilterSettings.cs index 11930783e..cc0aa4b05 100644 --- a/ILSpy/FilterSettings.cs +++ b/ILSpy/FilterSettings.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; @@ -34,6 +35,13 @@ namespace ICSharpCode.ILSpy /// public class FilterSettings : INotifyPropertyChanged { + /// + /// This dictionary is necessary to remember language versions across language changes. For example, + /// the user first select C# 10, then switches to IL, then switches back to C#. After that we must be + /// able to restore the original selection (i.e., C# 10). + /// + private readonly Dictionary languageVersionHistory = new Dictionary(); + public FilterSettings(XElement element) { this.ShowApiLevel = (ApiVisibility?)(int?)element.Element("ShowAPILevel") ?? ApiVisibility.PublicAndInternal; @@ -146,8 +154,27 @@ namespace ICSharpCode.ILSpy set { if (language != value) { + if (language != null && language.HasLanguageVersions) + { + languageVersionHistory[language] = languageVersion; + } language = value; OnPropertyChanged(); + if (language.HasLanguageVersions) + { + if (languageVersionHistory.TryGetValue(value, out var version)) + { + LanguageVersion = version; + } + else + { + LanguageVersion = Language.LanguageVersions.Last(); + } + } + else + { + LanguageVersion = default; + } } } } @@ -167,6 +194,10 @@ namespace ICSharpCode.ILSpy if (languageVersion != value) { languageVersion = value; + if (language.HasLanguageVersions) + { + languageVersionHistory[language] = languageVersion; + } OnPropertyChanged(); } } diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 8a20d45cb..8fbdd7d58 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -19,7 +19,7 @@ xmlns:styles="urn:TomsToolbox.Wpf.Styles" xmlns:b="http://schemas.microsoft.com/xaml/behaviors" xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes" - d:DataContext="{d:DesignInstance local:MainWindowDataContext}" + d:DataContext="{d:DesignInstance local:MainWindowViewModel}" > @@ -103,9 +103,9 @@ - - - + + + @@ -156,25 +156,25 @@ - + - + - + + SelectedItem="{Binding Workspace.ActiveTabPage.FilterSettings.Language}"/> + SelectedItem="{Binding Workspace.ActiveTabPage.FilterSettings.LanguageVersion, UpdateSourceTrigger=PropertyChanged}"/> diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index bbd390da2..3e8846aa3 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -32,7 +32,6 @@ using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; -using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Threading; @@ -55,13 +54,6 @@ using Microsoft.Win32; namespace ICSharpCode.ILSpy { - class MainWindowDataContext - { - public DockWorkspace Workspace { get; set; } - public SessionSettings SessionSettings { get; set; } - public AssemblyListManager AssemblyListManager { get; set; } - } - /// /// The main window of the application. /// @@ -70,7 +62,8 @@ namespace ICSharpCode.ILSpy bool refreshInProgress, changingActiveTab; readonly NavigationHistory history = new NavigationHistory(); ILSpySettings spySettingsForMainWindow_Loaded; - internal SessionSettings sessionSettings; + SessionSettings sessionSettings; + FilterSettings filterSettings; AssemblyList assemblyList; AssemblyListTreeNode assemblyListTreeNode; @@ -115,7 +108,7 @@ namespace ICSharpCode.ILSpy // Make sure Images are initialized on the UI thread. this.Icon = Images.ILSpyIcon; - this.DataContext = new MainWindowDataContext { + this.DataContext = new MainWindowViewModel { Workspace = DockWorkspace.Instance, SessionSettings = sessionSettings, AssemblyListManager = AssemblyListManager @@ -130,8 +123,10 @@ namespace ICSharpCode.ILSpy InitializeComponent(); InitToolPanes(); DockWorkspace.Instance.InitializeLayout(DockManager); - sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged; sessionSettings.PropertyChanged += SessionSettings_PropertyChanged; + filterSettings = sessionSettings.FilterSettings; + filterSettings.PropertyChanged += filterSettings_PropertyChanged; + DockWorkspace.Instance.PropertyChanged += DockWorkspace_PropertyChanged; InitMainMenu(); InitToolbar(); ContextMenuProvider.Add(AssemblyTreeView); @@ -139,6 +134,18 @@ namespace ICSharpCode.ILSpy this.Loaded += MainWindow_Loaded; } + private void DockWorkspace_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(DockWorkspace.Instance.ActiveTabPage): + filterSettings.PropertyChanged -= filterSettings_PropertyChanged; + filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; + filterSettings.PropertyChanged += filterSettings_PropertyChanged; + break; + } + } + private void SessionSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) @@ -406,7 +413,7 @@ namespace ICSharpCode.ILSpy { LoadAssemblies(args.AssembliesToLoad, commandLineLoadedAssemblies, focusNode: false); if (args.Language != null) - sessionSettings.FilterSettings.Language = Languages.GetLanguage(args.Language); + filterSettings.Language = Languages.GetLanguage(args.Language); return true; } @@ -597,8 +604,7 @@ namespace ICSharpCode.ILSpy void MainWindow_Loaded(object sender, RoutedEventArgs e) { DockWorkspace.Instance.TabPages.Add(new TabPageModel() { - Language = CurrentLanguage, - LanguageVersion = CurrentLanguageVersion + FilterSettings = filterSettings.Clone() }); DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.First(); @@ -770,7 +776,7 @@ namespace ICSharpCode.ILSpy assemblyList.CollectionChanged += assemblyList_Assemblies_CollectionChanged; assemblyListTreeNode = new AssemblyListTreeNode(assemblyList); - assemblyListTreeNode.FilterSettings = sessionSettings.FilterSettings.Clone(); + assemblyListTreeNode.FilterSettings = filterSettings.Clone(); assemblyListTreeNode.Select = x => SelectNode(x, inNewTabPage: false); AssemblyTreeView.Root = assemblyListTreeNode; @@ -837,7 +843,7 @@ namespace ICSharpCode.ILSpy // Thus, the main window will use one mutable instance (for data-binding), and assign a new clone to the ILSpyTreeNodes whenever the main // mutable instance changes. if (assemblyListTreeNode != null) - assemblyListTreeNode.FilterSettings = sessionSettings.FilterSettings.Clone(); + assemblyListTreeNode.FilterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings.Clone(); } internal AssemblyListTreeNode AssemblyListTreeNode { @@ -866,8 +872,7 @@ namespace ICSharpCode.ILSpy { DockWorkspace.Instance.TabPages.Add( new TabPageModel() { - Language = CurrentLanguage, - LanguageVersion = CurrentLanguageVersion + FilterSettings = filterSettings.Clone() }); DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.Last(); AssemblyTreeView.SelectedItem = null; @@ -913,8 +918,7 @@ namespace ICSharpCode.ILSpy { DockWorkspace.Instance.TabPages.Add( new TabPageModel() { - Language = CurrentLanguage, - LanguageVersion = CurrentLanguageVersion + FilterSettings = filterSettings.Clone() }); DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.Last(); } @@ -1200,7 +1204,10 @@ namespace ICSharpCode.ILSpy { state = DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState; } - DecompileSelectedNodes(state); + if (!changingActiveTab) + { + DecompileSelectedNodes(state); + } SelectionChanged?.Invoke(sender, e); } @@ -1268,8 +1275,10 @@ namespace ICSharpCode.ILSpy } } - public Language CurrentLanguage => sessionSettings.FilterSettings.Language; - public LanguageVersion CurrentLanguageVersion => sessionSettings.FilterSettings.LanguageVersion; + public Language CurrentLanguage => DockWorkspace.Instance.ActiveTabPage.FilterSettings.Language; + public LanguageVersion CurrentLanguageVersion => DockWorkspace.Instance.ActiveTabPage.FilterSettings.LanguageVersion; + + public bool SupportsLanguageSwitching => DockWorkspace.Instance.ActiveTabPage.SupportsLanguageSwitching; public event SelectionChangedEventHandler SelectionChanged; @@ -1341,8 +1350,7 @@ namespace ICSharpCode.ILSpy { DockWorkspace.Instance.TabPages.Add( new TabPageModel() { - Language = CurrentLanguage, - LanguageVersion = CurrentLanguageVersion + FilterSettings = filterSettings.Clone() }); DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.Last(); } @@ -1407,6 +1415,7 @@ namespace ICSharpCode.ILSpy sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(AssemblyTreeView.SelectedItem as SharpTreeNode); sessionSettings.WindowBounds = this.RestoreBounds; sessionSettings.DockLayout.Serialize(new XmlLayoutSerializer(DockManager)); + sessionSettings.FilterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings.Clone(); sessionSettings.Save(); } diff --git a/ILSpy/MainWindowViewModel.cs b/ILSpy/MainWindowViewModel.cs new file mode 100644 index 000000000..6568e18db --- /dev/null +++ b/ILSpy/MainWindowViewModel.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2021 Siegfried Pammer +// +// 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 ICSharpCode.ILSpy.Docking; +using ICSharpCode.ILSpy.ViewModels; + +namespace ICSharpCode.ILSpy +{ + class MainWindowViewModel + { + public DockWorkspace Workspace { get; set; } + public SessionSettings SessionSettings { get; set; } + public AssemblyListManager AssemblyListManager { get; set; } + } +} diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index 5c446c9bf..cb63a675d 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -50,6 +50,7 @@ namespace ICSharpCode.ILSpy RunningSearch currentSearch; bool runSearchOnNextShow; IComparer resultsComparer; + FilterSettings filterSettings; public static readonly DependencyProperty ResultsProperty = DependencyProperty.Register("Results", typeof(ObservableCollection), typeof(SearchPane), @@ -76,13 +77,27 @@ namespace ICSharpCode.ILSpy ContextMenuProvider.Add(listBox); MainWindow.Instance.CurrentAssemblyListChanged += MainWindow_Instance_CurrentAssemblyListChanged; - MainWindow.Instance.SessionSettings.FilterSettings.PropertyChanged += FilterSettings_PropertyChanged; + DockWorkspace.Instance.PropertyChanged += DockWorkspace_PropertyChanged; + filterSettings = MainWindow.Instance.SessionSettings.FilterSettings; + filterSettings.PropertyChanged += FilterSettings_PropertyChanged; CompositionTarget.Rendering += UpdateResults; // This starts empty search right away, so do at the end (we're still in ctor) searchModeComboBox.SelectedIndex = (int)MainWindow.Instance.SessionSettings.SelectedSearchMode; } + private void DockWorkspace_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(DockWorkspace.Instance.ActiveTabPage): + filterSettings.PropertyChanged -= FilterSettings_PropertyChanged; + filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; + filterSettings.PropertyChanged += FilterSettings_PropertyChanged; + break; + } + } + void MainWindow_Instance_CurrentAssemblyListChanged(object sender, NotifyCollectionChangedEventArgs e) { if (IsVisible) diff --git a/ILSpy/SessionSettings.cs b/ILSpy/SessionSettings.cs index ee1bcbf58..6fa003ff3 100644 --- a/ILSpy/SessionSettings.cs +++ b/ILSpy/SessionSettings.cs @@ -76,7 +76,7 @@ namespace ICSharpCode.ILSpy PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - public FilterSettings FilterSettings { get; private set; } + public FilterSettings FilterSettings { get; internal set; } public SearchMode SelectedSearchMode { get; set; } public bool IsDarkMode { diff --git a/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs b/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs index f6ff717a5..e1db19ac1 100644 --- a/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs +++ b/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs @@ -191,9 +191,9 @@ namespace ICSharpCode.ILSpy.ViewModels if (manager.AssemblyLists.Count > 0) { SelectedAssemblyList = manager.AssemblyLists[Math.Max(0, index - 1)]; - if (MainWindow.Instance.sessionSettings.ActiveAssemblyList == assemblyList) + if (MainWindow.Instance.SessionSettings.ActiveAssemblyList == assemblyList) { - MainWindow.Instance.sessionSettings.ActiveAssemblyList = SelectedAssemblyList; + MainWindow.Instance.SessionSettings.ActiveAssemblyList = SelectedAssemblyList; } } } @@ -234,9 +234,9 @@ namespace ICSharpCode.ILSpy.ViewModels string assemblyList = SelectedAssemblyList; SelectedAssemblyList = dlg.ListName; manager.RenameList(assemblyList, dlg.ListName); - if (MainWindow.Instance.sessionSettings.ActiveAssemblyList == assemblyList) + if (MainWindow.Instance.SessionSettings.ActiveAssemblyList == assemblyList) { - MainWindow.Instance.sessionSettings.ActiveAssemblyList = manager.AssemblyLists[manager.AssemblyLists.Count - 1]; + MainWindow.Instance.SessionSettings.ActiveAssemblyList = manager.AssemblyLists[manager.AssemblyLists.Count - 1]; } } } @@ -372,7 +372,7 @@ namespace ICSharpCode.ILSpy.ViewModels private void ExecuteSelectAssemblyList() { - MainWindow.Instance.sessionSettings.ActiveAssemblyList = SelectedAssemblyList; + MainWindow.Instance.SessionSettings.ActiveAssemblyList = SelectedAssemblyList; this.parent.Close(); } } diff --git a/ILSpy/ViewModels/TabPageModel.cs b/ILSpy/ViewModels/TabPageModel.cs index f4c42c23d..1a735841e 100644 --- a/ILSpy/ViewModels/TabPageModel.cs +++ b/ILSpy/ViewModels/TabPageModel.cs @@ -27,61 +27,36 @@ namespace ICSharpCode.ILSpy.ViewModels { public class TabPageModel : PaneModel { - private readonly Dictionary languageVersionHistory = new Dictionary(); - public TabPageModel() { this.Title = Properties.Resources.NewTab; } - private Language language; - public Language Language { - get => language; + private FilterSettings filterSettings; + + public FilterSettings FilterSettings { + get => filterSettings; set { - if (language != value) + if (filterSettings != value) { - if (language != null && language.HasLanguageVersions) - { - languageVersionHistory[language] = languageVersion; - } - language = value; - RaisePropertyChanged(nameof(Language)); - if (language.HasLanguageVersions) - { - if (languageVersionHistory.TryGetValue(value, out var version)) - { - LanguageVersion = version; - } - else - { - LanguageVersion = Language.LanguageVersions.Last(); - } - } - else - { - LanguageVersion = default; - } + filterSettings = value; + RaisePropertyChanged(nameof(FilterSettings)); } } } - private LanguageVersion languageVersion; + public Language Language { + get => filterSettings.Language; + set => filterSettings.Language = value; + } + public LanguageVersion LanguageVersion { - get => languageVersion; - set { - if (languageVersion != value) - { - languageVersion = value; - if (language.HasLanguageVersions) - { - languageVersionHistory[language] = languageVersion; - } - RaisePropertyChanged(nameof(LanguageVersion)); - } - } + get => filterSettings.LanguageVersion; + set => filterSettings.LanguageVersion = value; } private bool supportsLanguageSwitching = true; + public bool SupportsLanguageSwitching { get => supportsLanguageSwitching; set { @@ -94,6 +69,7 @@ namespace ICSharpCode.ILSpy.ViewModels } private object content; + public object Content { get => content; set { diff --git a/ILSpy/Views/DebugSteps.xaml.cs b/ILSpy/Views/DebugSteps.xaml.cs index d04ce4ebd..2b46753dd 100644 --- a/ILSpy/Views/DebugSteps.xaml.cs +++ b/ILSpy/Views/DebugSteps.xaml.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -22,13 +23,16 @@ namespace ICSharpCode.ILSpy #if DEBUG ILAstLanguage language; #endif + FilterSettings filterSettings; public DebugSteps() { InitializeComponent(); #if DEBUG - MainWindow.Instance.SessionSettings.FilterSettings.PropertyChanged += FilterSettings_PropertyChanged; + DockWorkspace.Instance.PropertyChanged += DockWorkspace_PropertyChanged; + this.filterSettings = MainWindow.Instance.SessionSettings.FilterSettings; + filterSettings.PropertyChanged += FilterSettings_PropertyChanged; MainWindow.Instance.SelectionChanged += SelectionChanged; writingOptions.PropertyChanged += WritingOptions_PropertyChanged; @@ -41,6 +45,18 @@ namespace ICSharpCode.ILSpy #endif } + private void DockWorkspace_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(DockWorkspace.Instance.ActiveTabPage): + filterSettings.PropertyChanged -= FilterSettings_PropertyChanged; + filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; + filterSettings.PropertyChanged += FilterSettings_PropertyChanged; + break; + } + } + private void WritingOptions_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { DecompileAsync(lastSelectedStep);