Browse Source

Merge pull request #3325 from tom-englert/dev/WpfRefactoring

Dev/wpf refactoring
pull/3335/head
Christoph Wille 6 months ago committed by GitHub
parent
commit
b4d85a557c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 72
      .editorconfig
  2. 5
      Directory.Build.props
  3. 1
      ILSpy.sln
  4. 7
      ILSpy/AboutPage.cs
  5. 57
      ILSpy/Controls/MainMenu.xaml
  6. 115
      ILSpy/Controls/MainMenu.xaml.cs
  7. 87
      ILSpy/Controls/MainToolBar.xaml
  8. 136
      ILSpy/Controls/MainToolBar.xaml.cs
  9. 122
      ILSpy/MainWindow.xaml
  10. 27
      ILSpy/MainWindow.xaml.cs

72
.editorconfig

@ -5,6 +5,8 @@ root = true @@ -5,6 +5,8 @@ root = true
indent_style = tab
indent_size = 4
guidelines = 110
tab_width = 4
end_of_line = crlf
[*.il]
indent_style = space
@ -44,7 +46,7 @@ csharp_indent_braces = false @@ -44,7 +46,7 @@ csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = false
csharp_indent_switch_labels = true
csharp_indent_labels = one_less
csharp_indent_labels = no_change
# Avoid 'this.' in generated code unless absolutely necessary, but allow developers to use it
dotnet_style_qualification_for_field = false:silent
@ -54,7 +56,7 @@ dotnet_style_qualification_for_event = false:silent @@ -54,7 +56,7 @@ dotnet_style_qualification_for_event = false:silent
# Do not use 'var' when generating code, but allow developers to use it
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = false:silent
# Use language keywords instead of BCL types when generating code, but allow developers to use either
@ -143,6 +145,72 @@ dotnet_naming_rule.private_fields_rule.severity = warning @@ -143,6 +145,72 @@ dotnet_naming_rule.private_fields_rule.severity = warning
dotnet_naming_rule.private_fields_rule.style = lower_camel_case_style
dotnet_naming_rule.private_fields_rule.symbols = private_fields_symbols
# General settings:
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_prefer_system_threading_lock = true:suggestion
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_prefer_static_local_function = true:suggestion
csharp_prefer_static_anonymous_function = true:suggestion
csharp_style_prefer_readonly_struct = true:suggestion
csharp_style_prefer_readonly_struct_member = true:suggestion
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
csharp_style_prefer_switch_expression = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_prefer_extended_property_pattern = true:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
dotnet_style_namespace_match_folder = true:suggestion
dotnet_style_readonly_field = true:suggestion
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
dotnet_style_require_accessibility_modifiers = omit_if_default:suggestion
dotnet_style_allow_multiple_blank_lines_experimental = false:warning
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
dotnet_code_quality_unused_parameters = all:suggestion
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_event = false:silent
# Errors and warnings
# MEF006: No importing constructor

5
Directory.Build.props

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<WarningsAsErrors>IDE2000</WarningsAsErrors>
</PropertyGroup>
</Project>

1
ILSpy.sln

@ -39,6 +39,7 @@ EndProject @@ -39,6 +39,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D0858E90-DCD5-4FD9-AA53-7262FAB8BEDB}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
EndProjectSection
EndProject

7
ILSpy/AboutPage.cs

