diff --git a/ILSpy/Commands/DecompileCommand.cs b/ILSpy/Commands/DecompileCommand.cs
index 0902c03e9..ff0a995df 100644
--- a/ILSpy/Commands/DecompileCommand.cs
+++ b/ILSpy/Commands/DecompileCommand.cs
@@ -16,15 +16,9 @@
// 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 System.Reflection;
-using System.Reflection.Metadata.Ecma335;
-using System.Windows.Controls;
-using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
-using ICSharpCode.ILSpy.Metadata;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TreeNodes;
@@ -73,41 +67,4 @@ namespace ICSharpCode.ILSpy.Commands
MainWindow.Instance.JumpToReference(selection);
}
}
-
- [ExportContextMenuEntry(Header = nameof(Resources.GoToToken), Order = 10)]
- class GoToToken : IContextMenuEntry
- {
- public void Execute(TextViewContext context)
- {
- int token = GetSelectedToken(context.DataGrid, out PEFile module).Value;
- MainWindow.Instance.JumpToReference(new EntityReference("metadata", module, MetadataTokens.Handle(token)));
- }
-
- public bool IsEnabled(TextViewContext context)
- {
- return true;
- }
-
- public bool IsVisible(TextViewContext context)
- {
- return context.DataGrid?.Name == "MetadataView" && GetSelectedToken(context.DataGrid, out _) != null;
- }
-
- private int? GetSelectedToken(DataGrid grid, out PEFile module)
- {
- module = null;
- if (grid == null)
- return null;
- var cell = grid.CurrentCell;
- if (!cell.IsValid)
- return null;
- Type type = cell.Item.GetType();
- var property = type.GetProperty(cell.Column.Header.ToString());
- var moduleField = type.GetField("module", BindingFlags.NonPublic | BindingFlags.Instance);
- if (property == null || property.PropertyType != typeof(int) || !property.GetCustomAttributes(false).Any(a => a is StringFormatAttribute sf && sf.Format == "X8"))
- return null;
- module = (PEFile)moduleField.GetValue(cell.Item);
- return (int)property.GetValue(cell.Item);
- }
- }
}
diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj
index f0e88dc0a..fbb27c280 100644
--- a/ILSpy/ILSpy.csproj
+++ b/ILSpy/ILSpy.csproj
@@ -118,6 +118,7 @@
+
@@ -173,7 +174,6 @@
Code
MSBuild:Compile
-
Code
MSBuild:Compile
@@ -209,6 +209,7 @@
+
diff --git a/ILSpy/Metadata/CoffHeaderTreeNode.cs b/ILSpy/Metadata/CoffHeaderTreeNode.cs
index 4a22313f4..906abc8d4 100644
--- a/ILSpy/Metadata/CoffHeaderTreeNode.cs
+++ b/ILSpy/Metadata/CoffHeaderTreeNode.cs
@@ -59,6 +59,7 @@ namespace ICSharpCode.ILSpy.Metadata
AutoGenerateColumns = false,
CanUserAddRows = false,
CanUserDeleteRows = false,
+ GridLinesVisibility = DataGridGridLinesVisibility.None,
RowDetailsTemplateSelector = new CharacteristicsDataTemplateSelector(),
RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Visible
};
diff --git a/ILSpy/Metadata/DataGridCustomTextColumn.cs b/ILSpy/Metadata/DataGridCustomTextColumn.cs
deleted file mode 100644
index b16dade3e..000000000
--- a/ILSpy/Metadata/DataGridCustomTextColumn.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of this
-// software and associated documentation files (the "Software"), to deal in the Software
-// without restriction, including without limitation the rights to use, copy, modify, merge,
-// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
-// to whom the Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all copies or
-// substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
-// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Input;
-using System.Windows.Media;
-
-namespace ICSharpCode.ILSpy.Metadata
-{
- class DataGridCustomTextColumn : DataGridTextColumn
- {
- public BindingBase ToolTipBinding { get; set; }
-
- protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
- {
- TextBox textBox = new TextBox() { Style = (Style)MetadataTableViews.Instance["DataGridCustomTextColumnTextBoxStyle"] };
- BindingOperations.SetBinding(textBox, TextBox.TextProperty, Binding);
- if (ToolTipBinding != null)
- {
- textBox.MouseMove += TextBox_MouseMove;
- }
- textBox.GotFocus += TextBox_GotFocus;
- return textBox;
- }
-
- private void TextBox_GotFocus(object sender, RoutedEventArgs e)
- {
- TextBox tb = (TextBox)sender;
- var cell = tb.GetParent();
- var row = cell.GetParent();
- row.IsSelected = cell.IsSelected = true;
- }
-
- private void TextBox_MouseMove(object sender, MouseEventArgs e)
- {
- e.Handled = true;
- var textBox = (TextBox)sender;
- BindingOperations.SetBinding(textBox, TextBox.ToolTipProperty, ToolTipBinding);
- textBox.MouseMove -= TextBox_MouseMove;
- }
- }
-}
diff --git a/ILSpy/Metadata/GoToTokenCommand.cs b/ILSpy/Metadata/GoToTokenCommand.cs
new file mode 100644
index 000000000..5bd70926a
--- /dev/null
+++ b/ILSpy/Metadata/GoToTokenCommand.cs
@@ -0,0 +1,101 @@
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Metadata.Ecma335;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+
+using ICSharpCode.Decompiler.Metadata;
+using ICSharpCode.ILSpy.Metadata;
+using ICSharpCode.ILSpy.Properties;
+
+namespace ICSharpCode.ILSpy.Commands
+{
+ [ExportContextMenuEntry(Header = nameof(Resources.GoToToken), Order = 10)]
+ class GoToTokenCommand : IContextMenuEntry
+ {
+ public void Execute(TextViewContext context)
+ {
+ int token = GetSelectedToken(context.DataGrid, out PEFile module).Value;
+ MainWindow.Instance.JumpToReference(new EntityReference("metadata", module, MetadataTokens.Handle(token)));
+ }
+
+ public bool IsEnabled(TextViewContext context)
+ {
+ return true;
+ }
+
+ public bool IsVisible(TextViewContext context)
+ {
+ return context.DataGrid?.Name == "MetadataView" && GetSelectedToken(context.DataGrid, out _) != null;
+ }
+
+ private int? GetSelectedToken(DataGrid grid, out PEFile module)
+ {
+ module = null;
+ if (grid == null)
+ return null;
+ var cell = grid.CurrentCell;
+ if (!cell.IsValid)
+ return null;
+ Type type = cell.Item.GetType();
+ var property = type.GetProperty(cell.Column.Header.ToString());
+ var moduleField = type.GetField("module", BindingFlags.NonPublic | BindingFlags.Instance);
+ if (property == null || property.PropertyType != typeof(int) || !property.GetCustomAttributes(false).Any(a => a is StringFormatAttribute sf && sf.Format == "X8"))
+ return null;
+ module = (PEFile)moduleField.GetValue(cell.Item);
+ return (int)property.GetValue(cell.Item);
+ }
+ }
+
+ [ExportContextMenuEntry(Header = nameof(Resources.Copy), Order = 10)]
+ class CopyCommand : IContextMenuEntry
+ {
+ public void Execute(TextViewContext context)
+ {
+ string content = GetSelectedCellContent(context.DataGrid, context.MousePosition);
+ Clipboard.SetText(content);
+ }
+
+ public bool IsEnabled(TextViewContext context)
+ {
+ return true;
+ }
+
+ public bool IsVisible(TextViewContext context)
+ {
+ return context.DataGrid?.Name == "MetadataView"
+ && GetSelectedCellContent(context.DataGrid, context.MousePosition) != null;
+ }
+
+ private string GetSelectedCellContent(DataGrid grid, Point position)
+ {
+ position = grid.PointFromScreen(position);
+ var hit = VisualTreeHelper.HitTest(grid, position);
+ if (hit == null)
+ return null;
+ var cell = hit.VisualHit.GetParent();
+ return (cell?.Content as TextBlock)?.Text;
+ }
+ }
+}
diff --git a/ILSpy/Metadata/Helpers.cs b/ILSpy/Metadata/Helpers.cs
index cc60901db..00db21388 100644
--- a/ILSpy/Metadata/Helpers.cs
+++ b/ILSpy/Metadata/Helpers.cs
@@ -57,21 +57,15 @@ namespace ICSharpCode.ILSpy.Metadata
RowHeaderWidth = 0,
EnableColumnVirtualization = true,
EnableRowVirtualization = true,
+ RowHeight = 20,
IsReadOnly = true,
SelectionMode = DataGridSelectionMode.Single,
- SelectionUnit = DataGridSelectionUnit.CellOrRowHeader,
+ SelectionUnit = DataGridSelectionUnit.FullRow,
SelectedTreeNode = selectedNode,
- CellStyle = new Style {
- Setters = {
- new Setter {
- Property = Control.BorderThicknessProperty,
- Value = new Thickness(0)
- }
- }
- }
+ VerticalContentAlignment = VerticalAlignment.Center,
+ CellStyle = (Style)MetadataTableViews.Instance["DataGridCellStyle"]
};
ContextMenuProvider.Add(view);
- ScrollViewer.SetIsDeferredScrollingEnabled(view, true);
DataGridFilter.SetIsAutoFilterEnabled(view, true);
DataGridFilter.SetContentFilterFactory(view, new RegexContentFilterFactory());
}
@@ -88,18 +82,6 @@ namespace ICSharpCode.ILSpy.Metadata
return view;
}
- class MetaDataGrid : DataGrid, IHaveState
- {
- public ILSpyTreeNode SelectedTreeNode { get; set; }
-
- public ViewState GetState()
- {
- return new ViewState {
- DecompiledNodes = SelectedTreeNode == null ? null : new HashSet(new[] { SelectedTreeNode })
- };
- }
- }
-
private static void View_AutoGeneratedColumns(object sender, EventArgs e)
{
((DataGrid)sender).AutoGeneratedColumns -= View_AutoGeneratedColumns;
@@ -109,24 +91,21 @@ namespace ICSharpCode.ILSpy.Metadata
private static void View_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var binding = new Binding(e.PropertyName) { Mode = BindingMode.OneWay };
- e.Column = new DataGridCustomTextColumn() {
+ e.Column = new DataGridTextColumn() {
Header = e.PropertyName,
- Binding = binding,
- ToolTipBinding = new Binding(e.PropertyName + "Tooltip") { Mode = BindingMode.OneWay }
+ Binding = binding
};
switch (e.PropertyName)
{
case "RID":
case "Meaning":
e.Column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["DefaultFilter"]);
- ((DataGridCustomTextColumn)e.Column).ToolTipBinding = null;
break;
case "Token":
case "Offset":
case "RVA":
binding.StringFormat = "X8";
e.Column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["HexFilter"]);
- ((DataGridCustomTextColumn)e.Column).ToolTipBinding = null;
break;
default:
e.Cancel = e.PropertyName.Contains("Tooltip");
@@ -147,13 +126,15 @@ namespace ICSharpCode.ILSpy.Metadata
if (descriptor.PropertyType.IsEnum)
{
binding.Converter = new UnderlyingEnumValueConverter();
- column.SetTemplate((ControlTemplate)MetadataTableViews.Instance[descriptor.PropertyType.Name + "Filter"]);
+ string key = descriptor.PropertyType.Name + "Filter";
+ column.SetTemplate((ControlTemplate)MetadataTableViews.Instance[key]);
}
var stringFormat = descriptor.Attributes.OfType().FirstOrDefault();
if (stringFormat != null)
{
binding.StringFormat = stringFormat.Format;
- if (!descriptor.PropertyType.IsEnum && stringFormat.Format.StartsWith("X", StringComparison.OrdinalIgnoreCase))
+ if (!descriptor.PropertyType.IsEnum
+ && stringFormat.Format.StartsWith("X", StringComparison.OrdinalIgnoreCase))
{
column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["HexFilter"]);
}
@@ -210,9 +191,12 @@ namespace ICSharpCode.ILSpy.Metadata
return (EntityHandle)fromTypeDefOrRefTag.Invoke(null, new object[] { tag });
}
- public static int ComputeCodedTokenSize(this MetadataReader metadata, int largeRowSize, TableMask mask)
+ public static int ComputeCodedTokenSize(this MetadataReader metadata, int largeRowSize,
+ TableMask mask)
{
- return (int)computeCodedTokenSize.Invoke(metadata, new object[] { largeRowSize, rowCounts.GetValue(metadata), (ulong)mask });
+ return (int)computeCodedTokenSize.Invoke(metadata, new object[] {
+ largeRowSize, rowCounts.GetValue(metadata), (ulong)mask }
+ );
}
class UnderlyingEnumValueConverter : IValueConverter
diff --git a/ILSpy/Metadata/MetaDataGrid.cs b/ILSpy/Metadata/MetaDataGrid.cs
new file mode 100644
index 000000000..a87b065e3
--- /dev/null
+++ b/ILSpy/Metadata/MetaDataGrid.cs
@@ -0,0 +1,119 @@
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+using ICSharpCode.AvalonEdit.Rendering;
+using ICSharpCode.ILSpy.TextView;
+using ICSharpCode.ILSpy.TreeNodes;
+using ICSharpCode.ILSpy.ViewModels;
+
+namespace ICSharpCode.ILSpy.Metadata
+{
+ class MetaDataGrid : DataGrid, IHaveState
+ {
+ private readonly MouseHoverLogic hoverLogic;
+ private ToolTip toolTip;
+
+ public ILSpyTreeNode SelectedTreeNode { get; set; }
+
+ public MetaDataGrid()
+ {
+ this.hoverLogic = new MouseHoverLogic(this);
+ this.hoverLogic.MouseHover += HoverLogic_MouseHover;
+ this.hoverLogic.MouseHoverStopped += HoverLogic_MouseHoverStopped;
+ }
+
+ private void HoverLogic_MouseHoverStopped(object sender, System.Windows.Input.MouseEventArgs e)
+ {
+ // Non-popup tooltips get closed as soon as the mouse starts moving again
+ if (toolTip != null)
+ {
+ toolTip.IsOpen = false;
+ e.Handled = true;
+ }
+ }
+
+ private void HoverLogic_MouseHover(object sender, System.Windows.Input.MouseEventArgs e)
+ {
+ var position = e.GetPosition(this);
+ var hit = VisualTreeHelper.HitTest(this, position);
+ if (hit == null)
+ {
+ return;
+ }
+ var cell = hit.VisualHit.GetParent();
+ if (cell == null)
+ return;
+ var data = cell.DataContext;
+ var name = (string)cell.Column.Header;
+ if (toolTip == null)
+ {
+ toolTip = new ToolTip();
+ toolTip.Closed += ToolTipClosed;
+ }
+ toolTip.PlacementTarget = this; // required for property inheritance
+
+ var pi = data?.GetType().GetProperty(name + "Tooltip");
+ if (pi == null)
+ return;
+ object tooltip = pi.GetValue(data);
+ if (tooltip is string s)
+ {
+ if (string.IsNullOrWhiteSpace(s))
+ return;
+ toolTip.Content = new TextBlock {
+ Text = s,
+ TextWrapping = TextWrapping.Wrap
+ };
+ }
+ else if (tooltip != null)
+ {
+ toolTip.Content = tooltip;
+ }
+ else
+ {
+ return;
+ }
+
+ e.Handled = true;
+ toolTip.IsOpen = true;
+ }
+
+ private void ToolTipClosed(object sender, RoutedEventArgs e)
+ {
+ if (toolTip == sender)
+ {
+ toolTip = null;
+ }
+ }
+
+ public ViewState GetState()
+ {
+ return new ViewState {
+ DecompiledNodes = SelectedTreeNode == null
+ ? null
+ : new HashSet(new[] { SelectedTreeNode })
+ };
+ }
+ }
+}
diff --git a/ILSpy/Metadata/MetadataTableViews.xaml b/ILSpy/Metadata/MetadataTableViews.xaml
index a0b52ecf0..0802dc5fe 100644
--- a/ILSpy/Metadata/MetadataTableViews.xaml
+++ b/ILSpy/Metadata/MetadataTableViews.xaml
@@ -8,30 +8,31 @@
xmlns:srm="clr-namespace:System.Reflection;assembly=System.Reflection.Metadata"
xmlns:dgx="urn:tom-englert.de/DataGridExtensions">
-
-
+
diff --git a/ILSpy/Metadata/OptionalHeaderTreeNode.cs b/ILSpy/Metadata/OptionalHeaderTreeNode.cs
index 6aad4c1c1..423924dbb 100644
--- a/ILSpy/Metadata/OptionalHeaderTreeNode.cs
+++ b/ILSpy/Metadata/OptionalHeaderTreeNode.cs
@@ -51,11 +51,11 @@ namespace ICSharpCode.ILSpy.Metadata
dataGrid.RowDetailsTemplateSelector = new DllCharacteristicsDataTemplateSelector();
dataGrid.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Visible;
dataGrid.AutoGenerateColumns = false;
- dataGrid.Columns.Add(new DataGridCustomTextColumn { Header = "Member", Binding = new Binding("Member") { Mode = BindingMode.OneWay } });
- dataGrid.Columns.Add(new DataGridCustomTextColumn { Header = "Offset", Binding = new Binding("Offset") { StringFormat = "X8", Mode = BindingMode.OneWay } });
- dataGrid.Columns.Add(new DataGridCustomTextColumn { Header = "Size", Binding = new Binding("Size") { Mode = BindingMode.OneWay } });
- dataGrid.Columns.Add(new DataGridCustomTextColumn { Header = "Value", Binding = new Binding(".") { Converter = ByteWidthConverter.Instance, Mode = BindingMode.OneWay } });
- dataGrid.Columns.Add(new DataGridCustomTextColumn { Header = "Meaning", Binding = new Binding("Meaning") { Mode = BindingMode.OneWay } });
+ dataGrid.Columns.Add(new DataGridTextColumn { Header = "Member", Binding = new Binding("Member") { Mode = BindingMode.OneWay } });
+ dataGrid.Columns.Add(new DataGridTextColumn { Header = "Offset", Binding = new Binding("Offset") { StringFormat = "X8", Mode = BindingMode.OneWay } });
+ dataGrid.Columns.Add(new DataGridTextColumn { Header = "Size", Binding = new Binding("Size") { Mode = BindingMode.OneWay } });
+ dataGrid.Columns.Add(new DataGridTextColumn { Header = "Value", Binding = new Binding(".") { Converter = ByteWidthConverter.Instance, Mode = BindingMode.OneWay } });
+ dataGrid.Columns.Add(new DataGridTextColumn { Header = "Meaning", Binding = new Binding("Meaning") { Mode = BindingMode.OneWay } });
var headers = module.Reader.PEHeaders;
var reader = module.Reader.GetEntireImage().GetReader(headers.PEHeaderStartOffset, 128);