Browse Source

Refactoring:

- Decouple services to reduce circular dependencies
- Move update panel to a separate control
- Remove unrelated methods from MainWindow
pull/3314/head
tom-englert 7 months ago
parent
commit
a24e0f96c8
  1. 5
      ICSharpCode.ILSpyX/Analyzers/AnalyzerScope.cs
  2. 1
      ICSharpCode.ILSpyX/Analyzers/IAnalyzer.cs
  3. 43
      ILSpy/AboutPage.cs
  4. 2
      ILSpy/Analyzers/AnalyzeCommand.cs
  5. 4
      ILSpy/Analyzers/AnalyzerSearchTreeNode.cs
  6. 6
      ILSpy/Analyzers/AnalyzerTreeNode.cs
  7. 17
      ILSpy/App.xaml.cs
  8. 86
      ILSpy/AssemblyTree/AssemblyTreeModel.cs
  9. 7
      ILSpy/Commands/CheckForUpdatesCommand.cs
  10. 4
      ILSpy/Commands/ExitCommand.cs
  11. 16
      ILSpy/Commands/ManageAssemblyListsCommand.cs
  12. 13
      ILSpy/Commands/OpenFromGacCommand.cs
  13. 2
      ILSpy/Commands/SearchMsdnContextMenuEntry.cs
  14. 36
      ILSpy/Commands/SimpleCommand.cs
  15. 54
      ILSpy/Docking/DockWorkspace.cs
  16. 6
      ILSpy/Languages/Language.cs
  17. 29
      ILSpy/MainWindow.xaml
  18. 152
      ILSpy/MainWindow.xaml.cs
  19. 6
      ILSpy/MainWindowViewModel.cs
  20. 5
      ILSpy/Options/OptionsDialog.xaml.cs
  21. 6
      ILSpy/Search/SearchPaneModel.cs
  22. 47
      ILSpy/TextView/DecompilerTextView.cs
  23. 6
      ILSpy/TextView/DocumentationUIBuilder.cs
  24. 4
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  25. 10
      ILSpy/TreeNodes/ILSpyTreeNode.cs
  26. 8
      ILSpy/Updates/UpdateService.cs
  27. 52
      ILSpy/Util/GlobalUtils.cs
  28. 52
      ILSpy/Util/MenuService.cs
  29. 48
      ILSpy/Util/MessageBus.cs
  30. 15
      ILSpy/ViewModels/ManageAssemblyListsViewModel.cs
  31. 5
      ILSpy/ViewModels/PaneModel.cs
  32. 62
      ILSpy/ViewModels/TabPageModel.cs
  33. 100
      ILSpy/ViewModels/UpdatePanelViewModel.cs
  34. 22
      ILSpy/Views/UpdatePanel.xaml
  35. 24
      ILSpy/Views/UpdatePanel.xaml.cs

5
ICSharpCode.ILSpyX/Analyzers/AnalyzerScope.cs

@ -40,17 +40,14 @@ namespace ICSharpCode.ILSpyX.Analyzers @@ -40,17 +40,14 @@ namespace ICSharpCode.ILSpyX.Analyzers
/// </summary>
public bool IsLocal { get; }
public AssemblyList AssemblyList { get; }
public ISymbol AnalyzedSymbol { get; }
public ITypeDefinition TypeScope => typeScope;
Accessibility effectiveAccessibility;
readonly Accessibility effectiveAccessibility;
public AnalyzerScope(AssemblyList assemblyList, IEntity entity)
{
AssemblyList = assemblyList;
assemblyListSnapshot = assemblyList.GetSnapshot();
AnalyzedSymbol = entity;
DetermineEffectiveAccessibility(entity, out typeScope, out effectiveAccessibility);

1
ICSharpCode.ILSpyX/Analyzers/IAnalyzer.cs

@ -42,6 +42,7 @@ namespace ICSharpCode.ILSpyX.Analyzers @@ -42,6 +42,7 @@ namespace ICSharpCode.ILSpyX.Analyzers
public interface IAnalyzerMetadata
{
string Header { get; }
int Order { get; }
}
}

43
ILSpy/AboutPage.cs

@ -26,36 +26,42 @@ using System.Windows.Controls; @@ -26,36 +26,42 @@ 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;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.Themes;
using ICSharpCode.ILSpy.Updates;
using ICSharpCode.ILSpy.ViewModels;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._Help), Header = nameof(Resources._About), MenuOrder = 99999)]
[Shared]
public sealed class AboutPageCommand(AssemblyTreeModel assemblyTreeModel) : SimpleCommand
public sealed class AboutPage : SimpleCommand
{
readonly SettingsService settingsService;
readonly IEnumerable<IAboutPageAddition> aboutPageAdditions;
public AboutPage(SettingsService settingsService, IEnumerable<IAboutPageAddition> aboutPageAdditions)
{
this.settingsService = settingsService;
this.aboutPageAdditions = aboutPageAdditions;
MessageBus<ShowAboutPageEventArgs>.Subscribers += (_, e) => ShowAboutPage(e.TabPage);
}
public override void Execute(object parameter)
{
assemblyTreeModel.NavigateTo(
new RequestNavigateEventArgs(new Uri("resource://aboutpage"), null),
inNewTabPage: true
);
MessageBus.Send(this, new NavigateToEventArgs(new(new("resource://aboutpage"), null), inNewTabPage: true));
}
}
[Export]
[Shared]
public sealed class AboutPage(IEnumerable<IAboutPageAddition> aboutPageAdditions, SettingsService settingsService)
{
public void Display(DecompilerTextView textView)
private void ShowAboutPage(TabPageModel tabPage)
{
tabPage.ShowTextView(Display);
}
private void Display(DecompilerTextView textView)
{
AvalonEditTextOutput output = new AvalonEditTextOutput() {
Title = Resources.About,
@ -72,14 +78,14 @@ namespace ICSharpCode.ILSpy @@ -72,14 +78,14 @@ namespace ICSharpCode.ILSpy
HorizontalAlignment = HorizontalAlignment.Center,
Orientation = Orientation.Horizontal
};
if (NotifyOfUpdatesStrategy.LatestAvailableVersion == null)
if (UpdateService.LatestAvailableVersion == null)
{
AddUpdateCheckButton(stackPanel, textView);
}
else
{
// we already retrieved the latest version sometime earlier
ShowAvailableVersion(NotifyOfUpdatesStrategy.LatestAvailableVersion, stackPanel);
ShowAvailableVersion(UpdateService.LatestAvailableVersion, stackPanel);
}
CheckBox checkBox = new() {
Margin = new Thickness(4),
@ -104,8 +110,7 @@ namespace ICSharpCode.ILSpy @@ -104,8 +110,7 @@ namespace ICSharpCode.ILSpy
{
using (StreamReader r = new StreamReader(s))
{
string line;
while ((line = r.ReadLine()) != null)
while (r.ReadLine() is { } line)
{
output.WriteLine(line);
}
@ -166,7 +171,7 @@ namespace ICSharpCode.ILSpy @@ -166,7 +171,7 @@ namespace ICSharpCode.ILSpy
try
{
AvailableVersionInfo vInfo = await NotifyOfUpdatesStrategy.GetLatestVersionAsync();
AvailableVersionInfo vInfo = await UpdateService.GetLatestVersionAsync();
stackPanel.Children.Clear();
ShowAvailableVersion(vInfo, stackPanel);
}
@ -209,7 +214,7 @@ namespace ICSharpCode.ILSpy @@ -209,7 +214,7 @@ namespace ICSharpCode.ILSpy
button.Content = Resources.Download;
button.Cursor = Cursors.Arrow;
button.Click += delegate {
MainWindow.OpenLink(availableVersion.DownloadUrl);
GlobalUtils.OpenLink(availableVersion.DownloadUrl);
};
stackPanel.Children.Add(button);
}

2
ILSpy/Analyzers/AnalyzeCommand.cs

@ -73,6 +73,8 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -73,6 +73,8 @@ namespace ICSharpCode.ILSpy.Analyzers
}
}
[Export]
[Shared]
public sealed class AnalyzeCommand(AssemblyTreeModel assemblyTreeModel, AnalyzerTreeViewModel analyzerTreeViewModel) : SimpleCommand
{
public override bool CanExecute(object parameter)

4
ILSpy/Analyzers/AnalyzerSearchTreeNode.cs

@ -58,10 +58,10 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -58,10 +58,10 @@ namespace ICSharpCode.ILSpy.Analyzers
{
if (symbol is IEntity)
{
var context = new AnalyzerContext() {
var context = new AnalyzerContext {
CancellationToken = ct,
Language = Language,
AssemblyList = AssemblyTreeModel.AssemblyList
AssemblyList = AssemblyList
};
var results = analyzer.Analyze(symbol, context).Select(SymbolTreeNodeFactory);
if (context.SortResults)

6
ILSpy/Analyzers/AnalyzerTreeNode.cs

@ -30,7 +30,9 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -30,7 +30,9 @@ namespace ICSharpCode.ILSpy.Analyzers
{
public abstract class AnalyzerTreeNode : SharpTreeNode
{
public static Language Language => App.ExportProvider.GetExportedValue<LanguageService>().Language;
protected static Language Language => App.ExportProvider.GetExportedValue<LanguageService>().Language;
protected static AssemblyList AssemblyList => App.ExportProvider.GetExportedValue<AssemblyList>();
public override bool CanDelete()
{
@ -47,8 +49,6 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -47,8 +49,6 @@ namespace ICSharpCode.ILSpy.Analyzers
DeleteCore();
}
public static AssemblyTreeModel AssemblyTreeModel { get; } = App.ExportProvider.GetExportedValue<AssemblyTreeModel>();
public static ICollection<IExport<IAnalyzer, IAnalyzerMetadata>> Analyzers => App.ExportProvider
.GetExports<IAnalyzer, IAnalyzerMetadata>("Analyzer")
.OrderBy(item => item.Metadata?.Order)

17
ILSpy/App.xaml.cs

@ -25,8 +25,6 @@ using System.Reflection; @@ -25,8 +25,6 @@ using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Navigation;
using System.Windows.Threading;
using ICSharpCode.ILSpy.AppEnv;
@ -115,9 +113,6 @@ namespace ICSharpCode.ILSpy @@ -115,9 +113,6 @@ namespace ICSharpCode.ILSpy
Thread.CurrentThread.CurrentUICulture = CultureInfo.DefaultThreadCurrentUICulture = new(sessionSettings.CurrentCulture);
}
EventManager.RegisterClassHandler(typeof(Window),
Hyperlink.RequestNavigateEvent,
new RequestNavigateEventHandler(Window_RequestNavigate));
ILSpyTraceListener.Install();
if (CommandLineArguments.ArgumentsParser.IsShowingInformation)
@ -138,7 +133,7 @@ namespace ICSharpCode.ILSpy @@ -138,7 +133,7 @@ namespace ICSharpCode.ILSpy
public new MainWindow MainWindow {
get => (MainWindow)base.MainWindow;
set => base.MainWindow = value;
private set => base.MainWindow = value;
}
private static void SingleInstance_NewInstanceDetected(object sender, NewInstanceEventArgs e) => ExportProvider.GetExportedValue<AssemblyTreeModel>().HandleSingleInstanceCommandLineArguments(e.Args).HandleExceptions();
@ -152,7 +147,7 @@ namespace ICSharpCode.ILSpy @@ -152,7 +147,7 @@ namespace ICSharpCode.ILSpy
return context.LoadFromAssemblyPath(assemblyFileName);
}
private static bool InitializeDependencyInjection(SettingsService settingsService)
private bool InitializeDependencyInjection(SettingsService settingsService)
{
// Add custom logic for resolution of dependencies.
// This necessary because the AssemblyLoadContext.LoadFromAssemblyPath and related methods,
@ -193,11 +188,14 @@ namespace ICSharpCode.ILSpy @@ -193,11 +188,14 @@ namespace ICSharpCode.ILSpy
services.AddSingleton(_ => ExportProvider);
// Add the docking manager
services.AddSingleton(serviceProvider => serviceProvider.GetService<MainWindow>().DockManager);
services.AddTransient(serviceProvider => serviceProvider.GetService<AssemblyTreeModel>().AssemblyList);
var serviceProvider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true });
ExportProvider = new ExportProviderAdapter(serviceProvider);
Exit += (_, _) => serviceProvider.Dispose();
return true;
}
catch (Exception ex)
@ -274,10 +272,5 @@ namespace ICSharpCode.ILSpy @@ -274,10 +272,5 @@ namespace ICSharpCode.ILSpy
}
}
#endregion
void Window_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
ExportProvider.GetExportedValue<AssemblyTreeModel>().NavigateTo(e);
}
}
}