@ -26,6 +26,7 @@ using System.Windows.Controls; @@ -26,6 +26,7 @@ using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Navigation;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.Decompiler;
@ -53,7 +54,7 @@ namespace ICSharpCode.ILSpy @@ -53,7 +54,7 @@ namespace ICSharpCode.ILSpy
public override void Execute(object parameter)
{
MessageBus.Send(this, new NavigateToEventArgs(new(new("resource://aboutpage"), null), inNewTabPage: true));
MessageBus.Send(this, new NavigateToEventArgs(new RequestNavigateEventArgs(new Uri("resource://aboutpage"), null), inNewTabPage: true));
}
private void ShowAboutPage(TabPageModel tabPage)
@ -74,7 +75,7 @@ namespace ICSharpCode.ILSpy @@ -74,7 +75,7 @@ namespace ICSharpCode.ILSpy
output.AddUIElement(
delegate {
StackPanel stackPanel = new() {
var stackPanel = new StackPanel {
HorizontalAlignment = HorizontalAlignment.Center,
Orientation = Orientation.Horizontal
};
@ -87,7 +88,7 @@ namespace ICSharpCode.ILSpy @@ -87,7 +88,7 @@ namespace ICSharpCode.ILSpy
// we already retrieved the latest version sometime earlier
ShowAvailableVersion(UpdateService.LatestAvailableVersion, stackPanel);
}
CheckBox checkBox = new() {
var checkBox = new CheckBox {
Margin = new Thickness(4),
Content = Resources.AutomaticallyCheckUpdatesEveryWeek
};

57
ILSpy/Controls/MainMenu.xaml

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
<UserControl x:Class="ICSharpCode.ILSpy.Controls.MainMenu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ICSharpCode.ILSpy.Controls"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes"
xmlns:composition="urn:TomsToolbox.Composition"
xmlns:toms="urn:TomsToolbox"
xmlns:commands="clr-namespace:ICSharpCode.ILSpy.Commands"
mc:Ignorable="d" d:DesignWidth="800" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Menu DockPanel.Dock="Top" Name="Menu" Height="23" KeyboardNavigation.TabNavigation="None">
<MenuItem Header="{x:Static properties:Resources._File}" Tag="_File">
<!-- content of file menu is added using MEF -->
</MenuItem>
<MenuItem Header="{x:Static properties:Resources._View}" Tag="_View">
<MenuItem Header="{x:Static properties:Resources.Show_publiconlyTypesMembers}" IsCheckable="True"
IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisPublicOnly}" />
<MenuItem Header="{x:Static properties:Resources.Show_internalTypesMembers}" IsCheckable="True"
IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisPublicAndInternal}" />
<MenuItem Header="{x:Static properties:Resources.Show_allTypesAndMembers}" IsCheckable="True"
IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisAll}" />
<Separator />
<MenuItem Header="{x:Static properties:Resources.Theme}" ItemsSource="{x:Static themes:ThemeManager.AllThemes}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Command" Value="{composition:Import commands:SetThemeCommand}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="IsCheckable" Value="True" />
<!-- Required by AvalonDock's MenuItem style to show the checkmark -->
<Setter Property="IsChecked">
<Setter.Value>
<MultiBinding Converter="{x:Static toms:BinaryOperationConverter.Equality}" Mode="OneWay">
<Binding />
<Binding Path="SessionSettings.Theme"
RelativeSource="{RelativeSource FindAncestor, AncestorType=UserControl}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
<MenuItem Header="{x:Static properties:Resources.UILanguage}">
<MenuItem Header="{x:Static properties:Resources.UILanguage_System}" IsCheckable="True"
IsChecked="{Binding SessionSettings.CurrentCulture, Converter={local:CultureSelectionConverter}, ConverterParameter={x:Null}}" />
<MenuItem Header="English" IsCheckable="True"
IsChecked="{Binding SessionSettings.CurrentCulture, Converter={local:CultureSelectionConverter}, ConverterParameter=en-US}" />
<MenuItem Header="中文" IsCheckable="True"
IsChecked="{Binding SessionSettings.CurrentCulture, Converter={local:CultureSelectionConverter}, ConverterParameter=zh-Hans}" />
</MenuItem>
</MenuItem>
<MenuItem x:Name="WindowMenuItem" Header="{x:Static properties:Resources._Window}" Tag="_Window">
<!-- content of window menu is added using MEF -->
</MenuItem>
</Menu>
</UserControl>

115
ILSpy/Util/MenuService.cs → ILSpy/Controls/MainMenu.xaml.cs

