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. 14
      ILSpy/TextView/DecompilerTextView.cs
  16. 5
      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 @@ -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");

2
ILSpy/App.xaml.cs

@ -244,7 +244,7 @@ namespace ICSharpCode.ILSpy @@ -244,7 +244,7 @@ namespace ICSharpCode.ILSpy
}
}
}
ILSpy.MainWindow.Instance.TextView.ShowText(output);
Docking.DockWorkspace.Instance.ShowText(output);
e.Handled = true;
}
}

8
ILSpy/Commands/DecompileAllCommand.cs

@ -38,7 +38,7 @@ namespace ICSharpCode.ILSpy @@ -38,7 +38,7 @@ namespace ICSharpCode.ILSpy
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();
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 @@ -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 @@ -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<AvalonEditTextOutput>.Factory.StartNew(() => {
Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
options.CancellationToken = ct;
Stopwatch w = Stopwatch.StartNew();
for (int i = 0; i < numRuns; ++i) {
@ -90,7 +90,7 @@ namespace ICSharpCode.ILSpy @@ -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();
}
}
}

15
ILSpy/Commands/DecompileInNewViewCommand.cs

@ -16,6 +16,7 @@ @@ -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; @@ -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 @@ -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<ILSpyTreeNode>().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());
}));
}
}
}

4
ILSpy/Commands/DisassembleAllCommand.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.ILSpy @@ -35,7 +35,7 @@ namespace ICSharpCode.ILSpy
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();
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 @@ -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();
}
}
}

4
ILSpy/Commands/GeneratePdbContextMenuEntry.cs

@ -64,7 +64,7 @@ namespace ICSharpCode.ILSpy @@ -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<AvalonEditTextOutput>.Factory.StartNew(() => {
Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.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 @@ -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();
}
}

4
ILSpy/Commands/Pdb2XmlCommand.cs

@ -49,7 +49,7 @@ namespace ICSharpCode.ILSpy @@ -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<AvalonEditTextOutput>.Factory.StartNew(() => {
Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput();
var writer = new TextOutputWriter(output);
foreach (var node in nodes) {
@ -60,7 +60,7 @@ namespace ICSharpCode.ILSpy @@ -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();
}
}

2
ILSpy/Commands/SaveCodeContextMenuEntry.cs

@ -55,7 +55,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -55,7 +55,7 @@ namespace ICSharpCode.ILSpy.TextView
public static void Execute(IReadOnlyList<SharpTreeNode> 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

36
ILSpy/ContextMenuEntry.cs

@ -126,19 +126,22 @@ namespace ICSharpCode.ILSpy @@ -126,19 +126,22 @@ namespace ICSharpCode.ILSpy
/// <summary>
/// Enables extensible context menu support for the specified tree view.
/// </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;
// 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 @@ -157,16 +160,23 @@ namespace ICSharpCode.ILSpy
{
entries = App.ExportProvider.GetExports<IContextMenuEntry, IContextMenuEntryMetadata>().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)

4
ILSpy/DebugSteps.xaml.cs

@ -123,8 +123,8 @@ namespace ICSharpCode.ILSpy @@ -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,

32
ILSpy/Docking/DockWorkspace.cs

@ -1,5 +1,10 @@ @@ -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 @@ -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<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 @@ @@ -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 @@ @@ -58,8 +59,7 @@
<ContentPresenter x:Key="MainPane" />
<DataTemplate x:Key="DecompilerTextViewTemplate">
<!-- decompilerTextView is inserted into the mainPane by code -->
<ContentControl Content="{StaticResource MainPane}" />
<textview:DecompilerTextView DataContext="{Binding}" />
</DataTemplate>
</Window.Resources>
@ -165,300 +165,8 @@ @@ -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">
<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:LayoutPanel Orientation="Horizontal">
<avalondock:LayoutAnchorablePane DockMinWidth="150" Name="LeftPane" />
@ -482,7 +190,7 @@ @@ -482,7 +190,7 @@
<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: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>
</avalondock:DockingManager.LayoutItemTemplateSelector>
@ -490,12 +198,14 @@ @@ -490,12 +198,14 @@
<avalondock:DockingManager.LayoutItemContainerStyleSelector>
<docking:PaneStyleSelector>
<docking:PaneStyleSelector.ToolPaneStyle>
<Style TargetType="{x:Type avalondock:LayoutItem}">
<Style TargetType="{x:Type avalondock:LayoutAnchorableItem}">
<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="ContentId" Value="{Binding Model.ContentId}"/>
<Setter Property="IsSelected" Value="{Binding Model.IsSelected, 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}" />
</Style>
</docking:PaneStyleSelector.ToolPaneStyle>

31
ILSpy/MainWindow.xaml.cs

@ -73,8 +73,6 @@ namespace ICSharpCode.ILSpy @@ -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 @@ -126,16 +124,13 @@ namespace ICSharpCode.ILSpy
InitializeComponent();
decompilerTextView = App.ExportProvider.GetExportedValue<DecompilerTextView>();
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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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<SharpTreeNode>()));
@ -955,10 +950,10 @@ namespace ICSharpCode.ILSpy @@ -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 @@ -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 @@ -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();

2
ILSpy/TaskHelper.cs

@ -196,7 +196,7 @@ namespace ICSharpCode.ILSpy @@ -196,7 +196,7 @@ namespace ICSharpCode.ILSpy
task.Catch<Exception>(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();
}
}

14
ILSpy/TextView/DecompilerTextView.cs

@ -53,6 +53,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -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 @@ -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.
/// </summary>
[Export, PartCreationPolicy(CreationPolicy.Shared)]
public sealed partial class DecompilerTextView : UserControl, IDisposable
{
readonly ReferenceElementGenerator referenceElementGenerator;
@ -103,7 +103,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -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 @@ -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 @@ -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

5
ILSpy/TextView/DecompilerTextView.xaml

@ -1,8 +1,9 @@ @@ -1,8 +1,9 @@
<UserControl x:Class="ICSharpCode.ILSpy.TextView.DecompilerTextView" x:ClassModifier="public" x:Name="self"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:ae="clr-namespace:ICSharpCode.AvalonEdit;assembly=ICSharpCode.AvalonEdit">
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:ae="clr-namespace:ICSharpCode.AvalonEdit;assembly=ICSharpCode.AvalonEdit"
DataContextChanged="self_DataContextChanged">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="boolToVisibility" />
</UserControl.Resources>

2
ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs

@ -65,7 +65,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -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();
}
}

38
ILSpy/ViewModels/DocumentModel.cs

@ -1,14 +1,40 @@ @@ -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));
}
}
}
}
}

41
ILSpy/ViewModels/PaneModel.cs

@ -1,10 +1,38 @@ @@ -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 @@ -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;

Loading…
Cancel
Save