86
ILSpy/AssemblyTree/AssemblyTreeModel.cs

@ -28,6 +28,7 @@ using System.Reflection.Metadata; @@ -28,6 +28,7 @@ using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Navigation;
using System.Windows.Threading;
@ -37,15 +38,12 @@ using ICSharpCode.Decompiler.Metadata; @@ -37,15 +38,12 @@ using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.Search;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.Updates;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.Settings;
using ICSharpCode.ILSpyX.TreeView;
using TomsToolbox.Composition;
@ -58,7 +56,6 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -58,7 +56,6 @@ namespace ICSharpCode.ILSpy.AssemblyTree
{
[ExportToolPane]
[Shared]
[Export]
public class AssemblyTreeModel : ToolPaneModel
{
public const string PaneContentId = "assemblyListPane";
@ -69,16 +66,12 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -69,16 +66,12 @@ namespace ICSharpCode.ILSpy.AssemblyTree
private readonly NavigationHistory<NavigationState> history = new();
private bool isNavigatingHistory;
private readonly AboutPage aboutPage;
private readonly SearchPaneModel searchPaneModel;
private readonly SettingsService settingsService;
private readonly LanguageService languageService;
private readonly IExportProvider exportProvider;
public AssemblyTreeModel(AboutPage aboutPage, SearchPaneModel searchPaneModel, SettingsService settingsService, LanguageService languageService, IExportProvider exportProvider)
public AssemblyTreeModel(SettingsService settingsService, LanguageService languageService, IExportProvider exportProvider)
{
this.aboutPage = aboutPage;
this.searchPaneModel = searchPaneModel;
this.settingsService = settingsService;
this.languageService = languageService;
this.exportProvider = exportProvider;
@ -90,8 +83,18 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -90,8 +83,18 @@ namespace ICSharpCode.ILSpy.AssemblyTree
MessageBus<NavigateToReferenceEventArgs>.Subscribers += JumpToReference;
MessageBus<SettingsChangedEventArgs>.Subscribers += (sender, e) => Settings_PropertyChanged(sender, e);
MessageBus<ApplySessionSettingsEventArgs>.Subscribers += ApplySessionSettings;
MessageBus<ActiveTabPageChangedEventArgs>.Subscribers += ActiveTabPageChanged;
MessageBus<ResetLayoutEventArgs>.Subscribers += ResetLayout;
MessageBus<NavigateToEventArgs>.Subscribers += (_, e) => NavigateTo(e.Request, e.InNewTabPage);
MessageBus<MainWindowLoadedEventArgs>.Subscribers += (_, _) => {
Initialize();
Show();
};
EventManager.RegisterClassHandler(typeof(Window), Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler((_, e) => NavigateTo(e)));
refreshThrottle = new DispatcherThrottle(DispatcherPriority.Background, RefreshInternal);
refreshThrottle = new(DispatcherPriority.Background, RefreshInternal);
AssemblyList = settingsService.CreateEmptyAssemblyList();
}
@ -183,8 +186,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -183,8 +186,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
if (args.Search != null)
{
this.searchPaneModel.SearchTerm = args.Search;
this.searchPaneModel.Show();
MessageBus.Send(this, new ShowSearchPageEventArgs(args.Search));
}
}
@ -301,11 +303,11 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -301,11 +303,11 @@ namespace ICSharpCode.ILSpy.AssemblyTree
SelectNode(node);
// only if not showing the about page, perform the update check:
await App.Current.MainWindow.ShowMessageIfUpdatesAvailableAsync(updateSettings);
MessageBus.Send(this, new CheckIfUpdateAvailableEventArgs());
}
else
{
DockWorkspace.ActiveTabPage.ShowTextView(aboutPage.Display);
MessageBus.Send(this, new ShowAboutPageEventArgs(DockWorkspace.ActiveTabPage));
}
}
}
@ -442,7 +444,6 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -442,7 +444,6 @@ namespace ICSharpCode.ILSpy.AssemblyTree
AssemblyList.CollectionChanged -= assemblyList_CollectionChanged;
AssemblyList = assemblyList;
assemblyList.CollectionChanged += assemblyList_CollectionChanged;
assemblyListTreeNode = new(assemblyList) {
@ -655,7 +656,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -655,7 +656,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
switch (reference)
{
case Decompiler.Disassembler.OpCodeInfo opCode:
MainWindow.OpenLink(opCode.Link);
GlobalUtils.OpenLink(opCode.Link);
break;
case EntityReference unresolvedEntity:
string protocol = unresolvedEntity.Protocol;
@ -817,11 +818,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -817,11 +818,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
public LanguageVersion? CurrentLanguageVersion => languageService.LanguageVersion;
public IEnumerable<ILSpyTreeNode> SelectedNodes {
get {
return GetTopLevelSelection().OfType<ILSpyTreeNode>();
}
}
public IEnumerable<ILSpyTreeNode> SelectedNodes => GetTopLevelSelection().OfType<ILSpyTreeNode>();
#endregion
@ -856,7 +853,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -856,7 +853,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
public bool CanNavigateForward => history.CanNavigateForward;
public void NavigateTo(RequestNavigateEventArgs e, bool inNewTabPage = false)
private void NavigateTo(RequestNavigateEventArgs e, bool inNewTabPage = false)
{
if (e.Uri.Scheme == "resource")
{
@ -868,7 +865,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -868,7 +865,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
if (e.Uri.Host == "aboutpage")
{
RecordHistory();
DockWorkspace.ActiveTabPage.ShowTextView(aboutPage.Display);
MessageBus.Send(this, new ShowAboutPageEventArgs(DockWorkspace.ActiveTabPage));
e.Handled = true;
return;
}
@ -997,5 +994,48 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -997,5 +994,48 @@ namespace ICSharpCode.ILSpy.AssemblyTree
LoadAssemblies(fileNames, focusNode: focusNode);
}
private void ApplySessionSettings(object? sender, ApplySessionSettingsEventArgs e)
{
var settings = e.SessionSettings;
settings.ActiveAssemblyList = AssemblyList.ListName;
settings.ActiveTreeViewPath = SelectedPath;
settings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(SelectedItem);
}
private static string? GetAutoLoadedAssemblyNode(SharpTreeNode? node)
{
var assemblyTreeNode = node?
.AncestorsAndSelf()
.OfType<AssemblyTreeNode>()
.FirstOrDefault();
var loadedAssembly = assemblyTreeNode?.LoadedAssembly;
return loadedAssembly is not { IsLoaded: true, IsAutoLoaded: true }
? null
: loadedAssembly.FileName;
}
private void ActiveTabPageChanged(object? sender, ActiveTabPageChangedEventArgs e)
{
if (e.ViewState is not { } state)
return;
if (state.DecompiledNodes != null)
{
SelectNodes(state.DecompiledNodes);
}
else
{
NavigateTo(new(state.ViewedUri, null));
}
}
private void ResetLayout(object? sender, ResetLayoutEventArgs e)
{
RefreshDecompiledView();
}
}
}

7
ILSpy/Commands/CheckForUpdatesCommand.cs

@ -20,17 +20,16 @@ @@ -20,17 +20,16 @@
using System.Composition;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.Updates;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._Help), Header = nameof(Resources._CheckUpdates), MenuOrder = 5000)]
[Shared]
sealed class CheckForUpdatesCommand(SettingsService settingsService) : SimpleCommand
sealed class CheckForUpdatesCommand : SimpleCommand
{
public override async void Execute(object parameter)
public override void Execute(object parameter)
{
await App.Current.MainWindow.ShowMessageIfUpdatesAvailableAsync(settingsService.GetSettings<UpdateSettings>(), forceCheck: true);
MessageBus.Send(this, new CheckIfUpdateAvailableEventArgs(notify: true));
}
}
}