@ -22,33 +22,43 @@ using System.Globalization; @@ -22,33 +22,43 @@ using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using ICSharpCode.ILSpy.Commands;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Themes;
using ICSharpCode.ILSpy.ViewModels;
using TomsToolbox.Composition;
using TomsToolbox.ObservableCollections;
using TomsToolbox.Wpf;
using TomsToolbox.Wpf.Converters;
namespace ICSharpCode.ILSpy.Util
namespace ICSharpCode.ILSpy.Controls
{
/// <summary>
/// Interaction logic for MainMenu.xaml
/// </summary>
[Export]
[Shared]
public class MenuService(IExportProvider exportProvider, DockWorkspace dockWorkspace)
[NonShared]
public partial class MainMenu
{
public void Init(Menu mainMenu, ToolBar toolBar, InputBindingCollection inputBindings)
public MainMenu(SettingsService settingsService, IExportProvider exportProvider, DockWorkspace dockWorkspace)
{
InitMainMenu(mainMenu);
InitWindowMenu(mainMenu, inputBindings);
InitToolbar(toolBar);
SessionSettings = settingsService.SessionSettings;
InitializeComponent();
this.BeginInvoke(() => {
InitMainMenu(Menu, exportProvider);
InitWindowMenu(WindowMenuItem, Window.GetWindow(this)!.InputBindings, dockWorkspace);
});
}
void InitMainMenu(Menu mainMenu)
public SessionSettings SessionSettings { get; }
static void InitMainMenu(Menu mainMenu, IExportProvider exportProvider)
{
var mainMenuCommands = exportProvider.GetExports<ICommand, IMainMenuCommandMetadata>("MainMenuCommand");
// Start by constructing the individual flat menus
@ -106,12 +116,9 @@ namespace ICSharpCode.ILSpy.Util @@ -106,12 +116,9 @@ namespace ICSharpCode.ILSpy.Util
}
}
foreach (var item in parentMenuItems.Values)
foreach (var item in parentMenuItems.Values.Where(item => item.Parent == null))
{
if (item.Parent == null)
{
mainMenu.Items.Add(item);
}
mainMenu.Items.Add(item);
}
MenuItem GetOrAddParentMenuItem(string menuId, string resourceKey)
@ -137,59 +144,21 @@ namespace ICSharpCode.ILSpy.Util @@ -137,59 +144,21 @@ namespace ICSharpCode.ILSpy.Util
}
}
void InitWindowMenu(Menu mainMenu, InputBindingCollection inputBindings)
static void InitWindowMenu(MenuItem windowMenuItem, InputBindingCollection inputBindings, DockWorkspace dockWorkspace)
{
var windowMenuItem = mainMenu.Items.OfType<MenuItem>().First(m => (string)m.Tag == nameof(Properties.Resources._Window));
var defaultItems = windowMenuItem.Items.Cast<Control>().ToArray();
windowMenuItem.Items.Clear();
var toolItems = dockWorkspace.ToolPanes.Select(toolPane => CreateMenuItem(toolPane, inputBindings)).ToArray();
var tabItems = dockWorkspace.TabPages.ObservableSelect(tabPage => CreateMenuItem(tabPage));
var toolItems = dockWorkspace.ToolPanes.Select(toolPane => CreateMenuItem(toolPane, inputBindings, dockWorkspace)).ToArray();
var tabItems = dockWorkspace.TabPages.ObservableSelect(tabPage => CreateMenuItem(tabPage, dockWorkspace));
var allItems = new ObservableCompositeCollection<Control>(defaultItems, [new Separator()], toolItems, [new Separator()], tabItems);
windowMenuItem.ItemsSource = allItems;
}
void InitToolbar(ToolBar toolBar)
{
int navigationPos = 0;
int openPos = 1;
var toolbarCommandsByCategory = exportProvider.GetExports<ICommand, IToolbarCommandMetadata>("ToolbarCommand")
.OrderBy(c => c.Metadata?.ToolbarOrder)
.GroupBy(c => c.Metadata?.ToolbarCategory);
foreach (var commandCategory in toolbarCommandsByCategory)
{
if (commandCategory.Key == nameof(Properties.Resources.Navigation))
{
foreach (var command in commandCategory)
{
toolBar.Items.Insert(navigationPos++, CreateToolbarItem(command));
openPos++;
}
}
else if (commandCategory.Key == nameof(Properties.Resources.Open))
{
foreach (var command in commandCategory)
{
toolBar.Items.Insert(openPos++, CreateToolbarItem(command));
}
}
else
{
toolBar.Items.Add(new Separator());
foreach (var command in commandCategory)
{
toolBar.Items.Add(CreateToolbarItem(command));
}
}
}
}
Control CreateMenuItem(TabPageModel pane)
static Control CreateMenuItem(TabPageModel pane, DockWorkspace dockWorkspace)
{
var header = new TextBlock {
MaxWidth = 200,
@ -200,7 +169,7 @@ namespace ICSharpCode.ILSpy.Util @@ -200,7 +169,7 @@ namespace ICSharpCode.ILSpy.Util
Source = pane
});
MenuItem menuItem = new() {
var menuItem = new MenuItem {
Command = new TabPageCommand(pane, dockWorkspace),
Header = header,
IsCheckable = true
@ -216,16 +185,16 @@ namespace ICSharpCode.ILSpy.Util @@ -216,16 +185,16 @@ namespace ICSharpCode.ILSpy.Util
return menuItem;
}
Control CreateMenuItem(ToolPaneModel pane, InputBindingCollection inputBindings)
static Control CreateMenuItem(ToolPaneModel pane, InputBindingCollection inputBindings, DockWorkspace dockWorkspace)
{
MenuItem menuItem = new() {
var menuItem = new MenuItem {
Command = pane.AssociatedCommand ?? new ToolPaneCommand(pane.ContentId, dockWorkspace),
Header = pane.Title
};
var shortcutKey = pane.ShortcutKey;
if (shortcutKey != null)
{
inputBindings.Add(new(menuItem.Command, shortcutKey));
inputBindings.Add(new InputBinding(menuItem.Command, shortcutKey));
menuItem.InputGestureText = shortcutKey.GetDisplayStringForCulture(CultureInfo.CurrentUICulture);
}
if (!string.IsNullOrEmpty(pane.Icon))
@ -239,29 +208,5 @@ namespace ICSharpCode.ILSpy.Util @@ -239,29 +208,5 @@ namespace ICSharpCode.ILSpy.Util
return menuItem;
}
static Button CreateToolbarItem(IExport<ICommand, IToolbarCommandMetadata> commandExport)
{
var command = commandExport.Value;
Button toolbarItem = new() {
Style = ThemeManager.Current.CreateToolBarButtonStyle(),
Command = CommandWrapper.Unwrap(command),
ToolTip = Properties.Resources.ResourceManager.GetString(commandExport.Metadata?.ToolTip ?? string.Empty),
Tag = commandExport.Metadata?.Tag,
Content = new Image {
Width = 16,
Height = 16,
Source = Images.Load(command, commandExport.Metadata?.ToolbarIcon)
}
};
if (command is IProvideParameterBinding parameterBinding)
{
BindingOperations.SetBinding(toolbarItem, ButtonBase.CommandParameterProperty, parameterBinding.ParameterBinding);
}
return toolbarItem;
}
}
}

