From 84bb61cc5bd0f09f5a22890a7d2b108bd05a50de Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 16 Nov 2019 13:56:46 +0100 Subject: [PATCH] Add primitive multi-document support. --- ILSpy/AboutPage.cs | 5 +- ILSpy/App.xaml.cs | 2 +- ILSpy/Commands/DecompileAllCommand.cs | 8 +- ILSpy/Commands/DecompileInNewViewCommand.cs | 15 +- ILSpy/Commands/DisassembleAllCommand.cs | 4 +- ILSpy/Commands/GeneratePdbContextMenuEntry.cs | 4 +- ILSpy/Commands/Pdb2XmlCommand.cs | 4 +- ILSpy/Commands/SaveCodeContextMenuEntry.cs | 2 +- ILSpy/ContextMenuEntry.cs | 36 ++- ILSpy/DebugSteps.xaml.cs | 4 +- ILSpy/Docking/DockWorkspace.cs | 32 +- ILSpy/MainWindow.xaml | 306 +----------------- ILSpy/MainWindow.xaml.cs | 31 +- ILSpy/TaskHelper.cs | 2 +- ILSpy/TextView/DecompilerTextView.cs | 14 +- ILSpy/TextView/DecompilerTextView.xaml | 5 +- .../ResourceNodes/ResourceTreeNode.cs | 2 +- ILSpy/ViewModels/DocumentModel.cs | 38 ++- ILSpy/ViewModels/PaneModel.cs | 41 ++- 19 files changed, 185 insertions(+), 370 deletions(-) diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs index 85d2b794a..8451fa533 100644 --- a/ILSpy/AboutPage.cs +++ b/ILSpy/AboutPage.cs @@ -41,13 +41,10 @@ namespace ICSharpCode.ILSpy [ExportMainMenuCommand(Menu = nameof(Resources._Help), Header = nameof(Resources._About), MenuOrder = 99999)] sealed class AboutPage : SimpleCommand { - [Import] - DecompilerTextView decompilerTextView = null; - public override void Execute(object parameter) { MainWindow.Instance.UnselectAll(); - Display(decompilerTextView); + Display(Docking.DockWorkspace.Instance.GetTextView()); } static readonly Uri UpdateUrl = new Uri("https://ilspy.net/updates.xml"); diff --git a/ILSpy/App.xaml.cs b/ILSpy/App.xaml.cs index 1417af930..ec05fb882 100644 --- a/ILSpy/App.xaml.cs +++ b/ILSpy/App.xaml.cs @@ -244,7 +244,7 @@ namespace ICSharpCode.ILSpy } } } - ILSpy.MainWindow.Instance.TextView.ShowText(output); + Docking.DockWorkspace.Instance.ShowText(output); e.Handled = true; } } diff --git a/ILSpy/Commands/DecompileAllCommand.cs b/ILSpy/Commands/DecompileAllCommand.cs index 9cf8cbc11..ac263edf7 100644 --- a/ILSpy/Commands/DecompileAllCommand.cs +++ b/ILSpy/Commands/DecompileAllCommand.cs @@ -38,7 +38,7 @@ namespace ICSharpCode.ILSpy public override void Execute(object parameter) { - MainWindow.Instance.TextView.RunWithCancellation(ct => Task.Factory.StartNew(() => { + Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new AvalonEditTextOutput(); Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) { if (!asm.HasLoadError) { @@ -64,7 +64,7 @@ namespace ICSharpCode.ILSpy } }); return output; - }, ct)).Then(output => MainWindow.Instance.TextView.ShowText(output)).HandleExceptions(); + }, ct)).Then(output => Docking.DockWorkspace.Instance.ShowText(output)).HandleExceptions(); } } @@ -77,7 +77,7 @@ namespace ICSharpCode.ILSpy var language = MainWindow.Instance.CurrentLanguage; var nodes = MainWindow.Instance.SelectedNodes.ToArray(); var options = new DecompilationOptions(); - MainWindow.Instance.TextView.RunWithCancellation(ct => Task.Factory.StartNew(() => { + Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task.Factory.StartNew(() => { options.CancellationToken = ct; Stopwatch w = Stopwatch.StartNew(); for (int i = 0; i < numRuns; ++i) { @@ -90,7 +90,7 @@ namespace ICSharpCode.ILSpy double msPerRun = w.Elapsed.TotalMilliseconds / numRuns; output.Write($"Average time: {msPerRun.ToString("f1")}ms\n"); return output; - }, ct)).Then(output => MainWindow.Instance.TextView.ShowText(output)).HandleExceptions(); + }, ct)).Then(output => Docking.DockWorkspace.Instance.ShowText(output)).HandleExceptions(); } } } diff --git a/ILSpy/Commands/DecompileInNewViewCommand.cs b/ILSpy/Commands/DecompileInNewViewCommand.cs index 2a1119fe3..e5ca26f03 100644 --- a/ILSpy/Commands/DecompileInNewViewCommand.cs +++ b/ILSpy/Commands/DecompileInNewViewCommand.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Linq; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Properties; @@ -24,7 +25,7 @@ using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Commands { - // [ExportContextMenuEntry(Header = nameof(Resources.DecompileToNewPanel), Icon = "images/Search", Category = nameof(Resources.Analyze), Order = 90)] + [ExportContextMenuEntry(Header = nameof(Resources.DecompileToNewPanel), Icon = "images/Search", Category = nameof(Resources.Analyze), Order = 90)] internal sealed class DecompileInNewViewCommand : IContextMenuEntry { public bool IsVisible(TextViewContext context) @@ -41,15 +42,15 @@ namespace ICSharpCode.ILSpy.Commands return true; } - public async void Execute(TextViewContext context) + public void Execute(TextViewContext context) { - var dtv = new DecompilerTextView(); var nodes = context.SelectedTreeNodes.Cast().ToArray(); var title = string.Join(", ", nodes.Select(x => x.ToString())); - // TODO - //MainWindow.Instance.ShowInNewPane(title, dtv, PanePosition.Document); - //DockWorkspace.Instance.Documents.Add(new ViewModels.DocumentModel() { Title = title }); - await dtv.DecompileAsync(MainWindow.Instance.CurrentLanguage, nodes, new DecompilationOptions()); + DockWorkspace.Instance.Documents.Add(new ViewModels.DecompiledDocumentModel(title, title)); + DockWorkspace.Instance.ActiveDocument = DockWorkspace.Instance.Documents.Last(); + MainWindow.Instance.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, (Action)(delegate { + DockWorkspace.Instance.GetTextView().DecompileAsync(MainWindow.Instance.CurrentLanguage, nodes, new DecompilationOptions()); + })); } } } diff --git a/ILSpy/Commands/DisassembleAllCommand.cs b/ILSpy/Commands/DisassembleAllCommand.cs index 74af340a1..23319be78 100644 --- a/ILSpy/Commands/DisassembleAllCommand.cs +++ b/ILSpy/Commands/DisassembleAllCommand.cs @@ -35,7 +35,7 @@ namespace ICSharpCode.ILSpy public override void Execute(object parameter) { - MainWindow.Instance.TextView.RunWithCancellation(ct => Task.Factory.StartNew(() => { + Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new AvalonEditTextOutput(); Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) { if (!asm.HasLoadError) { @@ -61,7 +61,7 @@ namespace ICSharpCode.ILSpy } }); return output; - }, ct)).Then(output => MainWindow.Instance.TextView.ShowText(output)).HandleExceptions(); + }, ct)).Then(output => Docking.DockWorkspace.Instance.ShowText(output)).HandleExceptions(); } } } diff --git a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs index a632ee27a..de254cfd4 100644 --- a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs +++ b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs @@ -64,7 +64,7 @@ namespace ICSharpCode.ILSpy if (dlg.ShowDialog() != true) return; DecompilationOptions options = new DecompilationOptions(); string fileName = dlg.FileName; - MainWindow.Instance.TextView.RunWithCancellation(ct => Task.Factory.StartNew(() => { + Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new AvalonEditTextOutput(); Stopwatch stopwatch = Stopwatch.StartNew(); using (FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write)) { @@ -83,7 +83,7 @@ namespace ICSharpCode.ILSpy output.AddButton(null, "Open Explorer", delegate { Process.Start("explorer", "/select,\"" + fileName + "\""); }); output.WriteLine(); return output; - }, ct)).Then(output => MainWindow.Instance.TextView.ShowText(output)).HandleExceptions(); + }, ct)).Then(output => Docking.DockWorkspace.Instance.ShowText(output)).HandleExceptions(); } } diff --git a/ILSpy/Commands/Pdb2XmlCommand.cs b/ILSpy/Commands/Pdb2XmlCommand.cs index 96932a1d9..91b481f7b 100644 --- a/ILSpy/Commands/Pdb2XmlCommand.cs +++ b/ILSpy/Commands/Pdb2XmlCommand.cs @@ -49,7 +49,7 @@ namespace ICSharpCode.ILSpy { var highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); var options = PdbToXmlOptions.IncludeEmbeddedSources | PdbToXmlOptions.IncludeMethodSpans | PdbToXmlOptions.IncludeTokens; - MainWindow.Instance.TextView.RunWithCancellation(ct => Task.Factory.StartNew(() => { + Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new AvalonEditTextOutput(); var writer = new TextOutputWriter(output); foreach (var node in nodes) { @@ -60,7 +60,7 @@ namespace ICSharpCode.ILSpy PdbToXmlConverter.ToXml(writer, pdbStream, peStream, options); } return output; - }, ct)).Then(output => MainWindow.Instance.TextView.ShowNodes(output, null, highlighting)).HandleExceptions(); + }, ct)).Then(output => Docking.DockWorkspace.Instance.ShowNodes(output, null, highlighting)).HandleExceptions(); } } diff --git a/ILSpy/Commands/SaveCodeContextMenuEntry.cs b/ILSpy/Commands/SaveCodeContextMenuEntry.cs index 3715b588d..e8cf4c4ac 100644 --- a/ILSpy/Commands/SaveCodeContextMenuEntry.cs +++ b/ILSpy/Commands/SaveCodeContextMenuEntry.cs @@ -55,7 +55,7 @@ namespace ICSharpCode.ILSpy.TextView public static void Execute(IReadOnlyList selectedNodes) { var currentLanguage = MainWindow.Instance.CurrentLanguage; - var textView = MainWindow.Instance.TextView; + var textView = Docking.DockWorkspace.Instance.GetTextView(); if (selectedNodes.Count == 1 && selectedNodes[0] is ILSpyTreeNode singleSelection) { // if there's only one treenode selected // we will invoke the custom Save logic diff --git a/ILSpy/ContextMenuEntry.cs b/ILSpy/ContextMenuEntry.cs index d04155f43..c6325650d 100644 --- a/ILSpy/ContextMenuEntry.cs +++ b/ILSpy/ContextMenuEntry.cs @@ -126,19 +126,22 @@ namespace ICSharpCode.ILSpy /// /// Enables extensible context menu support for the specified tree view. /// - public static void Add(SharpTreeView treeView, DecompilerTextView textView = null) + public static void Add(SharpTreeView treeView) { - var provider = new ContextMenuProvider(treeView, textView); + var provider = new ContextMenuProvider(treeView); treeView.ContextMenuOpening += provider.treeView_ContextMenuOpening; // Context menu is shown only when the ContextMenu property is not null before the // ContextMenuOpening event handler is called. treeView.ContextMenu = new ContextMenu(); - if (textView != null) { - textView.ContextMenuOpening += provider.textView_ContextMenuOpening; - // Context menu is shown only when the ContextMenu property is not null before the - // ContextMenuOpening event handler is called. - textView.ContextMenu = new ContextMenu(); - } + } + + public static void Add(DecompilerTextView textView) + { + var provider = new ContextMenuProvider(textView); + textView.ContextMenuOpening += provider.textView_ContextMenuOpening; + // Context menu is shown only when the ContextMenu property is not null before the + // ContextMenuOpening event handler is called. + textView.ContextMenu = new ContextMenu(); } public static void Add(ListBox listBox) @@ -157,16 +160,23 @@ namespace ICSharpCode.ILSpy { entries = App.ExportProvider.GetExports().ToArray(); } + + ContextMenuProvider(DecompilerTextView textView) + : this() + { + this.textView = textView ?? throw new ArgumentNullException(nameof(textView)); + } - ContextMenuProvider(SharpTreeView treeView, DecompilerTextView textView = null) : this() + ContextMenuProvider(SharpTreeView treeView) + : this() { - this.treeView = treeView; - this.textView = textView; + this.treeView = treeView ?? throw new ArgumentNullException(nameof(treeView)); } - ContextMenuProvider(ListBox listBox) : this() + ContextMenuProvider(ListBox listBox) + : this() { - this.listBox = listBox; + this.listBox = listBox ?? throw new ArgumentNullException(nameof(listBox)); } void treeView_ContextMenuOpening(object sender, ContextMenuEventArgs e) diff --git a/ILSpy/DebugSteps.xaml.cs b/ILSpy/DebugSteps.xaml.cs index 12cb5f807..71c4f448e 100644 --- a/ILSpy/DebugSteps.xaml.cs +++ b/ILSpy/DebugSteps.xaml.cs @@ -123,8 +123,8 @@ namespace ICSharpCode.ILSpy { lastSelectedStep = step; var window = MainWindow.Instance; - var state = window.TextView.GetState(); - window.TextView.DecompileAsync(window.CurrentLanguage, window.SelectedNodes, + var state = DockWorkspace.Instance.GetState(); + DockWorkspace.Instance.GetTextView().DecompileAsync(window.CurrentLanguage, window.SelectedNodes, new DecompilationOptions(window.CurrentLanguageVersion) { StepLimit = step, IsDebug = isDebug, diff --git a/ILSpy/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs index 71999a01b..1735507f7 100644 --- a/ILSpy/Docking/DockWorkspace.cs +++ b/ILSpy/Docking/DockWorkspace.cs @@ -1,5 +1,10 @@ -using System.Collections.ObjectModel; +using System; +using System.Collections.ObjectModel; using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; +using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Docking @@ -41,5 +46,30 @@ namespace ICSharpCode.ILSpy.Docking { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + + public void ShowText(AvalonEditTextOutput textOutput) + { + GetTextView().ShowText(textOutput); + } + + public DecompilerTextView GetTextView() + { + return ((DecompiledDocumentModel)ActiveDocument).TextView; + } + + public DecompilerTextViewState GetState() + { + return GetTextView().GetState(); + } + + public Task RunWithCancellation(Func> taskCreation) + { + return GetTextView().RunWithCancellation(taskCreation); + } + + internal void ShowNodes(AvalonEditTextOutput output, TreeNodes.ILSpyTreeNode[] nodes, IHighlightingDefinition highlighting) + { + GetTextView().ShowNodes(output, nodes, highlighting); + } } } diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index eaf340173..275419fa9 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -5,9 +5,10 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:tv="clr-namespace:ICSharpCode.TreeView;assembly=ICSharpCode.TreeView" xmlns:local="clr-namespace:ICSharpCode.ILSpy" xmlns:avalondock="http://schemas.xceed.com/wpf/xaml/avalondock" + xmlns:controls="clr-namespace:ICSharpCode.ILSpy.Controls" xmlns:avalondockproperties="clr-namespace:Xceed.Wpf.AvalonDock.Properties;assembly=Xceed.Wpf.AvalonDock" xmlns:docking="clr-namespace:ICSharpCode.ILSpy.Docking" - xmlns:controls="clr-namespace:ICSharpCode.ILSpy.Controls" + xmlns:textview="clr-namespace:ICSharpCode.ILSpy.TextView" xmlns:analyzers="clr-namespace:ICSharpCode.ILSpy.Analyzers" xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties" xmlns:viewmodels="clr-namespace:ICSharpCode.ILSpy.ViewModels" @@ -58,8 +59,7 @@ - - + @@ -165,300 +165,8 @@ DataContext="{Binding Workspace}" AnchorablesSource="{Binding ToolPanes}" DocumentsSource="{Binding Documents}" - ActiveContent="{Binding ActiveDocument, Mode=Default, Converter={StaticResource ActiveDocumentConverter}}" + ActiveContent="{Binding ActiveDocument, Mode=TwoWay, Converter={StaticResource ActiveDocumentConverter}}" AllowMixedOrientation="True"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -482,7 +190,7 @@ - + @@ -490,12 +198,14 @@ - diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 3e05b0f2a..66053dfce 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -73,8 +73,6 @@ namespace ICSharpCode.ILSpy AssemblyList assemblyList; AssemblyListTreeNode assemblyListTreeNode; - readonly DecompilerTextView decompilerTextView; - static MainWindow instance; public static MainWindow Instance { @@ -126,16 +124,13 @@ namespace ICSharpCode.ILSpy InitializeComponent(); - decompilerTextView = App.ExportProvider.GetExportedValue(); - mainPane.Content = decompilerTextView; - sessionSettings.DockLayout.Deserialize(new XmlLayoutSerializer(DockManager)); sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged; InitMainMenu(); InitToolbar(); - ContextMenuProvider.Add(treeView, decompilerTextView); + ContextMenuProvider.Add(treeView); this.Loaded += MainWindow_Loaded; } @@ -376,7 +371,7 @@ namespace ICSharpCode.ILSpy if (!found && treeView.SelectedItem == initialSelection) { AvalonEditTextOutput output = new AvalonEditTextOutput(); output.Write(string.Format("Cannot find '{0}' in command line specified assemblies.", navigateTo)); - decompilerTextView.ShowText(output); + DockWorkspace.Instance.ShowText(output); } } else if (relevantAssemblies.Count == 1) { // NavigateTo == null and an assembly was given on the command-line: @@ -404,7 +399,7 @@ namespace ICSharpCode.ILSpy // only if not showing the about page, perform the update check: await ShowMessageIfUpdatesAvailableAsync(spySettings); } else { - AboutPage.Display(decompilerTextView); + AboutPage.Display(DockWorkspace.Instance.GetTextView()); } } } @@ -466,7 +461,8 @@ namespace ICSharpCode.ILSpy void MainWindow_Loaded(object sender, RoutedEventArgs e) { DockWorkspace.Instance.ToolPanes.Add(AssemblyListPaneModel.Instance); - DockWorkspace.Instance.Documents.Add(new DocumentModel()); + DockWorkspace.Instance.Documents.Add(new DecompiledDocumentModel() { IsCloseable = false }); + DockWorkspace.Instance.ActiveDocument = DockWorkspace.Instance.Documents.First(); ILSpySettings spySettings = this.spySettingsForMainWindow_Loaded; this.spySettingsForMainWindow_Loaded = null; @@ -504,7 +500,7 @@ namespace ICSharpCode.ILSpy AvalonEditTextOutput output = new AvalonEditTextOutput(); if (FormatExceptions(App.StartupExceptions.ToArray(), output)) - decompilerTextView.ShowText(output); + DockWorkspace.Instance.ShowText(output); } bool FormatExceptions(App.ExceptionData[] exceptions, ITextOutput output) @@ -931,8 +927,7 @@ namespace ICSharpCode.ILSpy { DecompileSelectedNodes(); - if (SelectionChanged != null) - SelectionChanged(sender, e); + SelectionChanged?.Invoke(sender, e); } Task decompilationTask; @@ -947,7 +942,7 @@ namespace ICSharpCode.ILSpy return; if (recordHistory) { - var dtState = decompilerTextView.GetState(); + var dtState = DockWorkspace.Instance.GetState(); if (dtState != null) history.UpdateCurrent(new NavigationState(dtState)); history.Record(new NavigationState(treeView.SelectedItems.OfType())); @@ -955,10 +950,10 @@ namespace ICSharpCode.ILSpy if (treeView.SelectedItems.Count == 1) { ILSpyTreeNode node = treeView.SelectedItem as ILSpyTreeNode; - if (node != null && node.View(decompilerTextView)) + if (node != null && node.View(DockWorkspace.Instance.GetTextView())) return; } - decompilationTask = decompilerTextView.DecompileAsync(this.CurrentLanguage, this.SelectedNodes, new DecompilationOptions() { TextViewState = state }); + decompilationTask = DockWorkspace.Instance.GetTextView().DecompileAsync(this.CurrentLanguage, this.SelectedNodes, new DecompilationOptions() { TextViewState = state }); } void SaveCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) @@ -982,10 +977,6 @@ namespace ICSharpCode.ILSpy } } - public DecompilerTextView TextView { - get { return decompilerTextView; } - } - public Language CurrentLanguage => sessionSettings.FilterSettings.Language; public LanguageVersion CurrentLanguageVersion => sessionSettings.FilterSettings.LanguageVersion; @@ -1029,7 +1020,7 @@ namespace ICSharpCode.ILSpy void NavigateHistory(bool forward) { - var dtState = decompilerTextView.GetState(); + var dtState = DockWorkspace.Instance.GetState(); if (dtState != null) history.UpdateCurrent(new NavigationState(dtState)); var newState = forward ? history.GoForward() : history.GoBack(); diff --git a/ILSpy/TaskHelper.cs b/ILSpy/TaskHelper.cs index 8137ec152..bb76ffdbc 100644 --- a/ILSpy/TaskHelper.cs +++ b/ILSpy/TaskHelper.cs @@ -196,7 +196,7 @@ namespace ICSharpCode.ILSpy task.Catch(exception => MainWindow.Instance.Dispatcher.BeginInvoke(new Action(delegate { AvalonEditTextOutput output = new AvalonEditTextOutput(); output.Write(exception.ToString()); - MainWindow.Instance.TextView.ShowText(output); + Docking.DockWorkspace.Instance.ShowText(output); }))).IgnoreExceptions(); } } diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 52917540e..242e8bd47 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -53,6 +53,7 @@ using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.AvalonEdit; using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.ILSpy.ViewModels; using Microsoft.Win32; namespace ICSharpCode.ILSpy.TextView @@ -61,7 +62,6 @@ namespace ICSharpCode.ILSpy.TextView /// Manages the TextEditor showing the decompiled code. /// Contains all the threading logic that makes the decompiler work in the background. /// - [Export, PartCreationPolicy(CreationPolicy.Shared)] public sealed partial class DecompilerTextView : UserControl, IDisposable { readonly ReferenceElementGenerator referenceElementGenerator; @@ -103,7 +103,7 @@ namespace ICSharpCode.ILSpy.TextView }); InitializeComponent(); - + this.referenceElementGenerator = new ReferenceElementGenerator(this.JumpToReference, this.IsLink); textEditor.TextArea.TextView.ElementGenerators.Add(referenceElementGenerator); this.uiElementGenerator = new UIElementGenerator(); @@ -140,6 +140,8 @@ namespace ICSharpCode.ILSpy.TextView // add marker service & margin textEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService); textEditor.TextArea.TextView.LineTransformers.Add(textMarkerService); + + ContextMenuProvider.Add(this); } void RemoveEditCommand(RoutedUICommand command) @@ -1032,6 +1034,14 @@ namespace ICSharpCode.ILSpy.TextView } } #endregion + + private void self_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if (e.OldValue is DecompiledDocumentModel oldModel) + oldModel.TextView = null; + if (e.NewValue is DecompiledDocumentModel newModel) + newModel.TextView = this; + } } public class DecompilerTextViewState diff --git a/ILSpy/TextView/DecompilerTextView.xaml b/ILSpy/TextView/DecompilerTextView.xaml index eed20cda4..fbeaf98d3 100644 --- a/ILSpy/TextView/DecompilerTextView.xaml +++ b/ILSpy/TextView/DecompilerTextView.xaml @@ -1,8 +1,9 @@  + xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties" + xmlns:ae="clr-namespace:ICSharpCode.AvalonEdit;assembly=ICSharpCode.AvalonEdit" + DataContextChanged="self_DataContextChanged"> diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs index ff57aa944..c13118e1e 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs @@ -65,7 +65,7 @@ namespace ICSharpCode.ILSpy.TreeNodes ISmartTextOutput smartOutput = output as ISmartTextOutput; if (smartOutput != null) { - smartOutput.AddButton(Images.Save, Resources.Save, delegate { Save(MainWindow.Instance.TextView); }); + smartOutput.AddButton(Images.Save, Resources.Save, delegate { Save(Docking.DockWorkspace.Instance.GetTextView()); }); output.WriteLine(); } } diff --git a/ILSpy/ViewModels/DocumentModel.cs b/ILSpy/ViewModels/DocumentModel.cs index 2fa9129ab..3e7fbeb8d 100644 --- a/ILSpy/ViewModels/DocumentModel.cs +++ b/ILSpy/ViewModels/DocumentModel.cs @@ -1,14 +1,40 @@ -namespace ICSharpCode.ILSpy.ViewModels +using System; +using ICSharpCode.ILSpy.TextView; + +namespace ICSharpCode.ILSpy.ViewModels { - public class DocumentModel : PaneModel + public abstract class DocumentModel : PaneModel { public override PanePosition DefaultPosition => PanePosition.Document; - public DocumentModel() + protected DocumentModel(string contentId, string title) + { + this.ContentId = contentId; + this.Title = title; + } + } + + public class DecompiledDocumentModel : DocumentModel + { + public DecompiledDocumentModel() + : base("//Decompiled", "View") { - ContentId = "document"; - Title = "View"; - IsCloseable = false; + } + + public DecompiledDocumentModel(string id, string title) + : base("//Decompiled/" + id, title) + { + } + + private DecompilerTextView textView; + public DecompilerTextView TextView { + get => textView; + set { + if (textView != value) { + textView = value; + RaisePropertyChanged(nameof(TextView)); + } + } } } } diff --git a/ILSpy/ViewModels/PaneModel.cs b/ILSpy/ViewModels/PaneModel.cs index bce2ecc70..61b753276 100644 --- a/ILSpy/ViewModels/PaneModel.cs +++ b/ILSpy/ViewModels/PaneModel.cs @@ -1,10 +1,38 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; using System.Windows.Input; namespace ICSharpCode.ILSpy.ViewModels { public abstract class PaneModel : INotifyPropertyChanged { + class CloseCommandImpl : ICommand + { + readonly PaneModel model; + + public CloseCommandImpl(PaneModel model) + { + this.model = model; + } + + public event EventHandler CanExecuteChanged; + + public bool CanExecute(object parameter) + { + return model.IsCloseable; + } + + public void Execute(object parameter) + { + Docking.DockWorkspace.Instance.Remove(model); + } + } + + public PaneModel() + { + this.closeCommand = new CloseCommandImpl(this); + } + public abstract PanePosition DefaultPosition { get; } public event PropertyChangedEventHandler PropertyChanged; @@ -58,6 +86,17 @@ namespace ICSharpCode.ILSpy.ViewModels } } + private ICommand closeCommand; + public ICommand CloseCommand { + get { return closeCommand; } + set { + if (closeCommand != value) { + closeCommand = value; + RaisePropertyChanged(nameof(CloseCommand)); + } + } + } + private string contentId; public string ContentId { get => contentId;