4
ILSpy/Commands/ExitCommand.cs

@ -23,11 +23,11 @@ namespace ICSharpCode.ILSpy @@ -23,11 +23,11 @@ namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.E_xit), MenuOrder = 99999, MenuCategory = nameof(Resources.Exit))]
[Shared]
sealed class ExitCommand : SimpleCommand
sealed class ExitCommand(MainWindow mainWindow) : SimpleCommand
{
public override void Execute(object parameter)
{
App.Current.MainWindow.Close();
mainWindow.Close();
}
}
}

16
ILSpy/Commands/ManageAssemblyListsCommand.cs

@ -18,6 +18,8 @@ @@ -18,6 +18,8 @@
using System.Composition;
using System.Windows;
using System.Windows.Data;
using ICSharpCode.ILSpy.Properties;
@ -25,13 +27,21 @@ namespace ICSharpCode.ILSpy @@ -25,13 +27,21 @@ namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.ManageAssembly_Lists), MenuIcon = "Images/AssemblyList", MenuCategory = nameof(Resources.Open), MenuOrder = 1.7)]
[Shared]
sealed class ManageAssemblyListsCommand(SettingsService settingsService) : SimpleCommand
sealed class ManageAssemblyListsCommand(SettingsService settingsService) : SimpleCommand, IProvideParameterBinding
{
public override void Execute(object parameter)
{
ManageAssemblyListsDialog dlg = new ManageAssemblyListsDialog(settingsService);
dlg.Owner = App.Current.MainWindow;
ManageAssemblyListsDialog dlg = new(settingsService) {
Owner = parameter as Window
};
dlg.ShowDialog();
}
public Binding ParameterBinding => new() {
RelativeSource = new(RelativeSourceMode.FindAncestor) {
AncestorType = typeof(Window)
}
};
}
}

13
ILSpy/Commands/OpenFromGacCommand.cs

@ -26,15 +26,8 @@ namespace ICSharpCode.ILSpy @@ -26,15 +26,8 @@ namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.OpenFrom_GAC), MenuIcon = "Images/AssemblyListGAC", MenuCategory = nameof(Resources.Open), MenuOrder = 1)]
[Shared]
sealed class OpenFromGacCommand : SimpleCommand
sealed class OpenFromGacCommand(AssemblyTreeModel assemblyTreeModel, MainWindow mainWindow) : SimpleCommand
{
private readonly AssemblyTreeModel assemblyTreeModel;
public OpenFromGacCommand(AssemblyTreeModel assemblyTreeModel)
{
this.assemblyTreeModel = assemblyTreeModel;
}
public override bool CanExecute(object parameter)
{
return AppEnvironment.IsWindows;
@ -42,8 +35,8 @@ namespace ICSharpCode.ILSpy @@ -42,8 +35,8 @@ namespace ICSharpCode.ILSpy
public override void Execute(object parameter)
{
OpenFromGacDialog dlg = new OpenFromGacDialog {
Owner = App.Current.MainWindow
OpenFromGacDialog dlg = new() {
Owner = mainWindow
};
if (dlg.ShowDialog() == true)

2
ILSpy/Commands/SearchMsdnContextMenuEntry.cs

@ -136,7 +136,7 @@ namespace ICSharpCode.ILSpy @@ -136,7 +136,7 @@ namespace ICSharpCode.ILSpy
address = address.ToLower();
if (!string.IsNullOrEmpty(address))
MainWindow.OpenLink(address);
GlobalUtils.OpenLink(address);
}
}
}

36
ILSpy/Commands/SimpleCommand.cs

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.ComponentModel;
using System.Windows.Data;
using System.Windows.Input;
namespace ICSharpCode.ILSpy
@ -37,38 +37,8 @@ namespace ICSharpCode.ILSpy @@ -37,38 +37,8 @@ namespace ICSharpCode.ILSpy
}
}
public abstract class ToggleableCommand : ICommand, INotifyPropertyChanged
public interface IProvideParameterBinding
{
private bool isChecked;
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public event PropertyChangedEventHandler PropertyChanged;
void ICommand.Execute(object parameter)
{
IsChecked = Execute(parameter);
}
public bool IsChecked {
get => isChecked;
set {
if (isChecked != value)
{
isChecked = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsChecked)));
}
}
}
public abstract bool Execute(object parameter);
public virtual bool CanExecute(object parameter)
{
return true;
}
Binding ParameterBinding { get; }
}
}

54
ILSpy/Docking/DockWorkspace.cs

@ -33,7 +33,6 @@ using AvalonDock.Layout.Serialization; @@ -33,7 +33,6 @@ using AvalonDock.Layout.Serialization;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.ILSpy.Analyzers;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Search;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.ViewModels;
@ -50,24 +49,17 @@ namespace ICSharpCode.ILSpy.Docking @@ -50,24 +49,17 @@ namespace ICSharpCode.ILSpy.Docking
{
private readonly IExportProvider exportProvider;
private SettingsService SettingsService { get; }
private LanguageService LanguageService => exportProvider.GetExportedValue<LanguageService>();
private SessionSettings SessionSettings { get; }
private readonly ObservableCollection<TabPageModel> tabPages = [];
private DockingManager DockingManager => exportProvider.GetExportedValue<DockingManager>();
readonly SessionSettings sessionSettings;
private AssemblyTreeModel AssemblyTreeModel => exportProvider.GetExportedValue<AssemblyTreeModel>();
private DockingManager DockingManager => exportProvider.GetExportedValue<DockingManager>();
public DockWorkspace(SettingsService settingsService, IExportProvider exportProvider)
{
this.exportProvider = exportProvider;
SettingsService = settingsService;
SessionSettings = settingsService.SessionSettings;
sessionSettings = settingsService.SessionSettings;
this.tabPages.CollectionChanged += TabPages_CollectionChanged;
TabPages = new(tabPages);
@ -77,10 +69,9 @@ namespace ICSharpCode.ILSpy.Docking @@ -77,10 +69,9 @@ namespace ICSharpCode.ILSpy.Docking
private void CurrentAssemblyList_Changed(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems == null)
{
if (e.OldItems is not { } oldItems)
return;
}
foreach (var tab in tabPages.ToArray())
{
var state = tab.GetState();
@ -91,7 +82,7 @@ namespace ICSharpCode.ILSpy.Docking @@ -91,7 +82,7 @@ namespace ICSharpCode.ILSpy.Docking
bool found = decompiledNodes
.Select(node => node.Ancestors().OfType<TreeNodes.AssemblyTreeNode>().LastOrDefault())
.ExceptNullItems()
.Any(assemblyNode => !e.OldItems.Contains(assemblyNode.LoadedAssembly));
.Any(assemblyNode => !oldItems.Contains(assemblyNode.LoadedAssembly));
if (!found)
{
@ -127,7 +118,7 @@ namespace ICSharpCode.ILSpy.Docking @@ -127,7 +118,7 @@ namespace ICSharpCode.ILSpy.Docking
public void AddTabPage(TabPageModel tabPage = null)
{
tabPages.Add(tabPage ?? new TabPageModel(AssemblyTreeModel, SettingsService, LanguageService));
tabPages.Add(tabPage ?? exportProvider.GetExportedValue<TabPageModel>());
}
public ReadOnlyObservableCollection<TabPageModel> TabPages { get; }
@ -169,22 +160,13 @@ namespace ICSharpCode.ILSpy.Docking @@ -169,22 +160,13 @@ namespace ICSharpCode.ILSpy.Docking
}
set {
if (!SetProperty(ref activeTabPage, value))
{
return;
}
var state = value?.GetState();
if (state != null)
{
if (state.DecompiledNodes != null)
{
AssemblyTreeModel.SelectNodes(state.DecompiledNodes);
}
else
{
AssemblyTreeModel.NavigateTo(new(state.ViewedUri, null));
}
}
if (state == null)
return;
MessageBus.Send(this, new ActiveTabPageChangedEventArgs(value?.GetState()));
}
}
@ -195,15 +177,18 @@ namespace ICSharpCode.ILSpy.Docking @@ -195,15 +177,18 @@ namespace ICSharpCode.ILSpy.Docking
public void InitializeLayout()
{
// Make sure there is at least one tab open
AddTabPage();
if (tabPages.Count == 0)
{
// Make sure there is at least one tab open
AddTabPage();
}
DockingManager.LayoutUpdateStrategy = this;
XmlLayoutSerializer serializer = new XmlLayoutSerializer(DockingManager);
serializer.LayoutSerializationCallback += LayoutSerializationCallback;
try
{
SessionSettings.DockLayout.Deserialize(serializer);
sessionSettings.DockLayout.Deserialize(serializer);
}
finally
{
@ -260,9 +245,10 @@ namespace ICSharpCode.ILSpy.Docking @@ -260,9 +245,10 @@ namespace ICSharpCode.ILSpy.Docking
pane.IsVisible = false;
}
CloseAllTabs();
SessionSettings.DockLayout.Reset();
sessionSettings.DockLayout.Reset();
InitializeLayout();
App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, AssemblyTreeModel.RefreshDecompiledView);
App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, () => MessageBus.Send(this, new ResetLayoutEventArgs()));
}
static readonly PropertyInfo previousContainerProperty = typeof(LayoutContent).GetProperty("PreviousContainer", BindingFlags.NonPublic | BindingFlags.Instance);