87
ILSpy/Controls/MainToolBar.xaml

@ -0,0 +1,87 @@ @@ -0,0 +1,87 @@
<UserControl x:Class="ICSharpCode.ILSpy.Controls.MainToolBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:toms="urn:TomsToolbox"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:controls="clr-namespace:ICSharpCode.ILSpy.Controls"
xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes"
xmlns:composition="urn:TomsToolbox.Composition"
xmlns:ilSpy="clr-namespace:ICSharpCode.ILSpy"
mc:Ignorable="d" d:DesignWidth="800">
<ToolBar Name="ToolBar"
DockPanel.Dock="Top" ToolBarTray.IsLocked="True" KeyboardNavigation.TabNavigation="None">
<ToolBar.Resources>
<!-- Make images transparent if menu command is disabled -->
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ButtonBase}, AncestorLevel=1}, Path=IsEnabled}" Value="False">
<Setter Property="Opacity" Value="0.30" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Image}" x:Key="DarkModeAwareImageStyle">
<Setter Property="Effect" Value="{DynamicResource {x:Static themes:ResourceKeys.ThemeAwareButtonEffect}}" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ButtonBase}, AncestorLevel=1}, Path=IsEnabled}" Value="False">
<Setter Property="Opacity" Value="0.30" />
</DataTrigger>
</Style.Triggers>
</Style>
</ToolBar.Resources>
<!-- 'Navigation' toolbar category is inserted here -->
<Separator />
<!-- 'Open' toolbar category is inserted here -->
<Separator />
<Grid Margin="2,0">
<ItemsControl ItemsSource="{Binding AssemblyListManager.AssemblyLists}" Height="0" Margin="15,0" />
<ComboBox Name="assemblyListComboBox" MaxDropDownHeight="Auto"
ItemsSource="{Binding AssemblyListManager.AssemblyLists}"
ToolTip="{x:Static properties:Resources.SelectAssemblyListDropdownTooltip}"
SelectedItem="{Binding SessionSettings.ActiveAssemblyList}" />
</Grid>
<Button Command="{composition:Import ilSpy:ManageAssemblyListsCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
ToolTip="{x:Static properties:Resources.ManageAssemblyLists}">
<Image Width="16" Height="16" Source="{controls:XamlResource Images/AssemblyList}"
Style="{StaticResource DarkModeAwareImageStyle}" />
</Button>
<Separator />
<CheckBox IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisPublicOnly}"
ToolTip="{x:Static properties:Resources.ShowPublicOnlyTypesMembers}">
<Image Width="16" Height="16" Source="{controls:XamlResource Images/ShowPublicOnly}"
Style="{StaticResource DarkModeAwareImageStyle}" />
</CheckBox>
<CheckBox IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisPublicAndInternal}"
ToolTip="{x:Static properties:Resources.ShowInternalTypesMembers}">
<Image Width="16" Height="16" Source="{controls:XamlResource Images/ShowPrivateInternal}"
Style="{StaticResource DarkModeAwareImageStyle}" />
</CheckBox>
<CheckBox IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisAll}"
ToolTip="{x:Static properties:Resources.ShowAllTypesAndMembers}">
<Image Width="16" Height="16" Source="{controls:XamlResource Images/ShowAll}"
Style="{StaticResource DarkModeAwareImageStyle}" />
</CheckBox>
<Separator />
<Grid Margin="2,0">
<ItemsControl ItemsSource="{Binding LanguageService.AllLanguages}" DisplayMemberPath="Name" Height="0" Margin="15,0" />
<ComboBox Name="languageComboBox" DisplayMemberPath="Name" MaxDropDownHeight="Auto"
IsEnabled="{Binding Workspace.ActiveTabPage.SupportsLanguageSwitching}"
ItemsSource="{Binding LanguageService.AllLanguages}"
ToolTip="{x:Static properties:Resources.SelectLanguageDropdownTooltip}"
SelectedItem="{Binding LanguageService.Language}" />
</Grid>
<Grid Margin="2,0">
<ItemsControl
ItemsSource="{Binding SelectedItem.LanguageVersions, ElementName=languageComboBox, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="DisplayName" Height="0" Margin="15,0" />
<ComboBox Name="languageVersionComboBox" DisplayMemberPath="DisplayName" MaxDropDownHeight="Auto"
ToolTip="{x:Static properties:Resources.SelectVersionDropdownTooltip}"
Visibility="{Binding SelectedItem.HasLanguageVersions, ElementName=languageComboBox, Converter={toms:BooleanToVisibilityConverter}}"
IsEnabled="{Binding Workspace.ActiveTabPage.SupportsLanguageSwitching}"
ItemsSource="{Binding SelectedItem.LanguageVersions, ElementName=languageComboBox, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding LanguageService.LanguageVersion, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</ToolBar>
</UserControl>

