Browse Source

Add primitive multi-document support.

pull/1801/head
Siegfried Pammer 6 years ago
parent
commit
84bb61cc5b
  1. 5
      ILSpy/AboutPage.cs
  2. 2
      ILSpy/App.xaml.cs
  3. 8
      ILSpy/Commands/DecompileAllCommand.cs
  4. 15
      ILSpy/Commands/DecompileInNewViewCommand.cs
  5. 4
      ILSpy/Commands/DisassembleAllCommand.cs
  6. 4
      ILSpy/Commands/GeneratePdbContextMenuEntry.cs
  7. 4
      ILSpy/Commands/Pdb2XmlCommand.cs
  8. 2
      ILSpy/Commands/SaveCodeContextMenuEntry.cs
  9. 36
      ILSpy/ContextMenuEntry.cs
  10. 4
      ILSpy/DebugSteps.xaml.cs
  11. 32
      ILSpy/Docking/DockWorkspace.cs
  12. 306
      ILSpy/MainWindow.xaml
  13. 31
      ILSpy/MainWindow.xaml.cs
  14. 2
      ILSpy/TaskHelper.cs
  15. 12
      ILSpy/TextView/DecompilerTextView.cs
  16. 3
      ILSpy/TextView/DecompilerTextView.xaml
  17. 2
      ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs
  18. 38
      ILSpy/ViewModels/DocumentModel.cs
  19. 41
      ILSpy/ViewModels/PaneModel.cs

5
ILSpy/AboutPage.cs

@ -41,13 +41,10 @@ namespace ICSharpCode.ILSpy
[ExportMainMenuCommand(Menu = nameof(Resources._Help), Header = nameof(Resources._About), MenuOrder = 99999)] [ExportMainMenuCommand(Menu = nameof(Resources._Help), Header = nameof(Resources._About), MenuOrder = 99999)]
sealed class AboutPage : SimpleCommand sealed class AboutPage : SimpleCommand
{ {
[Import]
DecompilerTextView decompilerTextView = null;
public override void Execute(object parameter) public override void Execute(object parameter)
{ {
MainWindow.Instance.UnselectAll(); MainWindow.Instance.UnselectAll();
Display(decompilerTextView); Display(Docking.DockWorkspace.Instance.GetTextView());
} }
static readonly Uri UpdateUrl = new Uri("https://ilspy.net/updates.xml"); static readonly Uri UpdateUrl = new Uri("https://ilspy.net/updates.xml");

2
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; e.Handled = true;
} }
} }

8
ILSpy/Commands/DecompileAllCommand.cs

@ -38,7 +38,7 @@ namespace ICSharpCode.ILSpy
public override void Execute(object parameter) public override void Execute(object parameter)
{ {
MainWindow.Instance.TextView.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => { Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput(); AvalonEditTextOutput output = new AvalonEditTextOutput();
Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) { Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) {
if (!asm.HasLoadError) { if (!asm.HasLoadError) {
@ -64,7 +64,7 @@ namespace ICSharpCode.ILSpy
} }
}); });
return output; 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 language = MainWindow.Instance.CurrentLanguage;
var nodes = MainWindow.Instance.SelectedNodes.ToArray(); var nodes = MainWindow.Instance.SelectedNodes.ToArray();
var options = new DecompilationOptions(); var options = new DecompilationOptions();
MainWindow.Instance.TextView.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => { Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
options.CancellationToken = ct; options.CancellationToken = ct;
Stopwatch w = Stopwatch.StartNew(); Stopwatch w = Stopwatch.StartNew();
for (int i = 0; i < numRuns; ++i) { for (int i = 0; i < numRuns; ++i) {
@ -90,7 +90,7 @@ namespace ICSharpCode.ILSpy
double msPerRun = w.Elapsed.TotalMilliseconds / numRuns; double msPerRun = w.Elapsed.TotalMilliseconds / numRuns;
output.Write($"Average time: {msPerRun.ToString("f1")}ms\n"); output.Write($"Average time: {msPerRun.ToString("f1")}ms\n");
return output; return output;
}, ct)).Then(output => MainWindow.Instance.TextView.ShowText(output)).HandleExceptions(); }, ct)).Then(output => Docking.DockWorkspace.Instance.ShowText(output)).HandleExceptions();
} }
} }
} }

15
ILSpy/Commands/DecompileInNewViewCommand.cs