6
ILSpy/Languages/Language.cs

@ -44,11 +44,11 @@ namespace ICSharpCode.ILSpy @@ -44,11 +44,11 @@ namespace ICSharpCode.ILSpy
/// </remarks>
public abstract class Language : ILanguage
{
public static SettingsService SettingsService { get; } = App.ExportProvider.GetExportedValue<SettingsService>();
protected static SettingsService SettingsService { get; } = App.ExportProvider.GetExportedValue<SettingsService>();
public static AssemblyTreeModel AssemblyTreeModel { get; } = App.ExportProvider.GetExportedValue<AssemblyTreeModel>();
protected static AssemblyTreeModel AssemblyTreeModel { get; } = App.ExportProvider.GetExportedValue<AssemblyTreeModel>();
public static ICollection<IResourceFileHandler> ResourceFileHandlers { get; } = App.ExportProvider.GetExportedValues<IResourceFileHandler>().ToArray();
protected static ICollection<IResourceFileHandler> ResourceFileHandlers { get; } = App.ExportProvider.GetExportedValues<IResourceFileHandler>().ToArray();
/// <summary>
/// Gets the name of the language (as shown in the UI)

29
ILSpy/MainWindow.xaml

@ -20,6 +20,7 @@ @@ -20,6 +20,7 @@
xmlns:viewModels="clr-namespace:ICSharpCode.ILSpy.ViewModels"
xmlns:composition="urn:TomsToolbox.Composition"
xmlns:commands="clr-namespace:ICSharpCode.ILSpy.Commands"
xmlns:analyzers="clr-namespace:ICSharpCode.ILSpy.Analyzers"
d:DataContext="{d:DesignInstance local:MainWindowViewModel}">
<Window.Resources>
@ -38,7 +39,7 @@ @@ -38,7 +39,7 @@
</b:Interaction.Behaviors>
<Window.InputBindings>
<KeyBinding Key="R" Modifiers="Control" Command="{Binding AnalyzeCommand}" />
<KeyBinding Key="R" Modifiers="Control" Command="{composition:Import analyzers:AnalyzeCommand}" />
<KeyBinding Key="Z" Modifiers="Control" Command="{x:Static NavigationCommands.BrowseBack}" />
</Window.InputBindings>
@ -49,8 +50,9 @@ @@ -49,8 +50,9 @@
<DockPanel>
<!-- Main menu -->
<Menu DockPanel.Dock="Top" Name="mainMenu" Height="23" KeyboardNavigation.TabNavigation="None">
<MenuItem Header="{x:Static properties:Resources._File}" Tag="_File" />
<!-- contents of file menu are added using MEF -->
<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}" />
@ -62,7 +64,7 @@ @@ -62,7 +64,7 @@
<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 {x:Type commands:SetThemeCommand}}" />
<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 -->
@ -86,7 +88,9 @@ @@ -86,7 +88,9 @@
IsChecked="{Binding SessionSettings.CurrentCulture, Converter={controls:CultureSelectionConverter}, ConverterParameter=zh-Hans}" />
</MenuItem>
</MenuItem>
<MenuItem Header="{x:Static properties:Resources._Window}" Tag="_Window" />
<MenuItem Header="{x:Static properties:Resources._Window}" Tag="_Window" >
<!-- content of window menu is added using MEF -->
</MenuItem>
</Menu>
<!-- ToolBar -->
<ToolBar
@ -125,7 +129,8 @@ @@ -125,7 +129,8 @@
ToolTip="{x:Static properties:Resources.SelectAssemblyListDropdownTooltip}"
SelectedItem="{Binding SessionSettings.ActiveAssemblyList}" />
</Grid>
<Button Command="{composition:Import {x:Type local:ManageAssemblyListsCommand}}"
<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}" />
@ -166,17 +171,7 @@ @@ -166,17 +171,7 @@
</Grid>
</ToolBar>
<!-- Update panel -->
<Border DockPanel.Dock="Top" BorderBrush="Black" BorderThickness="1" Name="updatePanel" Visibility="Collapsed">
<DockPanel KeyboardNavigation.TabNavigation="Contained">
<Button DockPanel.Dock="Right" Click="UpdatePanelCloseButtonClick" MinWidth="0">X</Button>
<StackPanel Orientation="Horizontal">
<TextBlock Name="updatePanelMessage" Margin="4,0" VerticalAlignment="Center"
Text="{x:Static properties:Resources.ILSpyVersionAvailable}" />
<Button Name="downloadOrCheckUpdateButton" Click="DownloadOrCheckUpdateButtonClick"
Content="{x:Static properties:Resources.Download}" />
</StackPanel>
</DockPanel>
</Border>
<ContentControl DockPanel.Dock="Top" Content="{composition:Import viewModels:UpdatePanelViewModel}" />
<!-- Status bar -->
<StatusBar x:Name="statusBar" DockPanel.Dock="Bottom" Height="26" Visibility="Collapsed">
<StatusBarItem DockPanel.Dock="Right">

152
ILSpy/MainWindow.xaml.cs

@ -17,27 +17,15 @@ @@ -17,27 +17,15 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Composition;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using AvalonDock.Layout.Serialization;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.Updates;
using ICSharpCode.ILSpyX.FileLoaders;
using ICSharpCode.ILSpyX.Settings;
using ICSharpCode.ILSpyX.TreeView;
using Screen = System.Windows.Forms.Screen;
namespace ICSharpCode.ILSpy
@ -50,16 +38,10 @@ namespace ICSharpCode.ILSpy @@ -50,16 +38,10 @@ namespace ICSharpCode.ILSpy
#pragma warning disable MEF003 // Main window is a singleton
partial class MainWindow
{
private readonly AssemblyTreeModel assemblyTreeModel;
private readonly IEnumerable<IFileLoader> fileLoaders;
private readonly MenuService menuService;
private readonly SettingsService settingsService;
public MainWindow(MainWindowViewModel mainWindowViewModel, AssemblyTreeModel assemblyTreeModel, IEnumerable<IFileLoader> fileLoaders, MenuService menuService, SettingsService settingsService)
public MainWindow(MainWindowViewModel mainWindowViewModel, MenuService menuService, SettingsService settingsService)
{
this.assemblyTreeModel = assemblyTreeModel;
this.fileLoaders = fileLoaders;
this.menuService = menuService;
this.settingsService = settingsService;
// Make sure Images are initialized on the UI thread.
@ -69,16 +51,10 @@ namespace ICSharpCode.ILSpy @@ -69,16 +51,10 @@ namespace ICSharpCode.ILSpy
InitializeComponent();
InitFileLoaders();
Dispatcher.BeginInvoke(DispatcherPriority.Background, () => {
mainWindowViewModel.Workspace.InitializeLayout();
menuService.Init(mainMenu, toolBar, InputBindings);
Dispatcher.BeginInvoke(DispatcherPriority.Background, () => {
assemblyTreeModel.Initialize();
assemblyTreeModel.Show();
});
MessageBus.Send(this, new MainWindowLoadedEventArgs());
});
}
@ -90,20 +66,6 @@ namespace ICSharpCode.ILSpy @@ -90,20 +66,6 @@ namespace ICSharpCode.ILSpy
this.Height = bounds.Height;
}
#region File Loader extensibility
void InitFileLoaders()
{
// TODO
foreach (var loader in fileLoaders)
{
}
}
#endregion
#region Message Hook
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
@ -123,8 +85,6 @@ namespace ICSharpCode.ILSpy @@ -123,8 +85,6 @@ namespace ICSharpCode.ILSpy
this.WindowState = sessionSettings.WindowState;
}
#endregion
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
@ -148,93 +108,6 @@ namespace ICSharpCode.ILSpy @@ -148,93 +108,6 @@ namespace ICSharpCode.ILSpy
}
}
#region Update Check
string updateAvailableDownloadUrl;
public async Task ShowMessageIfUpdatesAvailableAsync(UpdateSettings settings, bool forceCheck = false)
{
string downloadUrl;
if (forceCheck)
{
downloadUrl = await NotifyOfUpdatesStrategy.CheckForUpdatesAsync(settings);
}
else
{
downloadUrl = await NotifyOfUpdatesStrategy.CheckForUpdatesIfEnabledAsync(settings);
}
// The Update Panel is only available for NotifyOfUpdatesStrategy, AutoUpdate will have differing UI requirements
AdjustUpdateUIAfterCheck(downloadUrl, forceCheck);
}
void UpdatePanelCloseButtonClick(object sender, RoutedEventArgs e)
{
updatePanel.Visibility = Visibility.Collapsed;
}
async void DownloadOrCheckUpdateButtonClick(object sender, RoutedEventArgs e)
{
if (updateAvailableDownloadUrl != null)
{
OpenLink(updateAvailableDownloadUrl);
}
else
{
updatePanel.Visibility = Visibility.Collapsed;
string downloadUrl = await NotifyOfUpdatesStrategy.CheckForUpdatesAsync(settingsService.GetSettings<UpdateSettings>());
AdjustUpdateUIAfterCheck(downloadUrl, true);
}
}
void AdjustUpdateUIAfterCheck(string downloadUrl, bool displayMessage)
{
updateAvailableDownloadUrl = downloadUrl;
updatePanel.Visibility = displayMessage ? Visibility.Visible : Visibility.Collapsed;
if (downloadUrl != null)
{
updatePanelMessage.Text = Properties.Resources.ILSpyVersionAvailable;
downloadOrCheckUpdateButton.Content = Properties.Resources.Download;
}
else
{
updatePanelMessage.Text = Properties.Resources.UpdateILSpyFound;
downloadOrCheckUpdateButton.Content = Properties.Resources.CheckAgain;
}
}
#endregion
public static void OpenLink(string link)
{
try
{
Process.Start(new ProcessStartInfo { FileName = link, UseShellExecute = true });
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
}
catch (Exception)
{
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
// Process.Start can throw several errors (not all of them documented),
// just ignore all of them.
}
}
public static void ExecuteCommand(string fileName, string arguments)
{
try
{
Process.Start(fileName, arguments);
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
}
catch (Exception)
{
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
// Process.Start can throw several errors (not all of them documented),
// just ignore all of them.
}
}
protected override void OnStateChanged(EventArgs e)
{
base.OnStateChanged(e);
@ -251,27 +124,12 @@ namespace ICSharpCode.ILSpy @@ -251,27 +124,12 @@ namespace ICSharpCode.ILSpy
var sessionSettings = snapshot.GetSettings<SessionSettings>();
sessionSettings.ActiveAssemblyList = assemblyTreeModel.AssemblyList.ListName;
sessionSettings.ActiveTreeViewPath = assemblyTreeModel.SelectedPath;
sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(assemblyTreeModel.SelectedItem);
MessageBus.Send(this, new ApplySessionSettingsEventArgs(sessionSettings));
sessionSettings.WindowBounds = this.RestoreBounds;
sessionSettings.DockLayout.Serialize(new XmlLayoutSerializer(DockManager));
sessionSettings.DockLayout.Serialize(new(DockManager));
snapshot.Save();
}
private static string GetAutoLoadedAssemblyNode(SharpTreeNode node)
{
var assemblyTreeNode = node?
.AncestorsAndSelf()
.OfType<AssemblyTreeNode>()
.FirstOrDefault();
var loadedAssembly = assemblyTreeNode?.LoadedAssembly;
return loadedAssembly is not { IsLoaded: true, IsAutoLoaded: true }
? null
: loadedAssembly.FileName;
}
}
}