136
ILSpy/Controls/MainToolBar.xaml.cs

@ -0,0 +1,136 @@ @@ -0,0 +1,136 @@
// Copyright (c) 2024 Tom Englert 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.Composition;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;
using ICSharpCode.ILSpy.Themes;
using TomsToolbox.Composition;
namespace ICSharpCode.ILSpy.Controls
{
/// <summary>
/// Interaction logic for MainToolBar.xaml
/// </summary>
[Export]
[NonShared]
public partial class MainToolBar
{
public MainToolBar(IExportProvider exportProvider)
{
InitializeComponent();
this.Dispatcher.BeginInvoke(DispatcherPriority.Background, () => {
InitToolbar(ToolBar, exportProvider);
Window.GetWindow(this)!.KeyDown += MainWindow_KeyDown;
});
}
static void InitToolbar(ToolBar toolBar, IExportProvider exportProvider)
{
int navigationPos = 0;
int openPos = 1;
var toolbarCommandsByCategory = exportProvider
.GetExports<ICommand, IToolbarCommandMetadata>("ToolbarCommand")
.OrderBy(c => c.Metadata?.ToolbarOrder)
.GroupBy(c => c.Metadata?.ToolbarCategory);
foreach (var commandCategory in toolbarCommandsByCategory)
{
if (commandCategory.Key == nameof(Properties.Resources.Navigation))
{
foreach (var command in commandCategory)
{
toolBar.Items.Insert(navigationPos++, CreateToolbarItem(command));
openPos++;
}
}
else if (commandCategory.Key == nameof(Properties.Resources.Open))
{
foreach (var command in commandCategory)
{
toolBar.Items.Insert(openPos++, CreateToolbarItem(command));
}
}
else
{
toolBar.Items.Add(new Separator());
foreach (var command in commandCategory)
{
toolBar.Items.Add(CreateToolbarItem(command));
}
}
}
}
static Button CreateToolbarItem(IExport<ICommand, IToolbarCommandMetadata> commandExport)
{
var command = commandExport.Value;
Button toolbarItem = new() {
Style = ThemeManager.Current.CreateToolBarButtonStyle(),
Command = CommandWrapper.Unwrap(command),
ToolTip = Properties.Resources.ResourceManager.GetString(
commandExport.Metadata?.ToolTip ?? string.Empty),
Tag = commandExport.Metadata?.Tag,
Content = new Image {
Width = 16,
Height = 16,
Source = Images.Load(command, commandExport.Metadata?.ToolbarIcon)
}
};
if (command is IProvideParameterBinding parameterBinding)
{
BindingOperations.SetBinding(toolbarItem, ButtonBase.CommandParameterProperty,
parameterBinding.ParameterBinding);
}
return toolbarItem;
}
void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (e.Handled || e.KeyboardDevice.Modifiers != ModifierKeys.Alt || e.Key != Key.System)
return;
switch (e.SystemKey)
{
case Key.A:
assemblyListComboBox.Focus();
e.Handled = true;
break;
case Key.L:
languageComboBox.Focus();
e.Handled = true;
break;
case Key.E: // Alt+V was already taken by _View menu
languageVersionComboBox.Focus();
e.Handled = true;
break;
}
}
}
}