@ -16,6 +16,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System;
using System.Linq; using System.Linq;
using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
@ -24,7 +25,7 @@ using ICSharpCode.ILSpy.TreeNodes;
namespace ICSharpCode.ILSpy.Commands 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 internal sealed class DecompileInNewViewCommand : IContextMenuEntry
{ {
public bool IsVisible(TextViewContext context) public bool IsVisible(TextViewContext context)
@ -41,15 +42,15 @@ namespace ICSharpCode.ILSpy.Commands
return true; return true;
} }
public async void Execute(TextViewContext context) public void Execute(TextViewContext context)
{ {
var dtv = new DecompilerTextView();
var nodes = context.SelectedTreeNodes.Cast<ILSpyTreeNode>().ToArray(); var nodes = context.SelectedTreeNodes.Cast<ILSpyTreeNode>().ToArray();
var title = string.Join(", ", nodes.Select(x => x.ToString())); var title = string.Join(", ", nodes.Select(x => x.ToString()));
// TODO DockWorkspace.Instance.Documents.Add(new ViewModels.DecompiledDocumentModel(title, title));
//MainWindow.Instance.ShowInNewPane(title, dtv, PanePosition.Document); DockWorkspace.Instance.ActiveDocument = DockWorkspace.Instance.Documents.Last();
//DockWorkspace.Instance.Documents.Add(new ViewModels.DocumentModel() { Title = title }); MainWindow.Instance.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, (Action)(delegate {
await dtv.DecompileAsync(MainWindow.Instance.CurrentLanguage, nodes, new DecompilationOptions()); DockWorkspace.Instance.GetTextView().DecompileAsync(MainWindow.Instance.CurrentLanguage, nodes, new DecompilationOptions());
}));
} }
} }
} }

4
ILSpy/Commands/DisassembleAllCommand.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.ILSpy
public override void Execute(object parameter) public override void Execute(object parameter)
{ {
MainWindow.Instance.TextView.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => { Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput(); AvalonEditTextOutput output = new AvalonEditTextOutput();
Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) { Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) {
if (!asm.HasLoadError) { if (!asm.HasLoadError) {
@ -61,7 +61,7 @@ namespace ICSharpCode.ILSpy
} }
}); });
return output; return output;
}, ct)).Then(output => MainWindow.Instance.TextView.ShowText(output)).HandleExceptions(); }, ct)).Then(output => Docking.DockWorkspace.Instance.ShowText(output)).HandleExceptions();
} }
} }
} }

4
ILSpy/Commands/GeneratePdbContextMenuEntry.cs

@ -64,7 +64,7 @@ namespace ICSharpCode.ILSpy
if (dlg.ShowDialog() != true) return; if (dlg.ShowDialog() != true) return;
DecompilationOptions options = new DecompilationOptions(); DecompilationOptions options = new DecompilationOptions();
string fileName = dlg.FileName; string fileName = dlg.FileName;
MainWindow.Instance.TextView.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => { Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput(); AvalonEditTextOutput output = new AvalonEditTextOutput();
Stopwatch stopwatch = Stopwatch.StartNew(); Stopwatch stopwatch = Stopwatch.StartNew();
using (FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write)) { 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.AddButton(null, "Open Explorer", delegate { Process.Start("explorer", "/select,\"" + fileName + "\""); });
output.WriteLine(); output.WriteLine();
return output; return output;
}, ct)).Then(output => MainWindow.Instance.TextView.ShowText(output)).HandleExceptions(); }, ct)).Then(output => Docking.DockWorkspace.Instance.ShowText(output)).HandleExceptions();
} }
} }

4
ILSpy/Commands/Pdb2XmlCommand.cs

@ -49,7 +49,7 @@ namespace ICSharpCode.ILSpy
{ {
var highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); var highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml");
var options = PdbToXmlOptions.IncludeEmbeddedSources | PdbToXmlOptions.IncludeMethodSpans | PdbToXmlOptions.IncludeTokens; var options = PdbToXmlOptions.IncludeEmbeddedSources | PdbToXmlOptions.IncludeMethodSpans | PdbToXmlOptions.IncludeTokens;
MainWindow.Instance.TextView.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => { Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput(); AvalonEditTextOutput output = new AvalonEditTextOutput();
var writer = new TextOutputWriter(output); var writer = new TextOutputWriter(output);
foreach (var node in nodes) { foreach (var node in nodes) {
@ -60,7 +60,7 @@ namespace ICSharpCode.ILSpy
PdbToXmlConverter.ToXml(writer, pdbStream, peStream, options); PdbToXmlConverter.ToXml(writer, pdbStream, peStream, options);
} }
return output; return output;
}, ct)).Then(output => MainWindow.Instance.TextView.ShowNodes(output, null, highlighting)).HandleExceptions(); }, ct)).Then(output => Docking.DockWorkspace.Instance.ShowNodes(output, null, highlighting)).HandleExceptions();
} }
} }