6
ILSpy/MainWindowViewModel.cs

@ -18,8 +18,6 @@ @@ -18,8 +18,6 @@
using System.Composition;
using ICSharpCode.ILSpy.Analyzers;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpyX;
@ -29,7 +27,7 @@ namespace ICSharpCode.ILSpy @@ -29,7 +27,7 @@ namespace ICSharpCode.ILSpy
{
[Export]
[Shared]
public class MainWindowViewModel(AssemblyTreeModel assemblyTreeModel, AnalyzerTreeViewModel analyzerTreeViewModel, SettingsService settingsService, LanguageService languageService, DockWorkspace dockWorkspace) : ObservableObject
public class MainWindowViewModel(SettingsService settingsService, LanguageService languageService, DockWorkspace dockWorkspace) : ObservableObject
{
public DockWorkspace Workspace { get; } = dockWorkspace;
@ -38,7 +36,5 @@ namespace ICSharpCode.ILSpy @@ -38,7 +36,5 @@ namespace ICSharpCode.ILSpy
public LanguageService LanguageService => languageService;
public AssemblyListManager AssemblyListManager => settingsService.AssemblyListManager;
public AnalyzeCommand AnalyzeCommand { get; } = new(assemblyTreeModel, analyzerTreeViewModel);
}
}

5
ILSpy/Options/OptionsDialog.xaml.cs