122
ILSpy/MainWindow.xaml

@ -49,127 +49,9 @@ @@ -49,127 +49,9 @@
<DockPanel>
<!-- Main menu -->
<Menu DockPanel.Dock="Top" Name="mainMenu" Height="23" KeyboardNavigation.TabNavigation="None">
<MenuItem Header="{x:Static properties:Resources._File}" Tag="_File">
<!-- content of file menu is added using MEF -->
</MenuItem>
<MenuItem Header="{x:Static properties:Resources._View}" Tag="_View">
<MenuItem Header="{x:Static properties:Resources.Show_publiconlyTypesMembers}" IsCheckable="True"
IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisPublicOnly}" />
<MenuItem Header="{x:Static properties:Resources.Show_internalTypesMembers}" IsCheckable="True"
IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisPublicAndInternal}" />
<MenuItem Header="{x:Static properties:Resources.Show_allTypesAndMembers}" IsCheckable="True"
IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisAll}" />
<Separator />
<MenuItem Header="{x:Static properties:Resources.Theme}" ItemsSource="{x:Static themes:ThemeManager.AllThemes}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Command" Value="{composition:Import commands:SetThemeCommand}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="IsCheckable" Value="True" />
<!-- Required by AvalonDock's MenuItem style to show the checkmark -->
<Setter Property="IsChecked">
<Setter.Value>
<MultiBinding Converter="{x:Static toms:BinaryOperationConverter.Equality}" Mode="OneWay">
<Binding />
<Binding Path="DataContext.SessionSettings.Theme" RelativeSource="{RelativeSource FindAncestor, AncestorType=Window}"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
<MenuItem Header="{x:Static properties:Resources.UILanguage}">
<MenuItem Header="{x:Static properties:Resources.UILanguage_System}" IsCheckable="True"
IsChecked="{Binding SessionSettings.CurrentCulture, Converter={controls:CultureSelectionConverter}, ConverterParameter={x:Null}}" />
<MenuItem Header="English" IsCheckable="True"
IsChecked="{Binding SessionSettings.CurrentCulture, Converter={controls:CultureSelectionConverter}, ConverterParameter=en-US}" />
<MenuItem Header="中文" IsCheckable="True"
IsChecked="{Binding SessionSettings.CurrentCulture, Converter={controls:CultureSelectionConverter}, ConverterParameter=zh-Hans}" />
</MenuItem>
</MenuItem>
<MenuItem Header="{x:Static properties:Resources._Window}" Tag="_Window" >
<!-- content of window menu is added using MEF -->
</MenuItem>
</Menu>
<ContentControl DockPanel.Dock="Top" Content="{composition:Import controls:MainMenu}"/>
<!-- ToolBar -->
<ToolBar
Name="toolBar"
DockPanel.Dock="Top" ToolBarTray.IsLocked="True" KeyboardNavigation.TabNavigation="None">
<ToolBar.Resources>
<!-- Make images transparent if menu command is disabled -->
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ButtonBase}, AncestorLevel=1}, Path=IsEnabled}"
Value="False">
<Setter Property="Opacity" Value="0.30" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Image}" x:Key="DarkModeAwareImageStyle">
<Setter Property="Effect" Value="{DynamicResource {x:Static themes:ResourceKeys.ThemeAwareButtonEffect}}" />
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ButtonBase}, AncestorLevel=1}, Path=IsEnabled}"
Value="False">
<Setter Property="Opacity" Value="0.30" />
</DataTrigger>
</Style.Triggers>
</Style>
</ToolBar.Resources>
<!-- 'Navigation' toolbar category is inserted here -->
<Separator />
<!-- 'Open' toolbar category is inserted here -->
<Separator />
<Grid Margin="2,0">
<ItemsControl ItemsSource="{Binding AssemblyListManager.AssemblyLists}" Height="0" Margin="15,0" />
<ComboBox Name="assemblyListComboBox" MaxDropDownHeight="Auto"
ItemsSource="{Binding AssemblyListManager.AssemblyLists}"
ToolTip="{x:Static properties:Resources.SelectAssemblyListDropdownTooltip}"
SelectedItem="{Binding SessionSettings.ActiveAssemblyList}" />
</Grid>
<Button Command="{composition:Import local:ManageAssemblyListsCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
ToolTip="{x:Static properties:Resources.ManageAssemblyLists}">
<Image Width="16" Height="16" Source="{controls:XamlResource Images/AssemblyList}"
Style="{StaticResource DarkModeAwareImageStyle}" />
</Button>
<Separator />
<CheckBox IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisPublicOnly}"
ToolTip="{x:Static properties:Resources.ShowPublicOnlyTypesMembers}">
<Image Width="16" Height="16" Source="{controls:XamlResource Images/ShowPublicOnly}"
Style="{StaticResource DarkModeAwareImageStyle}" />
</CheckBox>
<CheckBox IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisPublicAndInternal}"
ToolTip="{x:Static properties:Resources.ShowInternalTypesMembers}">
<Image Width="16" Height="16" Source="{controls:XamlResource Images/ShowPrivateInternal}"
Style="{StaticResource DarkModeAwareImageStyle}" />
</CheckBox>
<CheckBox IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisAll}"
ToolTip="{x:Static properties:Resources.ShowAllTypesAndMembers}">
<Image Width="16" Height="16" Source="{controls:XamlResource Images/ShowAll}"
Style="{StaticResource DarkModeAwareImageStyle}" />
</CheckBox>
<Separator />
<Grid Margin="2,0">
<ItemsControl ItemsSource="{Binding LanguageService.AllLanguages}" DisplayMemberPath="Name" Height="0" Margin="15,0" />
<ComboBox Name="languageComboBox" DisplayMemberPath="Name" MaxDropDownHeight="Auto"
IsEnabled="{Binding Workspace.ActiveTabPage.SupportsLanguageSwitching}"
ItemsSource="{Binding LanguageService.AllLanguages}"
ToolTip="{x:Static properties:Resources.SelectLanguageDropdownTooltip}"
SelectedItem="{Binding LanguageService.Language}" />
</Grid>
<Grid Margin="2,0">
<ItemsControl ItemsSource="{Binding SelectedItem.LanguageVersions, ElementName=languageComboBox, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="DisplayName" Height="0" Margin="15,0" />
<ComboBox Name="languageVersionComboBox" DisplayMemberPath="DisplayName" MaxDropDownHeight="Auto"
ToolTip="{x:Static properties:Resources.SelectVersionDropdownTooltip}"
Visibility="{Binding SelectedItem.HasLanguageVersions, ElementName=languageComboBox, Converter={toms:BooleanToVisibilityConverter}}"
IsEnabled="{Binding Workspace.ActiveTabPage.SupportsLanguageSwitching}"
ItemsSource="{Binding SelectedItem.LanguageVersions, ElementName=languageComboBox, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding LanguageService.LanguageVersion, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</ToolBar>
<ContentControl DockPanel.Dock="Top" Content="{composition:Import controls:MainToolBar}" />
<!-- Update panel -->
<ContentControl DockPanel.Dock="Top" Content="{composition:Import viewModels:UpdatePanelViewModel}" />
<!-- Status bar -->