2
ILSpy/Commands/SaveCodeContextMenuEntry.cs

@ -55,7 +55,7 @@ namespace ICSharpCode.ILSpy.TextView
public static void Execute(IReadOnlyList<SharpTreeNode> selectedNodes) public static void Execute(IReadOnlyList<SharpTreeNode> selectedNodes)
{ {
var currentLanguage = MainWindow.Instance.CurrentLanguage; 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 (selectedNodes.Count == 1 && selectedNodes[0] is ILSpyTreeNode singleSelection) {
// if there's only one treenode selected // if there's only one treenode selected
// we will invoke the custom Save logic // we will invoke the custom Save logic

36
ILSpy/ContextMenuEntry.cs

@ -126,19 +126,22 @@ namespace ICSharpCode.ILSpy
/// <summary> /// <summary>
/// Enables extensible context menu support for the specified tree view. /// Enables extensible context menu support for the specified tree view.
/// </summary> /// </summary>
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; treeView.ContextMenuOpening += provider.treeView_ContextMenuOpening;
// Context menu is shown only when the ContextMenu property is not null before the // Context menu is shown only when the ContextMenu property is not null before the
// ContextMenuOpening event handler is called. // ContextMenuOpening event handler is called.
treeView.ContextMenu = new ContextMenu(); 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 public static void Add(DecompilerTextView textView)
// ContextMenuOpening event handler is called. {
textView.ContextMenu = new ContextMenu(); 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) public static void Add(ListBox listBox)
@ -158,15 +161,22 @@ namespace ICSharpCode.ILSpy
entries = App.ExportProvider.GetExports<IContextMenuEntry, IContextMenuEntryMetadata>().ToArray(); entries = App.ExportProvider.GetExports<IContextMenuEntry, IContextMenuEntryMetadata>().ToArray();
} }
ContextMenuProvider(SharpTreeView treeView, DecompilerTextView textView = null) : this() ContextMenuProvider(DecompilerTextView textView)
: this()
{
this.textView = textView ?? throw new ArgumentNullException(nameof(textView));
}
ContextMenuProvider(SharpTreeView treeView)
: this()
{ {
this.treeView = treeView; this.treeView = treeView ?? throw new ArgumentNullException(nameof(treeView));
this.textView = textView;
} }
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) void treeView_ContextMenuOpening(object sender, ContextMenuEventArgs e)

4
ILSpy/DebugSteps.xaml.cs