@ -59,13 +59,14 @@ namespace ICSharpCode.ILSpy.Options @@ -59,13 +59,14 @@ namespace ICSharpCode.ILSpy.Options
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._View), Header = nameof(Resources._Options), MenuCategory = nameof(Resources.Options), MenuOrder = 999)]
[Shared]
sealed class ShowOptionsCommand(AssemblyTreeModel assemblyTreeModel, SettingsService settingsService) : SimpleCommand
sealed class ShowOptionsCommand(AssemblyTreeModel assemblyTreeModel, SettingsService settingsService, MainWindow mainWindow) : SimpleCommand
{
public override void Execute(object parameter)
{
OptionsDialog dlg = new(settingsService) {
Owner = App.Current.MainWindow,
Owner = mainWindow
};
if (dlg.ShowDialog() == true)
{
assemblyTreeModel.Refresh();

6
ILSpy/Search/SearchPaneModel.cs

@ -34,7 +34,6 @@ namespace ICSharpCode.ILSpy.Search @@ -34,7 +34,6 @@ namespace ICSharpCode.ILSpy.Search
[ExportToolPane]
[Shared]
[Export]
public class SearchPaneModel : ToolPaneModel
{
public const string PaneContentId = "searchPane";
@ -50,6 +49,11 @@ namespace ICSharpCode.ILSpy.Search @@ -50,6 +49,11 @@ namespace ICSharpCode.ILSpy.Search
Icon = "Images/Search";
ShortcutKey = new(Key.F, ModifierKeys.Control | ModifierKeys.Shift);
IsCloseable = true;
MessageBus<ShowSearchPageEventArgs>.Subscribers += (_, e) => {
SearchTerm = e.SearchTerm;
Show();
};
}
public SearchModeModel[] SearchModes { get; } = [

47
ILSpy/TextView/DecompilerTextView.cs

@ -62,6 +62,7 @@ using ICSharpCode.ILSpyX; @@ -62,6 +62,7 @@ using ICSharpCode.ILSpyX;
using Microsoft.Win32;
using TomsToolbox.Composition;
using TomsToolbox.Wpf;
using ResourceKeys = ICSharpCode.ILSpy.Themes.ResourceKeys;
@ -74,9 +75,10 @@ namespace ICSharpCode.ILSpy.TextView @@ -74,9 +75,10 @@ namespace ICSharpCode.ILSpy.TextView
/// </summary>
public sealed partial class DecompilerTextView : UserControl, IHaveState, IProgress<DecompilationProgress>
{
readonly AssemblyTreeModel assemblyTreeModel;
readonly IExportProvider exportProvider;
readonly SettingsService settingsService;
private readonly LanguageService languageService;
readonly LanguageService languageService;
readonly MainWindow mainWindow;
readonly ReferenceElementGenerator referenceElementGenerator;
readonly UIElementGenerator uiElementGenerator;
readonly List<VisualLineElementGenerator?> activeCustomElementGenerators = new List<VisualLineElementGenerator?>();
@ -97,11 +99,12 @@ namespace ICSharpCode.ILSpy.TextView @@ -97,11 +99,12 @@ namespace ICSharpCode.ILSpy.TextView
readonly List<ITextMarker> localReferenceMarks = new List<ITextMarker>();
#region Constructor
public DecompilerTextView(TabPageModel tabPage)
public DecompilerTextView(IExportProvider exportProvider)
{
this.assemblyTreeModel = tabPage.AssemblyTreeModel;
this.settingsService = tabPage.SettingsService;
this.languageService = tabPage.LanguageService;
this.exportProvider = exportProvider;
settingsService = exportProvider.GetExportedValue<SettingsService>();
languageService = exportProvider.GetExportedValue<LanguageService>();
mainWindow = exportProvider.GetExportedValue<MainWindow>();
RegisterHighlighting();
@ -137,7 +140,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -137,7 +140,7 @@ namespace ICSharpCode.ILSpy.TextView
// SearchPanel
SearchPanel searchPanel = SearchPanel.Install(textEditor.TextArea);
searchPanel.RegisterCommands(App.Current.MainWindow.CommandBindings);
searchPanel.RegisterCommands(mainWindow.CommandBindings);
searchPanel.SetResourceReference(SearchPanel.MarkerBrushProperty, ResourceKeys.SearchResultBackgroundBrush);
searchPanel.Loaded += (_, _) => {
// HACK: fix search text box
@ -412,7 +415,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -412,7 +415,7 @@ namespace ICSharpCode.ILSpy.TextView
if (segment.Reference is ICSharpCode.Decompiler.Disassembler.OpCodeInfo code)
{
XmlDocumentationProvider docProvider = XmlDocLoader.MscorlibDocumentation;
DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), languageService.Language.SyntaxHighlighting, settingsService.DisplaySettings);
DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), languageService.Language.SyntaxHighlighting, settingsService.DisplaySettings, mainWindow);
renderer.AddSignatureBlock($"{code.Name} (0x{code.Code:x})");
if (docProvider != null)
{
@ -422,18 +425,19 @@ namespace ICSharpCode.ILSpy.TextView @@ -422,18 +425,19 @@ namespace ICSharpCode.ILSpy.TextView
renderer.AddXmlDocumentation(documentation, null, null);
}
}
return new FlowDocumentTooltip(renderer.CreateDocument(), fontSize);
return new FlowDocumentTooltip(renderer.CreateDocument(), fontSize, mainWindow.ActualWidth);
}
else if (segment.Reference is IEntity entity)
{
var document = CreateTooltipForEntity(entity);
if (document == null)
return null;
return new FlowDocumentTooltip(document, fontSize);
return new FlowDocumentTooltip(document, fontSize, mainWindow.ActualWidth);
}
else if (segment.Reference is EntityReference unresolvedEntity)
{
var module = unresolvedEntity.ResolveAssembly(assemblyTreeModel.AssemblyList);
var assemblyList = exportProvider.GetExportedValue<AssemblyList>();
var module = unresolvedEntity.ResolveAssembly(assemblyList);
if (module == null)
return null;
var typeSystem = new DecompilerTypeSystem(module,
@ -450,7 +454,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -450,7 +454,7 @@ namespace ICSharpCode.ILSpy.TextView
var document = CreateTooltipForEntity(resolved);
if (document == null)
return null;
return new FlowDocumentTooltip(document, fontSize);
return new FlowDocumentTooltip(document, fontSize, mainWindow.ActualWidth);
}
catch (BadImageFormatException)
{
@ -463,7 +467,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -463,7 +467,7 @@ namespace ICSharpCode.ILSpy.TextView
FlowDocument? CreateTooltipForEntity(IEntity resolved)
{
Language currentLanguage = languageService.Language;
DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), currentLanguage.SyntaxHighlighting, settingsService.DisplaySettings);
DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), currentLanguage.SyntaxHighlighting, settingsService.DisplaySettings, mainWindow);
RichText richText = currentLanguage.GetRichTextTooltip(resolved);
if (richText == null)
{
@ -493,7 +497,8 @@ namespace ICSharpCode.ILSpy.TextView @@ -493,7 +497,8 @@ namespace ICSharpCode.ILSpy.TextView
IEntity? ResolveReference(string idString)
{
return AssemblyTreeModel.FindEntityInRelevantAssemblies(idString, assemblyTreeModel.AssemblyList.GetAssemblies());
var assemblyList = exportProvider.GetExportedValue<AssemblyList>();
return AssemblyTreeModel.FindEntityInRelevantAssemblies(idString, assemblyList.GetAssemblies());
}
}
@ -501,14 +506,14 @@ namespace ICSharpCode.ILSpy.TextView @@ -501,14 +506,14 @@ namespace ICSharpCode.ILSpy.TextView
{
readonly FlowDocumentScrollViewer viewer;
public FlowDocumentTooltip(FlowDocument document, double fontSize)
public FlowDocumentTooltip(FlowDocument document, double fontSize, double maxWith)
{
TextOptions.SetTextFormattingMode(this, TextFormattingMode.Display);
viewer = new FlowDocumentScrollViewer() {
viewer = new() {
Width = document.MinPageWidth + fontSize * 5,
MaxWidth = App.Current.MainWindow.ActualWidth
MaxWidth = maxWith,
Document = document
};
viewer.Document = document;
Border border = new Border {
BorderThickness = new Thickness(1),
MaxHeight = 400,
@ -572,7 +577,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -572,7 +577,7 @@ namespace ICSharpCode.ILSpy.TextView
progressTitle.Text = !string.IsNullOrWhiteSpace(value.Title) ? value.Title : Properties.Resources.Decompiling;
progressText.Text = value.Status;
progressText.Visibility = !string.IsNullOrWhiteSpace(progressText.Text) ? Visibility.Visible : Visibility.Collapsed;
var taskBar = App.Current.MainWindow.TaskbarItemInfo;
var taskBar = mainWindow.TaskbarItemInfo;
if (taskBar != null)
{
taskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Normal;
@ -602,7 +607,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -602,7 +607,7 @@ namespace ICSharpCode.ILSpy.TextView
progressText.Text = null;
progressText.Visibility = Visibility.Collapsed;
waitAdorner.BeginAnimation(OpacityProperty, new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(0.5)), FillBehavior.Stop));
var taskBar = App.Current.MainWindow.TaskbarItemInfo;
var taskBar = mainWindow.TaskbarItemInfo;
if (taskBar != null)
{
taskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Indeterminate;
@ -641,7 +646,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -641,7 +646,7 @@ namespace ICSharpCode.ILSpy.TextView
progressBar.IsIndeterminate = false;
progressText.Text = null;
progressText.Visibility = Visibility.Collapsed;
var taskBar = App.Current.MainWindow.TaskbarItemInfo;
var taskBar = mainWindow.TaskbarItemInfo;
if (taskBar != null)
{
taskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;

6
ILSpy/TextView/DocumentationUIBuilder.cs

@ -45,15 +45,17 @@ namespace ICSharpCode.ILSpy.TextView @@ -45,15 +45,17 @@ namespace ICSharpCode.ILSpy.TextView
readonly IAmbience ambience;
readonly IHighlightingDefinition highlightingDefinition;
readonly DisplaySettings displaySettings;
readonly MainWindow mainWindow;
readonly FlowDocument document;
BlockCollection blockCollection;
InlineCollection inlineCollection;
public DocumentationUIBuilder(IAmbience ambience, IHighlightingDefinition highlightingDefinition, DisplaySettings displaySettings)
public DocumentationUIBuilder(IAmbience ambience, IHighlightingDefinition highlightingDefinition, DisplaySettings displaySettings, MainWindow mainWindow)
{
this.ambience = ambience;
this.highlightingDefinition = highlightingDefinition;
this.displaySettings = displaySettings;
this.mainWindow = mainWindow;
this.document = new FlowDocument();
this.blockCollection = document.Blocks;
@ -119,7 +121,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -119,7 +121,7 @@ namespace ICSharpCode.ILSpy.TextView
};
text.Inlines.AddRange(richText.CreateRuns(document));
text.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
this.document.MinPageWidth = Math.Min(text.DesiredSize.Width, App.Current.MainWindow.ActualWidth);
this.document.MinPageWidth = Math.Min(text.DesiredSize.Width, mainWindow.ActualWidth);
block.Inlines.AddRange(richText.CreateRuns(document));
block.FontFamily = GetCodeFont();
block.TextAlignment = TextAlignment.Left;

4
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -764,7 +764,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -764,7 +764,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
var path = node.LoadedAssembly.FileName;
if (File.Exists(path))
{
MainWindow.ExecuteCommand("explorer.exe", $"/select,\"{path}\"");
GlobalUtils.ExecuteCommand("explorer.exe", $"/select,\"{path}\"");
}
}
}
@ -806,7 +806,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -806,7 +806,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
var path = Path.GetDirectoryName(node.LoadedAssembly.FileName);
if (Directory.Exists(path))
{
MainWindow.ExecuteCommand("cmd.exe", $"/k \"cd {path}\"");
GlobalUtils.ExecuteCommand("cmd.exe", $"/k \"cd {path}\"");
}
}
}

10
ILSpy/TreeNodes/ILSpyTreeNode.cs

@ -50,15 +50,15 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -50,15 +50,15 @@ namespace ICSharpCode.ILSpy.TreeNodes
public Language Language => LanguageService.Language;
public static AssemblyTreeModel AssemblyTreeModel { get; } = App.ExportProvider.GetExportedValue<AssemblyTreeModel>();
protected static AssemblyTreeModel AssemblyTreeModel { get; } = App.ExportProvider.GetExportedValue<AssemblyTreeModel>();
public static ICollection<IResourceNodeFactory> ResourceNodeFactories { get; } = App.ExportProvider.GetExportedValues<IResourceNodeFactory>().ToArray();
protected static ICollection<IResourceNodeFactory> ResourceNodeFactories { get; } = App.ExportProvider.GetExportedValues<IResourceNodeFactory>().ToArray();
public static SettingsService SettingsService { get; } = App.ExportProvider.GetExportedValue<SettingsService>();
protected static SettingsService SettingsService { get; } = App.ExportProvider.GetExportedValue<SettingsService>();
public static LanguageService LanguageService { get; } = App.ExportProvider.GetExportedValue<LanguageService>();
protected static LanguageService LanguageService { get; } = App.ExportProvider.GetExportedValue<LanguageService>();
public static DockWorkspace DockWorkspace { get; } = App.ExportProvider.GetExportedValue<DockWorkspace>();
protected static DockWorkspace DockWorkspace { get; } = App.ExportProvider.GetExportedValue<DockWorkspace>();
public virtual FilterResult Filter(LanguageSettings settings)
{

8
ILSpy/Updates/NotifyOfUpdatesStrategy.cs → ILSpy/Updates/UpdateService.cs

@ -23,11 +23,9 @@ using System.Net.Http; @@ -23,11 +23,9 @@ using System.Net.Http;
using System.Threading.Tasks;
using System.Xml.Linq;
using ICSharpCode.ILSpyX.Settings;
namespace ICSharpCode.ILSpy.Updates
{
internal static class NotifyOfUpdatesStrategy
internal static class UpdateService
{
static readonly Uri UpdateUrl = new Uri("https://ilspy.net/updates.xml");
const string band = "stable";
@ -68,8 +66,8 @@ namespace ICSharpCode.ILSpy.Updates @@ -68,8 +66,8 @@ namespace ICSharpCode.ILSpy.Updates
// perform update check if we never did one before;
// or if the last check wasn't in the past 7 days
if (settings.LastSuccessfulUpdateCheck == null
|| settings.LastSuccessfulUpdateCheck < DateTime.UtcNow.AddDays(-7)
|| settings.LastSuccessfulUpdateCheck > DateTime.UtcNow)
|| settings.LastSuccessfulUpdateCheck < DateTime.UtcNow.AddDays(-7)
|| settings.LastSuccessfulUpdateCheck > DateTime.UtcNow)
{
return await CheckForUpdateInternal(settings).ConfigureAwait(false);
}

52
ILSpy/Util/GlobalUtils.cs

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
// 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;
using System.Diagnostics;
namespace ICSharpCode.ILSpy.Util
{
static class GlobalUtils
{
public static void OpenLink(string link)
{
try
{
Process.Start(new ProcessStartInfo { FileName = link, UseShellExecute = true });
}
catch (Exception)
{
// Process.Start can throw several errors (not all of them documented),
// just ignore all of them.
}
}
public static void ExecuteCommand(string fileName, string arguments)
{
try
{
Process.Start(fileName, arguments);
}
catch (Exception)
{
// Process.Start can throw several errors (not all of them documented),
// just ignore all of them.
}
}
}
}

52
ILSpy/Util/MenuService.cs

@ -22,6 +22,7 @@ using System.Globalization; @@ -22,6 +22,7 @@ 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;
@ -74,28 +75,31 @@ namespace ICSharpCode.ILSpy.Util @@ -74,28 +75,31 @@ namespace ICSharpCode.ILSpy.Util
}
else
{
var command = entry.Value;
var menuItem = new MenuItem {
Command = CommandWrapper.Unwrap(entry.Value),
Command = CommandWrapper.Unwrap(command),
Tag = entry.Metadata?.MenuID,
Header = ResourceHelper.GetString(entry.Metadata?.Header)
};
if (!string.IsNullOrEmpty(entry.Metadata?.MenuIcon))
{
menuItem.Icon = new Image {
Width = 16,
Height = 16,
Source = Images.Load(entry.Value, entry.Metadata.MenuIcon)
Source = Images.Load(command, entry.Metadata.MenuIcon)
};
}
menuItem.IsEnabled = entry.Metadata?.IsEnabled ?? false;
if (entry.Value is ToggleableCommand)
menuItem.InputGestureText = entry.Metadata?.InputGestureText;
if (command is IProvideParameterBinding parameterBinding)
{
menuItem.IsCheckable = true;
menuItem.SetBinding(MenuItem.IsCheckedProperty, new Binding("IsChecked") { Source = entry.Value, Mode = BindingMode.OneWay });
BindingOperations.SetBinding(menuItem, MenuItem.CommandParameterProperty, parameterBinding.ParameterBinding);
}
menuItem.InputGestureText = entry.Metadata?.InputGestureText;
parentMenuItem.Items.Add(menuItem);
}
}
@ -153,23 +157,23 @@ namespace ICSharpCode.ILSpy.Util @@ -153,23 +157,23 @@ namespace ICSharpCode.ILSpy.Util
{
int navigationPos = 0;
int openPos = 1;
var toolbarCommandsByTitle = exportProvider.GetExports<ICommand, IToolbarCommandMetadata>("ToolbarCommand")
var toolbarCommandsByCategory = exportProvider.GetExports<ICommand, IToolbarCommandMetadata>("ToolbarCommand")
.OrderBy(c => c.Metadata?.ToolbarOrder)
.GroupBy(c => c.Metadata?.ToolbarCategory);
foreach (var commandGroup in toolbarCommandsByTitle)
foreach (var commandCategory in toolbarCommandsByCategory)
{
if (commandGroup.Key == nameof(Properties.Resources.Navigation))
if (commandCategory.Key == nameof(Properties.Resources.Navigation))
{
foreach (var command in commandGroup)
foreach (var command in commandCategory)
{
toolBar.Items.Insert(navigationPos++, CreateToolbarItem(command));
openPos++;
}
}
else if (commandGroup.Key == nameof(Properties.Resources.Open))
else if (commandCategory.Key == nameof(Properties.Resources.Open))
{
foreach (var command in commandGroup)
foreach (var command in commandCategory)
{
toolBar.Items.Insert(openPos++, CreateToolbarItem(command));
}
@ -177,13 +181,12 @@ namespace ICSharpCode.ILSpy.Util @@ -177,13 +181,12 @@ namespace ICSharpCode.ILSpy.Util
else
{
toolBar.Items.Add(new Separator());
foreach (var command in commandGroup)
foreach (var command in commandCategory)
{
toolBar.Items.Add(CreateToolbarItem(command));
}
}
}
}
Control CreateMenuItem(TabPageModel pane)
@ -237,19 +240,28 @@ namespace ICSharpCode.ILSpy.Util @@ -237,19 +240,28 @@ namespace ICSharpCode.ILSpy.Util
return menuItem;
}
static Button CreateToolbarItem(IExport<ICommand, IToolbarCommandMetadata> command)
static Button CreateToolbarItem(IExport<ICommand, IToolbarCommandMetadata> commandExport)
{
return new() {
var command = commandExport.Value;
Button toolbarItem = new() {
Style = ThemeManager.Current.CreateToolBarButtonStyle(),
Command = CommandWrapper.Unwrap(command.Value),
ToolTip = Properties.Resources.ResourceManager.GetString(command.Metadata?.ToolTip),
Tag = command.Metadata?.Tag,
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.Value, command.Metadata?.ToolbarIcon)
Source = Images.Load(command, commandExport.Metadata?.ToolbarIcon)
}
};
if (command is IProvideParameterBinding parameterBinding)
{
BindingOperations.SetBinding(toolbarItem, ButtonBase.CommandParameterProperty, parameterBinding.ParameterBinding);
}
return toolbarItem;
}
}
}