27
ILSpy/MainWindow.xaml.cs

@ -22,7 +22,6 @@ using System.Composition; @@ -22,7 +22,6 @@ using System.Composition;
using System.Drawing;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
@ -40,7 +39,7 @@ namespace ICSharpCode.ILSpy @@ -40,7 +39,7 @@ namespace ICSharpCode.ILSpy
{
private readonly SettingsService settingsService;
public MainWindow(MainWindowViewModel mainWindowViewModel, MenuService menuService, SettingsService settingsService)
public MainWindow(MainWindowViewModel mainWindowViewModel, SettingsService settingsService)
{
this.settingsService = settingsService;
@ -53,7 +52,6 @@ namespace ICSharpCode.ILSpy @@ -53,7 +52,6 @@ namespace ICSharpCode.ILSpy
Dispatcher.BeginInvoke(DispatcherPriority.Background, () => {
mainWindowViewModel.Workspace.InitializeLayout();
menuService.Init(mainMenu, toolBar, InputBindings);
MessageBus.Send(this, new MainWindowLoadedEventArgs());
});
}
@ -85,29 +83,6 @@ namespace ICSharpCode.ILSpy @@ -85,29 +83,6 @@ namespace ICSharpCode.ILSpy
this.WindowState = sessionSettings.WindowState;
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (!e.Handled && e.KeyboardDevice.Modifiers == ModifierKeys.Alt && e.Key == Key.System)
{
switch (e.SystemKey)
{
case Key.A:
assemblyListComboBox.Focus();
e.Handled = true;
break;
case Key.L:
languageComboBox.Focus();
e.Handled = true;
break;
case Key.E: // Alt+V was already taken by _View menu
languageVersionComboBox.Focus();
e.Handled = true;
break;
}
}
}
protected override void OnStateChanged(EventArgs e)
{
base.OnStateChanged(e);

Loading…
Cancel
Save