@ -123,8 +123,8 @@ namespace ICSharpCode.ILSpy
{ {
lastSelectedStep = step; lastSelectedStep = step;
var window = MainWindow.Instance; var window = MainWindow.Instance;
var state = window.TextView.GetState(); var state = DockWorkspace.Instance.GetState();
window.TextView.DecompileAsync(window.CurrentLanguage, window.SelectedNodes, DockWorkspace.Instance.GetTextView().DecompileAsync(window.CurrentLanguage, window.SelectedNodes,
new DecompilationOptions(window.CurrentLanguageVersion) { new DecompilationOptions(window.CurrentLanguageVersion) {
StepLimit = step, StepLimit = step,
IsDebug = isDebug, IsDebug = isDebug,

32
ILSpy/Docking/DockWorkspace.cs

@ -1,5 +1,10 @@
using System.Collections.ObjectModel; using System;
using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpy.ViewModels;
namespace ICSharpCode.ILSpy.Docking namespace ICSharpCode.ILSpy.Docking
@ -41,5 +46,30 @@ namespace ICSharpCode.ILSpy.Docking
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 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<T> RunWithCancellation<T>(Func<CancellationToken, Task<T>> taskCreation)
{
return GetTextView().RunWithCancellation(taskCreation);
}
internal void ShowNodes(AvalonEditTextOutput output, TreeNodes.ILSpyTreeNode[] nodes, IHighlightingDefinition highlighting)
{
GetTextView().ShowNodes(output, nodes, highlighting);
}
} }
} }

306
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="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:local="clr-namespace:ICSharpCode.ILSpy"
xmlns:avalondock="http://schemas.xceed.com/wpf/xaml/avalondock" 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:avalondockproperties="clr-namespace:Xceed.Wpf.AvalonDock.Properties;assembly=Xceed.Wpf.AvalonDock"
xmlns:docking="clr-namespace:ICSharpCode.ILSpy.Docking" 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:analyzers="clr-namespace:ICSharpCode.ILSpy.Analyzers"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties" xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:viewmodels="clr-namespace:ICSharpCode.ILSpy.ViewModels" xmlns:viewmodels="clr-namespace:ICSharpCode.ILSpy.ViewModels"
@ -58,8 +59,7 @@
<ContentPresenter x:Key="MainPane" /> <ContentPresenter x:Key="MainPane" />
<DataTemplate x:Key="DecompilerTextViewTemplate"> <DataTemplate x:Key="DecompilerTextViewTemplate">
<!-- decompilerTextView is inserted into the mainPane by code --> <textview:DecompilerTextView DataContext="{Binding}" />
<ContentControl Content="{StaticResource MainPane}" />
</DataTemplate> </DataTemplate>
</Window.Resources> </Window.Resources>
@ -165,300 +165,8 @@
DataContext="{Binding Workspace}" DataContext="{Binding Workspace}"
AnchorablesSource="{Binding ToolPanes}" AnchorablesSource="{Binding ToolPanes}"
DocumentsSource="{Binding Documents}" DocumentsSource="{Binding Documents}"
ActiveContent="{Binding ActiveDocument, Mode=Default, Converter={StaticResource ActiveDocumentConverter}}" ActiveContent="{Binding ActiveDocument, Mode=TwoWay, Converter={StaticResource ActiveDocumentConverter}}"
AllowMixedOrientation="True"> AllowMixedOrientation="True">
<avalondock:DockingManager.Resources>
<Style TargetType="avalondock:AnchorablePaneTitle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<avalondock:DropDownControlArea DropDownContextMenu="{Binding Model.Root.Manager.AnchorableContextMenu, RelativeSource={RelativeSource TemplatedParent}}"
DropDownContextMenuDataContext="{Binding Path=LayoutItem, RelativeSource={RelativeSource TemplatedParent}}">
<ContentPresenter Content="{Binding Model, RelativeSource={RelativeSource TemplatedParent}}"
ContentTemplate="{Binding Model.Root.Manager.AnchorableTitleTemplate, RelativeSource={RelativeSource TemplatedParent}}"
ContentTemplateSelector="{Binding Model.Root.Manager.AnchorableTitleTemplateSelector, RelativeSource={RelativeSource TemplatedParent}}" />
</avalondock:DropDownControlArea>
<avalondock:DropDownButton Style="{StaticResource {x:Static ToolBar.ToggleButtonStyleKey}}"
Focusable="False"
Grid.Column="1"
DropDownContextMenu="{Binding Model.Root.Manager.AnchorableContextMenu, RelativeSource={RelativeSource TemplatedParent}}"
DropDownContextMenuDataContext="{Binding Path=LayoutItem, RelativeSource={RelativeSource TemplatedParent}}"
ToolTip="{x:Static avalondockproperties:Resources.Anchorable_CxMenu_Hint}">
<Border Background="White">
<Image Source="/Xceed.Wpf.AvalonDock;component/Themes/Generic/Images/PinMenu.png">
</Image>
</Border>
</avalondock:DropDownButton>
<Button x:Name="PART_AutoHidePin"
Grid.Column="2"
Focusable="False"
Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
Visibility="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}"
Command="{Binding Path=LayoutItem.AutoHideCommand, RelativeSource={RelativeSource TemplatedParent}}"
ToolTip="{x:Static avalondockproperties:Resources.Anchorable_BtnAutoHide_Hint}">
<Border Background="White">
<Image Source="/Xceed.Wpf.AvalonDock;component/Themes/Generic/Images/PinAutoHide.png">
</Image>
</Border>
</Button>
<Button x:Name="PART_HidePin"
Grid.Column="3"
Focusable="False"
Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
Visibility="{Binding Path=LayoutItem.Model.IsCloseable, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}"
Command="{Binding Path=LayoutItem.Model.CloseCommand, RelativeSource={RelativeSource TemplatedParent}}"
ToolTip="{x:Static avalondockproperties:Resources.Anchorable_BtnClose_Hint}">
<Border Background="White">
<Image Source="/Xceed.Wpf.AvalonDock;component/Themes/Generic/Images/PinClose.png">
</Image>
</Border>
</Button>
</Grid>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Model.IsAutoHidden, RelativeSource={RelativeSource Mode=Self}}"
Value="True">
<Setter Property="LayoutTransform"
TargetName="PART_AutoHidePin">
<Setter.Value>
<RotateTransform Angle="90" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Model.CanClose, RelativeSource={RelativeSource Mode=Self}}"
Value="True">
<Setter Property="Command"
TargetName="PART_HidePin"
Value="{Binding Path=LayoutItem.CloseCommand, RelativeSource={RelativeSource TemplatedParent}}" />
<Setter Property="ToolTip"
TargetName="PART_HidePin"
Value="{x:Static avalondockproperties:Resources.Document_Close}" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type avalondock:LayoutAnchorableControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type avalondock:LayoutAnchorableControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
KeyboardNavigation.TabNavigation="Cycle">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border x:Name="Header">
<avalondock:AnchorablePaneTitle Model="{Binding Model, RelativeSource={RelativeSource TemplatedParent}}" />
</Border>
<!--
Added ContentTemplate and ContentTemplateSelector
https://github.com/xceedsoftware/wpftoolkit/issues/1525
-->
<ContentPresenter Grid.Row="1"
FlowDirection="{TemplateBinding FlowDirection}"
Content="{Binding LayoutItem.View, RelativeSource={RelativeSource TemplatedParent}}"
ContentTemplate="{Binding LayoutItem.View.ContentTemplate, RelativeSource={RelativeSource TemplatedParent}}"
ContentTemplateSelector="{Binding LayoutItem.View.ContentTemplateSelector, RelativeSource={RelativeSource TemplatedParent}}"
/>
<!--<ContentPresenter
FlowDirection="{TemplateBinding FlowDirection}"
Content="{Binding Model.Content, RelativeSource={RelativeSource TemplatedParent}}"
ContentTemplate="{Binding LayoutItemTemplate, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type avalonDock:DockingManager}, Mode=FindAncestor}}"
ContentTemplateSelector="{Binding LayoutItemTemplateSelector, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type avalonDock:DockingManager}, Mode=FindAncestor}}"
Grid.Row="1"/>-->
</Grid>
</Border>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=Model.IsFloating}"
Value="True" />
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=Model.Parent.IsDirectlyHostedInFloatingWindow}"
Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Visibility"
Value="Collapsed"
TargetName="Header" />
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--AnchorablePaneControlStyle-->
<Style x:Key="AnchorablePaneControlStyle"
TargetType="{x:Type avalondock:LayoutAnchorablePaneControl}">
<Setter Property="Foreground"
Value="{Binding Model.Root.Manager.Foreground, RelativeSource={RelativeSource Self}}" />
<Setter Property="Background"
Value="{Binding Model.Root.Manager.Background, RelativeSource={RelativeSource Self}}" />
<Setter Property="TabStripPlacement"
Value="Bottom" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type avalondock:LayoutAnchorablePaneControl}">
<Grid ClipToBounds="true"
SnapsToDevicePixels="true"
KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--Following border is required to catch mouse events-->
<Border Background="Transparent"
Grid.RowSpan="2" />
<Border x:Name="ContentPanel"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Grid.Column="0"
KeyboardNavigation.DirectionalNavigation="Contained"
Grid.Row="0"
KeyboardNavigation.TabIndex="2"
KeyboardNavigation.TabNavigation="Cycle">
<ContentPresenter x:Name="PART_SelectedContentHost"
ContentSource="SelectedContent"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
<avalondock:AnchorablePaneTabPanel x:Name="HeaderPanel"
Margin="2,0,2,2"
IsItemsHost="true"
Grid.Row="1"
KeyboardNavigation.TabIndex="1"
Panel.ZIndex="1" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsSelected"
Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="IsEnabled"
Value="{Binding IsEnabled}" />
<Setter Property="ToolTip"
Value="{Binding ToolTip}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid SnapsToDevicePixels="true">
<Border x:Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1,0,1,1"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}">
<ContentPresenter x:Name="Content"
ContentSource="Header"
HorizontalAlignment="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Selector.IsSelected"
Value="true">
<Setter Property="Background"
Value="White" />
<Setter Property="Panel.ZIndex"
Value="1" />
<Setter Property="Margin"
Value="0,-1,-1,-2" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver"
Value="true" />
<Condition Property="Selector.IsSelected"
Value="false" />
</MultiTrigger.Conditions>
<Setter Property="Background"
Value="{DynamicResource {x:Static SystemColors.GradientInactiveCaptionBrushKey}}" />
<Setter Property="BorderBrush"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
<Setter Property="Panel.ZIndex"
Value="0" />
</MultiTrigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}, Path=Items.Count, FallbackValue=1}"
Value="1">
<Setter Property="Visibility"
Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<avalondock:LayoutAnchorableTabItem Model="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<avalondock:LayoutAnchorableControl Model="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</avalondock:DockingManager.Resources>
<avalondock:DockingManager.AnchorablePaneControlStyle>
<StaticResource ResourceKey="AnchorablePaneControlStyle" />
</avalondock:DockingManager.AnchorablePaneControlStyle>
<avalondock:LayoutRoot> <avalondock:LayoutRoot>
<avalondock:LayoutPanel Orientation="Horizontal"> <avalondock:LayoutPanel Orientation="Horizontal">
<avalondock:LayoutAnchorablePane DockMinWidth="150" Name="LeftPane" /> <avalondock:LayoutAnchorablePane DockMinWidth="150" Name="LeftPane" />
@ -482,7 +190,7 @@
<docking:TemplateMapping Type="{x:Type viewmodels:SearchPaneModel}" Template="{StaticResource SearchPaneTemplate}" /> <docking:TemplateMapping Type="{x:Type viewmodels:SearchPaneModel}" Template="{StaticResource SearchPaneTemplate}" />
<docking:TemplateMapping Type="{x:Type viewmodels:DebugStepsPaneModel}" Template="{StaticResource DebugStepsPaneTemplate}" /> <docking:TemplateMapping Type="{x:Type viewmodels:DebugStepsPaneModel}" Template="{StaticResource DebugStepsPaneTemplate}" />
<docking:TemplateMapping Type="{x:Type viewmodels:AnalyzerPaneModel}" Template="{StaticResource AnalyzerPaneTemplate}" /> <docking:TemplateMapping Type="{x:Type viewmodels:AnalyzerPaneModel}" Template="{StaticResource AnalyzerPaneTemplate}" />
<docking:TemplateMapping Type="{x:Type viewmodels:DocumentModel}" Template="{StaticResource DecompilerTextViewTemplate}" /> <docking:TemplateMapping Type="{x:Type viewmodels:DecompiledDocumentModel}" Template="{StaticResource DecompilerTextViewTemplate}" />
</docking:PaneTemplateSelector.Mappings> </docking:PaneTemplateSelector.Mappings>
</docking:PaneTemplateSelector> </docking:PaneTemplateSelector>
</avalondock:DockingManager.LayoutItemTemplateSelector> </avalondock:DockingManager.LayoutItemTemplateSelector>
@ -490,12 +198,14 @@
<avalondock:DockingManager.LayoutItemContainerStyleSelector> <avalondock:DockingManager.LayoutItemContainerStyleSelector>
<docking:PaneStyleSelector> <docking:PaneStyleSelector>
<docking:PaneStyleSelector.ToolPaneStyle> <docking:PaneStyleSelector.ToolPaneStyle>
<Style TargetType="{x:Type avalondock:LayoutItem}"> <Style TargetType="{x:Type avalondock:LayoutAnchorableItem}">
<Setter Property="Title" Value="{Binding Model.Title}"/> <Setter Property="Title" Value="{Binding Model.Title}"/>
<Setter Property="Visibility" Value="{Binding Model.IsVisible, Mode=TwoWay, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter={x:Static Visibility.Hidden}}"/> <Setter Property="Visibility" Value="{Binding Model.IsVisible, Mode=TwoWay, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter={x:Static Visibility.Hidden}}"/>
<Setter Property="ContentId" Value="{Binding Model.ContentId}"/> <Setter Property="ContentId" Value="{Binding Model.ContentId}"/>
<Setter Property="IsSelected" Value="{Binding Model.IsSelected, Mode=TwoWay}"/> <Setter Property="IsSelected" Value="{Binding Model.IsSelected, Mode=TwoWay}"/>
<Setter Property="IsActive" Value="{Binding Model.IsActive, Mode=TwoWay}"/> <Setter Property="IsActive" Value="{Binding Model.IsActive, Mode=TwoWay}"/>
<Setter Property="CanHide" Value="{Binding Model.IsCloseable}" />
<Setter Property="HideCommand" Value="{Binding Model.CloseCommand}" />
<Setter Property="CanClose" Value="{Binding Model.IsCloseable}" /> <Setter Property="CanClose" Value="{Binding Model.IsCloseable}" />
</Style> </Style>
</docking:PaneStyleSelector.ToolPaneStyle> </docking:PaneStyleSelector.ToolPaneStyle>