48
ILSpy/Util/MessageBus.cs

@ -19,14 +19,20 @@ @@ -19,14 +19,20 @@
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Navigation;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.ViewModels;
using TomsToolbox.Essentials;
#nullable enable
namespace ICSharpCode.ILSpy.Util
{
public static class MessageBus
{
public static void Send<T>(object sender, T e)
public static void Send<T>(object? sender, T e)
where T : EventArgs
{
MessageBus<T>.Send(sender, e);
@ -47,9 +53,9 @@ namespace ICSharpCode.ILSpy.Util @@ -47,9 +53,9 @@ namespace ICSharpCode.ILSpy.Util
remove => subscriptions.Unsubscribe(value);
}
public static void Send(object sender, T e)
public static void Send(object? sender, T e)
{
subscriptions.Raise(sender, e);
subscriptions.Raise(sender!, e);
}
}
@ -79,5 +85,41 @@ namespace ICSharpCode.ILSpy.Util @@ -79,5 +85,41 @@ namespace ICSharpCode.ILSpy.Util
public bool InNewTabPage { get; } = inNewTabPage;
}
public class NavigateToEventArgs(RequestNavigateEventArgs request, bool inNewTabPage = false) : EventArgs
{
public RequestNavigateEventArgs Request { get; } = request;
public bool InNewTabPage { get; } = inNewTabPage;
}
public class AssemblyTreeSelectionChangedEventArgs() : EventArgs;
public class ApplySessionSettingsEventArgs(SessionSettings sessionSettings) : EventArgs
{
public SessionSettings SessionSettings { get; } = sessionSettings;
}
public class MainWindowLoadedEventArgs() : EventArgs;
public class ActiveTabPageChangedEventArgs(ViewState? viewState) : EventArgs
{
public ViewState? ViewState { get; } = viewState;
}
public class ResetLayoutEventArgs : EventArgs;
public class ShowAboutPageEventArgs(TabPageModel tabPage) : EventArgs
{
public TabPageModel TabPage { get; } = tabPage;
}
public class ShowSearchPageEventArgs(string? searchTerm) : EventArgs
{
public string? SearchTerm { get; } = searchTerm;
}
public class CheckIfUpdateAvailableEventArgs(bool notify = false) : EventArgs
{
public bool Notify { get; } = notify;
}
}

15
ILSpy/ViewModels/ManageAssemblyListsViewModel.cs

