Browse Source

Make the main menu extensible using MEF.

pull/70/head
Daniel Grunwald 15 years ago
parent
commit
fe8f32bef0
  1. 13
      ILSpy/AboutPage.cs
  2. 24
      ILSpy/Commands.cs
  3. 30
      ILSpy/ExportCommandAttribute.cs
  4. 11
      ILSpy/MainWindow.xaml
  5. 79
      ILSpy/MainWindow.xaml.cs
  6. 5
      ILSpy/TextView/DecompilerTextView.cs

13
ILSpy/AboutPage.cs

@ -3,6 +3,7 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -14,13 +15,23 @@ using System.Windows.Data;
using System.Windows.Input; using System.Windows.Input;
using System.Xml; using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TextView;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
{ {
static class AboutPage [ExportMainMenuCommand(Menu = "_Help", Header = "_About", Order = 99999)]
sealed class AboutPage : SimpleCommand
{ {
[Import]
DecompilerTextView decompilerTextView = null;
public override void Execute(object parameter)
{
Display(decompilerTextView);
}
static readonly Uri UpdateUrl = new Uri("http://www.ilspy.net/updates.xml"); static readonly Uri UpdateUrl = new Uri("http://www.ilspy.net/updates.xml");
static AvailableVersionInfo latestAvailableVersion; static AvailableVersionInfo latestAvailableVersion;

24
ILSpy/Commands.cs

@ -6,6 +6,15 @@ using System.Windows.Input;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
{ {
[ExportMainMenuCommand(Menu = "_File", Header = "E_xit", Order = 99999, Category = "Exit")]
sealed class ExitCommand : SimpleCommand
{
public override void Execute(object parameter)
{
MainWindow.Instance.Close();
}
}
[ExportToolbarCommand(ToolTip = "Back", Icon = "Images/Back.png", Category = "Navigation")] [ExportToolbarCommand(ToolTip = "Back", Icon = "Images/Back.png", Category = "Navigation")]
sealed class BrowseBackCommand : CommandWrapper { sealed class BrowseBackCommand : CommandWrapper {
public BrowseBackCommand() : base(NavigationCommands.BrowseBack) {} public BrowseBackCommand() : base(NavigationCommands.BrowseBack) {}
@ -50,4 +59,19 @@ namespace ICSharpCode.ILSpy
return wrappedCommand.CanExecute(parameter); return wrappedCommand.CanExecute(parameter);
} }
} }
public abstract class SimpleCommand : ICommand
{
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public abstract void Execute(object parameter);
public virtual bool CanExecute(object parameter)
{
return true;
}
}
} }

30
ILSpy/ExportCommandAttribute.cs

@ -22,7 +22,7 @@ namespace ICSharpCode.ILSpy
public class ExportToolbarCommandAttribute : ExportAttribute public class ExportToolbarCommandAttribute : ExportAttribute
{ {
public ExportToolbarCommandAttribute() public ExportToolbarCommandAttribute()
: base(typeof(ICommand)) : base("ToolbarCommand", typeof(ICommand))
{ {
} }
@ -32,4 +32,32 @@ namespace ICSharpCode.ILSpy
public double Order { get; set; } public double Order { get; set; }
} }
#endregion #endregion
#region Main Menu
public interface IMainMenuCommandMetadata
{
string Icon { get; }
string Header { get; }
string Menu { get; }
string Category { get; }
double Order { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class ExportMainMenuCommandAttribute : ExportAttribute
{
public ExportMainMenuCommandAttribute()
: base("MainMenuCommand", typeof(ICommand))
{
}
public string Icon { get; set; }
public string Header { get; set; }
public string Menu { get; set; }
public string Category { get; set; }
public double Order { get; set; }
}
#endregion
} }

11
ILSpy/MainWindow.xaml

@ -33,7 +33,7 @@
</Window.CommandBindings> </Window.CommandBindings>
<DockPanel> <DockPanel>
<!-- Main menu --> <!-- Main menu -->
<Menu DockPanel.Dock="Top"> <Menu DockPanel.Dock="Top" Name="mainMenu">
<MenuItem Header="_File"> <MenuItem Header="_File">
<MenuItem Command="Open"> <MenuItem Command="Open">
<MenuItem.Icon> <MenuItem.Icon>
@ -52,8 +52,6 @@
<Image Width="16" Height="16" Source="Images/SaveFile.png" /> <Image Width="16" Height="16" Source="Images/SaveFile.png" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<Separator />
<MenuItem Header="E_xit" Click="ExitClick" />
</MenuItem> </MenuItem>
<MenuItem Header="_View"> <MenuItem Header="_View">
<MenuItem Header="Show _internal types and members" IsCheckable="True" IsChecked="{Binding FilterSettings.ShowInternalApi}"> <MenuItem Header="Show _internal types and members" IsCheckable="True" IsChecked="{Binding FilterSettings.ShowInternalApi}">
@ -63,9 +61,6 @@
</MenuItem> </MenuItem>
<MenuItem Header="Show _analyzer" Name="showAnalyzer" IsCheckable="True" Checked="ShowAnalyzer_Checked" Unchecked="ShowAnalyzer_Unchecked" /> <MenuItem Header="Show _analyzer" Name="showAnalyzer" IsCheckable="True" Checked="ShowAnalyzer_Checked" Unchecked="ShowAnalyzer_Unchecked" />
</MenuItem> </MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="_About" Click="AboutClick" />
</MenuItem>
</Menu> </Menu>
<!-- ToolBar --> <!-- ToolBar -->
<ToolBar <ToolBar
@ -135,7 +130,7 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BorderBrush="Transparent" /> BorderBrush="Transparent" />
<!-- Right pane: Text Editor --> <!-- Right pane: Text Editor -->
<Grid Grid.Column="2"> <Grid Grid.Column="2" Name="rightPane">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition <ColumnDefinition
Width="*" /> Width="*" />
@ -156,7 +151,7 @@
</DockPanel> </DockPanel>
</Border> </Border>
<textView:DecompilerTextView x:Name="decompilerTextView" Grid.Row="1" /> <!-- decompilerTextView is inserted into row 1 by code -->
<GridSplitter <GridSplitter
Grid.ZIndex="1" Grid.ZIndex="1"

79
ILSpy/MainWindow.xaml.cs

@ -29,8 +29,10 @@ using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.FlowAnalysis;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.TreeNodes.Analyzer; using ICSharpCode.ILSpy.TreeNodes.Analyzer;
using ICSharpCode.TreeView; using ICSharpCode.TreeView;
@ -51,6 +53,9 @@ namespace ICSharpCode.ILSpy
AssemblyList assemblyList; AssemblyList assemblyList;
AssemblyListTreeNode assemblyListTreeNode; AssemblyListTreeNode assemblyListTreeNode;
[Import]
DecompilerTextView decompilerTextView = null;
static MainWindow instance; static MainWindow instance;
public static MainWindow Instance { public static MainWindow Instance {
@ -78,7 +83,9 @@ namespace ICSharpCode.ILSpy
this.WindowState = sessionSettings.WindowState; this.WindowState = sessionSettings.WindowState;
InitializeComponent(); InitializeComponent();
decompilerTextView.mainWindow = this; App.CompositionContainer.ComposeParts(this);
Grid.SetRow(decompilerTextView, 1);
rightPane.Children.Add(decompilerTextView);
if (sessionSettings.SplitterPosition > 0 && sessionSettings.SplitterPosition < 1) { if (sessionSettings.SplitterPosition > 0 && sessionSettings.SplitterPosition < 1) {
leftColumn.Width = new GridLength(sessionSettings.SplitterPosition, GridUnitType.Star); leftColumn.Width = new GridLength(sessionSettings.SplitterPosition, GridUnitType.Star);
@ -86,28 +93,38 @@ namespace ICSharpCode.ILSpy
} }
sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged; sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged;
App.CompositionContainer.ComposeParts(this); InitMainMenu();
InitToolbar();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
#region Toolbar extensibility
[ImportMany("ToolbarCommand", typeof(ICommand))]
Lazy<ICommand, IToolbarCommandMetadata>[] toolbarCommands = null;
void InitToolbar()
{
int navigationPos = 0; int navigationPos = 0;
int openPos = 1; int openPos = 1;
foreach (var commandGroup in ToolbarCommands.GroupBy(c => c.Metadata.Category)) { foreach (var commandGroup in toolbarCommands.OrderBy(c => c.Metadata.Order).GroupBy(c => c.Metadata.Category)) {
if (commandGroup.Key == "Navigation") { if (commandGroup.Key == "Navigation") {
foreach (var command in commandGroup.OrderBy(c => c.Metadata.Order)) { foreach (var command in commandGroup) {
toolBar.Items.Insert(navigationPos++, MakeToolbarItem(command)); toolBar.Items.Insert(navigationPos++, MakeToolbarItem(command));
openPos++; openPos++;
} }
} else if (commandGroup.Key == "Open") { } else if (commandGroup.Key == "Open") {
foreach (var command in commandGroup.OrderBy(c => c.Metadata.Order)) { foreach (var command in commandGroup) {
toolBar.Items.Insert(openPos++, MakeToolbarItem(command)); toolBar.Items.Insert(openPos++, MakeToolbarItem(command));
} }
} else { } else {
toolBar.Items.Add(new Separator()); toolBar.Items.Add(new Separator());
foreach (var command in commandGroup.OrderBy(c => c.Metadata.Order)) { foreach (var command in commandGroup) {
toolBar.Items.Add(MakeToolbarItem(command)); toolBar.Items.Add(MakeToolbarItem(command));
} }
} }
} }
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
} }
Button MakeToolbarItem(Lazy<ICommand, IToolbarCommandMetadata> command) Button MakeToolbarItem(Lazy<ICommand, IToolbarCommandMetadata> command)
@ -122,9 +139,41 @@ namespace ICSharpCode.ILSpy
} }
}; };
} }
#endregion
[ImportMany] #region Main Menu extensibility
internal Lazy<ICommand, IToolbarCommandMetadata>[] ToolbarCommands { get; set; } [ImportMany("MainMenuCommand", typeof(ICommand))]
Lazy<ICommand, IMainMenuCommandMetadata>[] mainMenuCommands = null;
void InitMainMenu()
{
foreach (var topLevelMenu in mainMenuCommands.OrderBy(c => c.Metadata.Order).GroupBy(c => c.Metadata.Menu)) {
var topLevelMenuItem = mainMenu.Items.OfType<MenuItem>().FirstOrDefault(m => (m.Header as string) == topLevelMenu.Key);
foreach (var category in topLevelMenu.GroupBy(c => c.Metadata.Category)) {
if (topLevelMenuItem == null) {
topLevelMenuItem = new MenuItem();
topLevelMenuItem.Header = topLevelMenu.Key;
mainMenu.Items.Add(topLevelMenuItem);
} else {
topLevelMenuItem.Items.Add(new Separator());
}
foreach (var entry in category) {
MenuItem menuItem = new MenuItem();
menuItem.Header = entry.Metadata.Header;
menuItem.Command = entry.Value;
if (!string.IsNullOrEmpty(entry.Metadata.Icon)) {
menuItem.Icon = new Image {
Width = 16,
Height = 16,
Source = Images.LoadImage(entry.Value, entry.Metadata.Icon)
};
}
topLevelMenuItem.Items.Add(menuItem);
}
}
}
}
#endregion
void MainWindow_Loaded(object sender, RoutedEventArgs e) void MainWindow_Loaded(object sender, RoutedEventArgs e)
{ {
@ -375,18 +424,6 @@ namespace ICSharpCode.ILSpy
} }
#endregion #endregion
#region Exit/About
void ExitClick(object sender, RoutedEventArgs e)
{
Close();
}
void AboutClick(object sender, RoutedEventArgs e)
{
AboutPage.Display(decompilerTextView);
}
#endregion
#region Decompile / Save #region Decompile / Save
void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e) void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {

5
ILSpy/TextView/DecompilerTextView.cs

@ -18,6 +18,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@ -49,12 +50,12 @@ 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 public sealed partial class DecompilerTextView : UserControl
{ {
readonly ReferenceElementGenerator referenceElementGenerator; readonly ReferenceElementGenerator referenceElementGenerator;
readonly UIElementGenerator uiElementGenerator; readonly UIElementGenerator uiElementGenerator;
FoldingManager foldingManager; FoldingManager foldingManager;
internal MainWindow mainWindow;
DefinitionLookup definitionLookup; DefinitionLookup definitionLookup;
CancellationTokenSource currentCancellationTokenSource; CancellationTokenSource currentCancellationTokenSource;
@ -477,7 +478,7 @@ namespace ICSharpCode.ILSpy.TextView
return; return;
} }
} }
mainWindow.JumpToReference(reference); MainWindow.Instance.JumpToReference(reference);
} }
/// <summary> /// <summary>

Loading…
Cancel
Save