31
ILSpy/MainWindow.xaml.cs

@ -73,8 +73,6 @@ namespace ICSharpCode.ILSpy
AssemblyList assemblyList; AssemblyList assemblyList;
AssemblyListTreeNode assemblyListTreeNode; AssemblyListTreeNode assemblyListTreeNode;
readonly DecompilerTextView decompilerTextView;
static MainWindow instance; static MainWindow instance;
public static MainWindow Instance { public static MainWindow Instance {
@ -126,16 +124,13 @@ namespace ICSharpCode.ILSpy
InitializeComponent(); InitializeComponent();
decompilerTextView = App.ExportProvider.GetExportedValue<DecompilerTextView>();
mainPane.Content = decompilerTextView;
sessionSettings.DockLayout.Deserialize(new XmlLayoutSerializer(DockManager)); sessionSettings.DockLayout.Deserialize(new XmlLayoutSerializer(DockManager));
sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged; sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged;
InitMainMenu(); InitMainMenu();
InitToolbar(); InitToolbar();
ContextMenuProvider.Add(treeView, decompilerTextView); ContextMenuProvider.Add(treeView);
this.Loaded += MainWindow_Loaded; this.Loaded += MainWindow_Loaded;
} }
@ -376,7 +371,7 @@ namespace ICSharpCode.ILSpy
if (!found && treeView.SelectedItem == initialSelection) { if (!found && treeView.SelectedItem == initialSelection) {
AvalonEditTextOutput output = new AvalonEditTextOutput(); AvalonEditTextOutput output = new AvalonEditTextOutput();
output.Write(string.Format("Cannot find '{0}' in command line specified assemblies.", navigateTo)); 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) { } else if (relevantAssemblies.Count == 1) {
// NavigateTo == null and an assembly was given on the command-line: // 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: // only if not showing the about page, perform the update check:
await ShowMessageIfUpdatesAvailableAsync(spySettings); await ShowMessageIfUpdatesAvailableAsync(spySettings);
} else { } else {
AboutPage.Display(decompilerTextView); AboutPage.Display(DockWorkspace.Instance.GetTextView());
} }
} }
} }
@ -466,7 +461,8 @@ namespace ICSharpCode.ILSpy
void MainWindow_Loaded(object sender, RoutedEventArgs e) void MainWindow_Loaded(object sender, RoutedEventArgs e)
{ {
DockWorkspace.Instance.ToolPanes.Add(AssemblyListPaneModel.Instance); 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; ILSpySettings spySettings = this.spySettingsForMainWindow_Loaded;
this.spySettingsForMainWindow_Loaded = null; this.spySettingsForMainWindow_Loaded = null;
@ -504,7 +500,7 @@ namespace ICSharpCode.ILSpy
AvalonEditTextOutput output = new AvalonEditTextOutput(); AvalonEditTextOutput output = new AvalonEditTextOutput();
if (FormatExceptions(App.StartupExceptions.ToArray(), output)) if (FormatExceptions(App.StartupExceptions.ToArray(), output))
decompilerTextView.ShowText(output); DockWorkspace.Instance.ShowText(output);
} }
bool FormatExceptions(App.ExceptionData[] exceptions, ITextOutput output) bool FormatExceptions(App.ExceptionData[] exceptions, ITextOutput output)
@ -931,8 +927,7 @@ namespace ICSharpCode.ILSpy
{ {
DecompileSelectedNodes(); DecompileSelectedNodes();
if (SelectionChanged != null) SelectionChanged?.Invoke(sender, e);
SelectionChanged(sender, e);
} }
Task decompilationTask; Task decompilationTask;
@ -947,7 +942,7 @@ namespace ICSharpCode.ILSpy
return; return;
if (recordHistory) { if (recordHistory) {
var dtState = decompilerTextView.GetState(); var dtState = DockWorkspace.Instance.GetState();
if (dtState != null) if (dtState != null)
history.UpdateCurrent(new NavigationState(dtState)); history.UpdateCurrent(new NavigationState(dtState));
history.Record(new NavigationState(treeView.SelectedItems.OfType<SharpTreeNode>())); history.Record(new NavigationState(treeView.SelectedItems.OfType<SharpTreeNode>()));
@ -955,10 +950,10 @@ namespace ICSharpCode.ILSpy
if (treeView.SelectedItems.Count == 1) { if (treeView.SelectedItems.Count == 1) {
ILSpyTreeNode node = treeView.SelectedItem as ILSpyTreeNode; ILSpyTreeNode node = treeView.SelectedItem as ILSpyTreeNode;
if (node != null && node.View(decompilerTextView)) if (node != null && node.View(DockWorkspace.Instance.GetTextView()))
return; 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) 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 Language CurrentLanguage => sessionSettings.FilterSettings.Language;
public LanguageVersion CurrentLanguageVersion => sessionSettings.FilterSettings.LanguageVersion; public LanguageVersion CurrentLanguageVersion => sessionSettings.FilterSettings.LanguageVersion;
@ -1029,7 +1020,7 @@ namespace ICSharpCode.ILSpy
void NavigateHistory(bool forward) void NavigateHistory(bool forward)
{ {
var dtState = decompilerTextView.GetState(); var dtState = DockWorkspace.Instance.GetState();
if (dtState != null) if (dtState != null)
history.UpdateCurrent(new NavigationState(dtState)); history.UpdateCurrent(new NavigationState(dtState));
var newState = forward ? history.GoForward() : history.GoBack(); var newState = forward ? history.GoForward() : history.GoBack();

2
ILSpy/TaskHelper.cs

@ -196,7 +196,7 @@ namespace ICSharpCode.ILSpy
task.Catch<Exception>(exception => MainWindow.Instance.Dispatcher.BeginInvoke(new Action(delegate { task.Catch<Exception>(exception => MainWindow.Instance.Dispatcher.BeginInvoke(new Action(delegate {
AvalonEditTextOutput output = new AvalonEditTextOutput(); AvalonEditTextOutput output = new AvalonEditTextOutput();
output.Write(exception.ToString()); output.Write(exception.ToString());
MainWindow.Instance.TextView.ShowText(output); Docking.DockWorkspace.Instance.ShowText(output);
}))).IgnoreExceptions(); }))).IgnoreExceptions();
} }
} }

12
ILSpy/TextView/DecompilerTextView.cs

@ -53,6 +53,7 @@ using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.AvalonEdit; using ICSharpCode.ILSpy.AvalonEdit;
using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.Options;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.ViewModels;
using Microsoft.Win32; using Microsoft.Win32;
namespace ICSharpCode.ILSpy.TextView namespace ICSharpCode.ILSpy.TextView
@ -61,7 +62,6 @@ namespace ICSharpCode.ILSpy.TextView
/// Manages the TextEditor showing the decompiled code. /// Manages the TextEditor showing the decompiled code.
/// Contains all the threading logic that makes the decompiler work in the background. /// Contains all the threading logic that makes the decompiler work in the background.
/// </summary> /// </summary>
[Export, PartCreationPolicy(CreationPolicy.Shared)]
public sealed partial class DecompilerTextView : UserControl, IDisposable public sealed partial class DecompilerTextView : UserControl, IDisposable
{ {
readonly ReferenceElementGenerator referenceElementGenerator; readonly ReferenceElementGenerator referenceElementGenerator;
@ -140,6 +140,8 @@ namespace ICSharpCode.ILSpy.TextView
// add marker service & margin // add marker service & margin
textEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService); textEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService);
textEditor.TextArea.TextView.LineTransformers.Add(textMarkerService); textEditor.TextArea.TextView.LineTransformers.Add(textMarkerService);
ContextMenuProvider.Add(this);
} }
void RemoveEditCommand(RoutedUICommand command) void RemoveEditCommand(RoutedUICommand command)
@ -1032,6 +1034,14 @@ namespace ICSharpCode.ILSpy.TextView
} }
} }
#endregion #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 public class DecompilerTextViewState