@ -25,13 +25,16 @@ using System.Windows; @@ -25,13 +25,16 @@ using System.Windows;
using System.Windows.Input;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.Commands;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpyX;
using TomsToolbox.Wpf;
using DelegateCommand = ICSharpCode.ILSpy.Commands.DelegateCommand;
namespace ICSharpCode.ILSpy.ViewModels
{
public class ManageAssemblyListsViewModel : ViewModelBase
public class ManageAssemblyListsViewModel : ObservableObject
{
private readonly AssemblyListManager manager;
private readonly Window parent;
@ -106,13 +109,7 @@ namespace ICSharpCode.ILSpy.ViewModels @@ -106,13 +109,7 @@ namespace ICSharpCode.ILSpy.ViewModels
public string SelectedAssemblyList {
get => selectedAssemblyList;
set {
if (selectedAssemblyList != value)
{
selectedAssemblyList = value;
RaisePropertyChanged();
}
}
set => SetProperty(ref selectedAssemblyList, value);
}
public ICommand NewCommand { get; }

5
ILSpy/ViewModels/PaneModel.cs

@ -21,7 +21,6 @@ using System.ComponentModel; @@ -21,7 +21,6 @@ using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.ILSpy.Docking;
using TomsToolbox.Wpf;
@ -30,9 +29,9 @@ namespace ICSharpCode.ILSpy.ViewModels @@ -30,9 +29,9 @@ namespace ICSharpCode.ILSpy.ViewModels
{
public abstract class PaneModel : ObservableObject
{
private Throttle titleChangeThrottle;
private readonly Throttle titleChangeThrottle;
public static DockWorkspace DockWorkspace => App.ExportProvider.GetExportedValue<DockWorkspace>();
protected static DockWorkspace DockWorkspace => App.ExportProvider.GetExportedValue<DockWorkspace>();
protected PaneModel()
{

62
ILSpy/ViewModels/TabPageModel.cs

@ -17,60 +17,48 @@ @@ -17,60 +17,48 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Composition;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.TextView;
using TomsToolbox.Composition;
using TomsToolbox.Wpf;
#nullable enable
namespace ICSharpCode.ILSpy.ViewModels
{
[Export]
[NonShared]
public class TabPageModel : PaneModel
{
public AssemblyTreeModel AssemblyTreeModel { get; }
public SettingsService SettingsService { get; }
public LanguageService LanguageService { get; }
public IExportProvider ExportProvider { get; }
public TabPageModel(AssemblyTreeModel assemblyTreeModel, SettingsService settingsService, LanguageService languageService)
public TabPageModel(IExportProvider exportProvider)
{
AssemblyTreeModel = assemblyTreeModel;
SettingsService = settingsService;
LanguageService = languageService;
this.Title = Properties.Resources.NewTab;
ExportProvider = exportProvider;
Title = Properties.Resources.NewTab;
}
private bool supportsLanguageSwitching = true;
public bool SupportsLanguageSwitching {
get => supportsLanguageSwitching;
set {
if (supportsLanguageSwitching != value)
{
supportsLanguageSwitching = value;
OnPropertyChanged(nameof(SupportsLanguageSwitching));
}
}
set => SetProperty(ref supportsLanguageSwitching, value);
}
private object content;
private object? content;
public object Content {
public object? Content {
get => content;
set {
if (content != value)
{
content = value;
OnPropertyChanged(nameof(Content));
}
}
set => SetProperty(ref content, value);
}
public ViewState GetState()
public ViewState? GetState()
{
return (Content as IHaveState)?.GetState();
}
@ -80,9 +68,9 @@ namespace ICSharpCode.ILSpy.ViewModels @@ -80,9 +68,9 @@ namespace ICSharpCode.ILSpy.ViewModels
{
public static Task<T> ShowTextViewAsync<T>(this TabPageModel tabPage, Func<DecompilerTextView, Task<T>> action)
{
if (!(tabPage.Content is DecompilerTextView textView))
if (tabPage.Content is not DecompilerTextView textView)
{
textView = new DecompilerTextView(tabPage);
textView = new DecompilerTextView(tabPage.ExportProvider);
tabPage.Content = textView;
}
tabPage.Title = Properties.Resources.Decompiling;
@ -91,9 +79,9 @@ namespace ICSharpCode.ILSpy.ViewModels @@ -91,9 +79,9 @@ namespace ICSharpCode.ILSpy.ViewModels
public static Task ShowTextViewAsync(this TabPageModel tabPage, Func<DecompilerTextView, Task> action)
{
if (!(tabPage.Content is DecompilerTextView textView))
if (tabPage.Content is not DecompilerTextView textView)
{
textView = new DecompilerTextView(tabPage);
textView = new DecompilerTextView(tabPage.ExportProvider);
tabPage.Content = textView;
}
string oldTitle = tabPage.Title;
@ -113,9 +101,9 @@ namespace ICSharpCode.ILSpy.ViewModels @@ -113,9 +101,9 @@ namespace ICSharpCode.ILSpy.ViewModels
public static void ShowTextView(this TabPageModel tabPage, Action<DecompilerTextView> action)
{
if (!(tabPage.Content is DecompilerTextView textView))
if (tabPage.Content is not DecompilerTextView textView)
{
textView = new DecompilerTextView(tabPage);
textView = new DecompilerTextView(tabPage.ExportProvider);
tabPage.Content = textView;
}
string oldTitle = tabPage.Title;
@ -142,12 +130,16 @@ namespace ICSharpCode.ILSpy.ViewModels @@ -142,12 +130,16 @@ namespace ICSharpCode.ILSpy.ViewModels
public static DecompilationOptions CreateDecompilationOptions(this TabPageModel tabPage)
{
return new(tabPage.LanguageService.LanguageVersion, tabPage.SettingsService.DecompilerSettings, tabPage.SettingsService.DisplaySettings) { Progress = tabPage.Content as IProgress<DecompilationProgress> };
var exportProvider = tabPage.ExportProvider;
var languageService = exportProvider.GetExportedValue<LanguageService>();
var settingsService = exportProvider.GetExportedValue<SettingsService>();
return new(languageService.LanguageVersion, settingsService.DecompilerSettings, settingsService.DisplaySettings) { Progress = tabPage.Content as IProgress<DecompilationProgress> };
}
}
public interface IHaveState
{
ViewState GetState();
ViewState? GetState();
}
}

100
ILSpy/ViewModels/UpdatePanelViewModel.cs

@ -0,0 +1,100 @@ @@ -0,0 +1,100 @@
// 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.Threading.Tasks;
using System.Windows.Input;
using ICSharpCode.ILSpy.Updates;
using TomsToolbox.Wpf;
namespace ICSharpCode.ILSpy.ViewModels;
[Export]
[NonShared]
public class UpdatePanelViewModel : ObservableObject
{
bool isPanelVisible;
string updateAvailableDownloadUrl;
readonly SettingsService settingsService;
public UpdatePanelViewModel(SettingsService settingsService)
{
this.settingsService = settingsService;
MessageBus<CheckIfUpdateAvailableEventArgs>.Subscribers += (_, e) => CheckIfUpdatesAvailableAsync(e.Notify).IgnoreExceptions();
}
public bool IsPanelVisible {
get => isPanelVisible;
set => SetProperty(ref isPanelVisible, value);
}
public string UpdateAvailableDownloadUrl {
get => updateAvailableDownloadUrl;
set => SetProperty(ref updateAvailableDownloadUrl, value);
}
public ICommand CloseCommand => new DelegateCommand(() => IsPanelVisible = false);
public ICommand DownloadOrCheckUpdateCommand => new DelegateCommand(DownloadOrCheckUpdate);
[PropertyDependency(nameof(UpdateAvailableDownloadUrl))]
public string ButtonText => UpdateAvailableDownloadUrl != null
? Properties.Resources.Download
: Properties.Resources.CheckAgain;
[PropertyDependency(nameof(UpdateAvailableDownloadUrl))]
public string Message => UpdateAvailableDownloadUrl != null
? Properties.Resources.ILSpyVersionAvailable
: Properties.Resources.UpdateILSpyFound;
async Task CheckIfUpdatesAvailableAsync(bool notify = false)
{
var settings = settingsService.GetSettings<UpdateSettings>();
string downloadUrl = notify
? await UpdateService.CheckForUpdatesAsync(settings)
: await UpdateService.CheckForUpdatesIfEnabledAsync(settings);
AdjustUpdateUIAfterCheck(downloadUrl, notify);
}
async void DownloadOrCheckUpdate()
{
if (updateAvailableDownloadUrl != null)
{
GlobalUtils.OpenLink(updateAvailableDownloadUrl);
}
else
{
IsPanelVisible = false;
string downloadUrl = await UpdateService.CheckForUpdatesAsync(settingsService.GetSettings<UpdateSettings>());
AdjustUpdateUIAfterCheck(downloadUrl, true);
}
}
void AdjustUpdateUIAfterCheck(string downloadUrl, bool notify)
{
UpdateAvailableDownloadUrl = downloadUrl;
IsPanelVisible = notify;
}
}

22
ILSpy/Views/UpdatePanel.xaml

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
<UserControl x:Class="ICSharpCode.ILSpy.Views.UpdatePanel"
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:viewModels="clr-namespace:ICSharpCode.ILSpy.ViewModels"
xmlns:toms="urn:TomsToolbox"
mc:Ignorable="d" d:DesignWidth="500" d:DataContext="{d:DesignInstance viewModels:UpdatePanelViewModel}">
<Border BorderBrush="Black" BorderThickness="1" Visibility="{Binding IsPanelVisible, Converter={toms:BooleanToVisibilityConverter} }">
<DockPanel KeyboardNavigation.TabNavigation="Contained">
<Button DockPanel.Dock="Right" MinWidth="0" Command="{Binding CloseCommand}" Content="X" />
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="4,0" VerticalAlignment="Center"
Text="{Binding Message}" />
<Button Content="{Binding ButtonText}"
Command="{Binding DownloadOrCheckUpdateCommand}"/>
</StackPanel>
</StackPanel>
</DockPanel>
</Border>
</UserControl>

24
ILSpy/ViewModels/ViewModelBase.cs → ILSpy/Views/UpdatePanel.xaml.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team
// 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
@ -16,18 +16,24 @@ @@ -16,18 +16,24 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Composition;
namespace ICSharpCode.ILSpy.ViewModels
using ICSharpCode.ILSpy.ViewModels;
using TomsToolbox.Wpf.Composition.AttributedModel;
namespace ICSharpCode.ILSpy.Views
{
public abstract class ViewModelBase : INotifyPropertyChanged
/// <summary>
/// Interaction logic for UpdatePanel.xaml
/// </summary>
[DataTemplate(typeof(UpdatePanelViewModel))]
[NonShared]
public partial class UpdatePanel
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
public UpdatePanel()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
InitializeComponent();
}
}
}
Loading…
Cancel
Save