3
ILSpy/TextView/DecompilerTextView.xaml

@ -2,7 +2,8 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 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:properties="clr-namespace:ICSharpCode.ILSpy.Properties" xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:ae="clr-namespace:ICSharpCode.AvalonEdit;assembly=ICSharpCode.AvalonEdit"> xmlns:ae="clr-namespace:ICSharpCode.AvalonEdit;assembly=ICSharpCode.AvalonEdit"
DataContextChanged="self_DataContextChanged">
<UserControl.Resources> <UserControl.Resources>
<BooleanToVisibilityConverter x:Key="boolToVisibility" /> <BooleanToVisibilityConverter x:Key="boolToVisibility" />
</UserControl.Resources> </UserControl.Resources>

2
ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs

@ -65,7 +65,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
ISmartTextOutput smartOutput = output as ISmartTextOutput; ISmartTextOutput smartOutput = output as ISmartTextOutput;
if (smartOutput != null) { 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(); output.WriteLine();
} }
} }

38
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 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));
}
}
} }
} }
} }

41
ILSpy/ViewModels/PaneModel.cs

@ -1,10 +1,38 @@
using System.ComponentModel; using System;
using System.ComponentModel;
using System.Windows.Input; using System.Windows.Input;
namespace ICSharpCode.ILSpy.ViewModels namespace ICSharpCode.ILSpy.ViewModels
{ {
public abstract class PaneModel : INotifyPropertyChanged 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 abstract PanePosition DefaultPosition { get; }
public event PropertyChangedEventHandler PropertyChanged; 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; private string contentId;
public string ContentId { public string ContentId {
get => contentId; get => contentId;

Loading…
Cancel
Save