diff --git a/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs b/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs
index ce9e62a1e..8d08b413e 100644
--- a/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs
+++ b/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs
@@ -38,12 +38,14 @@ namespace ICSharpCode.ILSpy.ReadyToRun
 		private readonly ITextOutput output;
 		private readonly ReadyToRunReader reader;
 		private readonly RuntimeFunction runtimeFunction;
+		private readonly SettingsService settingsService;
 
-		public ReadyToRunDisassembler(ITextOutput output, ReadyToRunReader reader, RuntimeFunction runtimeFunction)
+		public ReadyToRunDisassembler(ITextOutput output, ReadyToRunReader reader, RuntimeFunction runtimeFunction, SettingsService settingsService)
 		{
 			this.output = output;
 			this.reader = reader;
 			this.runtimeFunction = runtimeFunction;
+			this.settingsService = settingsService;
 		}
 
 		public void Disassemble(PEFile currentFile, int bitness, ulong address, bool showMetadataTokens, bool showMetadataTokensInBase10)
@@ -51,7 +53,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun
 			ReadyToRunMethod readyToRunMethod = runtimeFunction.Method;
 			WriteCommentLine(readyToRunMethod.SignatureString);
 
-			var options = SettingsService.Instance.GetSettings<ReadyToRunOptions>();
+			var options = settingsService.GetSettings<ReadyToRunOptions>();
 
 			if (options.IsShowGCInfo)
 			{
diff --git a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs
index 78e478861..26a3dc0df 100644
--- a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs
+++ b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs
@@ -38,6 +38,10 @@ using ICSharpCode.ILSpyX;
 
 using ILCompiler.Reflection.ReadyToRun;
 
+using TomsToolbox.Composition;
+
+using MetadataReader = System.Reflection.Metadata.MetadataReader;
+
 namespace ICSharpCode.ILSpy.ReadyToRun
 {
 #if STRESS
@@ -97,7 +101,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun
 
 	[Export(typeof(Language))]
 	[Shared]
-	internal class ReadyToRunLanguage : Language
+	internal class ReadyToRunLanguage(SettingsService settingsService, IExportProvider exportProvider) : Language
 	{
 		private static readonly ConditionalWeakTable<MetadataFile, ReadyToRunReaderCacheEntry> readyToRunReaders = new ConditionalWeakTable<MetadataFile, ReadyToRunReaderCacheEntry>();
 
@@ -175,7 +179,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun
 							.GroupBy(m => m.MethodHandle)
 							.ToDictionary(g => g.Key, g => g.ToArray());
 				}
-				var displaySettings = SettingsService.Instance.DisplaySettings;
+				var displaySettings = settingsService.DisplaySettings;
 				bool showMetadataTokens = displaySettings.ShowMetadataTokens;
 				bool showMetadataTokensInBase10 = displaySettings.ShowMetadataTokensInBase10;
 #if STRESS
@@ -205,7 +209,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun
 								file = ((IlSpyAssemblyMetadata)readyToRunMethod.ComponentReader).Module;
 							}
 
-							new ReadyToRunDisassembler(output, disassemblingReader, runtimeFunction).Disassemble(file, bitness, (ulong)runtimeFunction.StartAddress, showMetadataTokens, showMetadataTokensInBase10);
+							new ReadyToRunDisassembler(output, disassemblingReader, runtimeFunction, settingsService).Disassemble(file, bitness, (ulong)runtimeFunction.StartAddress, showMetadataTokens, showMetadataTokensInBase10);
 						}
 					}
 				}
@@ -218,7 +222,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun
 
 		public override RichText GetRichTextTooltip(IEntity entity)
 		{
-			return LanguageService.Instance.ILLanguage.GetRichTextTooltip(entity);
+			return exportProvider.GetExportedValue<LanguageService>().ILLanguage.GetRichTextTooltip(entity);
 		}
 
 		private ReadyToRunReaderCacheEntry GetReader(LoadedAssembly assembly, MetadataFile file)
diff --git a/ILSpy.Tests/Analyzers/MemberImplementsInterfaceAnalyzerTests.cs b/ILSpy.Tests/Analyzers/MemberImplementsInterfaceAnalyzerTests.cs
index 7b6f77c28..1149418e8 100644
--- a/ILSpy.Tests/Analyzers/MemberImplementsInterfaceAnalyzerTests.cs
+++ b/ILSpy.Tests/Analyzers/MemberImplementsInterfaceAnalyzerTests.cs
@@ -145,7 +145,7 @@ namespace ICSharpCode.ILSpy.Tests.Analyzers
 			var analyzer = new MemberImplementsInterfaceAnalyzer();
 
 			// Act
-			var results = analyzer.Analyze(symbol, new AnalyzerContext() { AssemblyList = new ILSpyX.AssemblyList(), Language = new CSharpLanguage([]) });
+			var results = analyzer.Analyze(symbol, new AnalyzerContext() { AssemblyList = new ILSpyX.AssemblyList(), Language = new CSharpLanguage() });
 
 			// Assert
 			Assert.That(results, Is.Not.Null);
diff --git a/ILSpy.Tests/Analyzers/MethodUsesAnalyzerTests.cs b/ILSpy.Tests/Analyzers/MethodUsesAnalyzerTests.cs
index 79bbbee7d..033deaa1d 100644
--- a/ILSpy.Tests/Analyzers/MethodUsesAnalyzerTests.cs
+++ b/ILSpy.Tests/Analyzers/MethodUsesAnalyzerTests.cs
@@ -30,7 +30,7 @@ namespace ICSharpCode.ILSpy.Tests.Analyzers
 			testAssembly = assemblyList.OpenAssembly(typeof(MethodUsesAnalyzerTests).Assembly.Location);
 			assemblyList.OpenAssembly(typeof(void).Assembly.Location);
 			testAssemblyTypeSystem = testAssembly.GetTypeSystemOrNull();
-			language = new CSharpLanguage([]);
+			language = new CSharpLanguage();
 			typeDefinition = testAssemblyTypeSystem.FindType(typeof(TestCases.Main.MainAssembly)).GetDefinition();
 		}
 
diff --git a/ILSpy.Tests/Analyzers/TypeUsedByAnalyzerTests.cs b/ILSpy.Tests/Analyzers/TypeUsedByAnalyzerTests.cs
index 2a47b84ac..972cb6fc7 100644
--- a/ILSpy.Tests/Analyzers/TypeUsedByAnalyzerTests.cs
+++ b/ILSpy.Tests/Analyzers/TypeUsedByAnalyzerTests.cs
@@ -41,7 +41,7 @@ namespace ICSharpCode.ILSpy.Tests.Analyzers
 			assemblyList = new AssemblyList();
 			testAssembly = assemblyList.OpenAssembly(typeof(MethodUsesAnalyzerTests).Assembly.Location);
 			testAssemblyTypeSystem = new DecompilerTypeSystem(testAssembly.GetMetadataFileOrNull(), testAssembly.GetAssemblyResolver());
-			language = new CSharpLanguage([]);
+			language = new CSharpLanguage();
 		}
 
 		[Test]
diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs
index 5683b253d..525d9b892 100644
--- a/ILSpy/AboutPage.cs
+++ b/ILSpy/AboutPage.cs
@@ -17,6 +17,7 @@
 // DEALINGS IN THE SOFTWARE.
 
 using System;
+using System.Collections.Generic;
 using System.Composition;
 using System.IO;
 using System.Text.RegularExpressions;
@@ -28,6 +29,7 @@ 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;
@@ -38,17 +40,22 @@ namespace ICSharpCode.ILSpy
 {
 	[ExportMainMenuCommand(ParentMenuID = nameof(Resources._Help), Header = nameof(Resources._About), MenuOrder = 99999)]
 	[Shared]
-	sealed class AboutPage : SimpleCommand
+	public sealed class AboutPageCommand(AssemblyTreeModel assemblyTreeModel) : SimpleCommand
 	{
 		public override void Execute(object parameter)
 		{
-			MainWindow.Instance.AssemblyTreeModel.NavigateTo(
+			assemblyTreeModel.NavigateTo(
 				new RequestNavigateEventArgs(new Uri("resource://aboutpage"), null),
 				inNewTabPage: true
 			);
 		}
+	}
 
-		public static void Display(DecompilerTextView textView)
+	[Export]
+	[Shared]
+	public sealed class AboutPage(IEnumerable<IAboutPageAddition> aboutPageAdditions)
+	{
+		public void Display(DecompilerTextView textView)
 		{
 			AvalonEditTextOutput output = new AvalonEditTextOutput() {
 				Title = Resources.About,
@@ -86,7 +93,7 @@ namespace ICSharpCode.ILSpy
 			});
 			output.WriteLine();
 
-			foreach (var plugin in App.ExportProvider.GetExportedValues<IAboutPageAddition>())
+			foreach (var plugin in aboutPageAdditions)
 				plugin.Write(output);
 			output.WriteLine();
 			output.Address = new Uri("resource://AboutPage");
diff --git a/ILSpy/Analyzers/AnalyzeCommand.cs b/ILSpy/Analyzers/AnalyzeCommand.cs
index e29dacb23..30c5a9ee7 100644
--- a/ILSpy/Analyzers/AnalyzeCommand.cs
+++ b/ILSpy/Analyzers/AnalyzeCommand.cs
@@ -20,17 +20,18 @@ using System.Composition;
 using System.Linq;
 
 using ICSharpCode.Decompiler.TypeSystem;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.Properties;
 using ICSharpCode.ILSpy.TreeNodes;
 
+using TomsToolbox.Composition;
+
 namespace ICSharpCode.ILSpy.Analyzers
 {
 	[ExportContextMenuEntry(Header = nameof(Resources.Analyze), Icon = "Images/Search", Category = nameof(Resources.Analyze), InputGestureText = "Ctrl+R", Order = 100)]
 	[Shared]
-	internal sealed class AnalyzeContextMenuCommand : IContextMenuEntry
+	internal sealed class AnalyzeContextMenuCommand(AnalyzerTreeViewModel analyzerTreeView) : IContextMenuEntry
 	{
-		private static readonly AnalyzerTreeViewModel AnalyzerTreeView = App.ExportProvider.GetExportedValue<AnalyzerTreeViewModel>();
-
 		public bool IsVisible(TextViewContext context)
 		{
 			if (context.TreeView is AnalyzerTreeView && context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n.Parent.IsRoot))
@@ -62,30 +63,28 @@ namespace ICSharpCode.ILSpy.Analyzers
 			{
 				foreach (var node in context.SelectedTreeNodes.OfType<IMemberTreeNode>().ToArray())
 				{
-					AnalyzerTreeView.Analyze(node.Member);
+					analyzerTreeView.Analyze(node.Member);
 				}
 			}
 			else if (context.Reference is { Reference: IEntity entity })
 			{
-				AnalyzerTreeView.Analyze(entity);
+				analyzerTreeView.Analyze(entity);
 			}
 		}
 	}
 
-	internal sealed class AnalyzeCommand : SimpleCommand
+	public sealed class AnalyzeCommand(AssemblyTreeModel assemblyTreeModel, AnalyzerTreeViewModel analyzerTreeViewModel) : SimpleCommand
 	{
-		private static readonly AnalyzerTreeViewModel AnalyzerTreeView = App.ExportProvider.GetExportedValue<AnalyzerTreeViewModel>();
-
 		public override bool CanExecute(object parameter)
 		{
-			return MainWindow.Instance.AssemblyTreeModel.SelectedNodes.All(n => n is IMemberTreeNode);
+			return assemblyTreeModel.SelectedNodes.All(n => n is IMemberTreeNode);
 		}
 
 		public override void Execute(object parameter)
 		{
-			foreach (var node in MainWindow.Instance.AssemblyTreeModel.SelectedNodes.OfType<IMemberTreeNode>())
+			foreach (var node in assemblyTreeModel.SelectedNodes.OfType<IMemberTreeNode>())
 			{
-				AnalyzerTreeView.Analyze(node.Member);
+				analyzerTreeViewModel.Analyze(node.Member);
 			}
 		}
 	}
diff --git a/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs b/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs
index 7c3d484bc..8609fc674 100644
--- a/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs
+++ b/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs
@@ -61,7 +61,7 @@ namespace ICSharpCode.ILSpy.Analyzers
 				var context = new AnalyzerContext() {
 					CancellationToken = ct,
 					Language = Language,
-					AssemblyList = MainWindow.Instance.AssemblyTreeModel.AssemblyList
+					AssemblyList = AssemblyTreeModel.AssemblyList
 				};
 				var results = analyzer.Analyze(symbol, context).Select(SymbolTreeNodeFactory);
 				if (context.SortResults)
diff --git a/ILSpy/Analyzers/AnalyzerTreeNode.cs b/ILSpy/Analyzers/AnalyzerTreeNode.cs
index d2cffeb49..2775634a6 100644
--- a/ILSpy/Analyzers/AnalyzerTreeNode.cs
+++ b/ILSpy/Analyzers/AnalyzerTreeNode.cs
@@ -17,15 +17,20 @@
 // DEALINGS IN THE SOFTWARE.
 
 using System.Collections.Generic;
+using System.Linq;
 
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpyX;
+using ICSharpCode.ILSpyX.Analyzers;
 using ICSharpCode.ILSpyX.TreeView;
 
+using TomsToolbox.Composition;
+
 namespace ICSharpCode.ILSpy.Analyzers
 {
 	public abstract class AnalyzerTreeNode : SharpTreeNode
 	{
-		public Language Language => LanguageService.Instance.Language;
+		public static Language Language => App.ExportProvider.GetExportedValue<LanguageService>().Language;
 
 		public override bool CanDelete()
 		{
@@ -42,6 +47,13 @@ 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)
+			.ToArray();
+
 		/// <summary>
 		/// Handles changes to the assembly list.
 		/// </summary>
diff --git a/ILSpy/Analyzers/AnalyzerTreeViewModel.cs b/ILSpy/Analyzers/AnalyzerTreeViewModel.cs
index ce45558b9..c325d0151 100644
--- a/ILSpy/Analyzers/AnalyzerTreeViewModel.cs
+++ b/ILSpy/Analyzers/AnalyzerTreeViewModel.cs
@@ -24,6 +24,7 @@ using System.Windows.Input;
 
 using ICSharpCode.Decompiler.TypeSystem;
 using ICSharpCode.ILSpy.Analyzers.TreeNodes;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.TreeNodes;
 using ICSharpCode.ILSpy.ViewModels;
 
@@ -38,12 +39,12 @@ namespace ICSharpCode.ILSpy.Analyzers
 	{
 		public const string PaneContentId = "analyzerPane";
 
-		public AnalyzerTreeViewModel()
+		public AnalyzerTreeViewModel(AssemblyTreeModel assemblyTreeModel)
 		{
 			ContentId = PaneContentId;
 			Title = Properties.Resources.Analyze;
 			ShortcutKey = new(Key.R, ModifierKeys.Control);
-			AssociatedCommand = ILSpyCommands.Analyze;
+			AssociatedCommand = new AnalyzeCommand(assemblyTreeModel, this);
 		}
 
 		public AnalyzerRootNode Root { get; } = new();
diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs
index 8b6ff6f03..f8fccd112 100644
--- a/ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs
+++ b/ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs
@@ -17,15 +17,12 @@
 // DEALINGS IN THE SOFTWARE.
 
 using System;
-using System.Linq;
 
+using ICSharpCode.Decompiler.TypeSystem;
 using ICSharpCode.ILSpy.TreeNodes;
 
 namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
 {
-	using ICSharpCode.Decompiler.TypeSystem;
-	using ICSharpCode.ILSpyX.Analyzers;
-
 	internal sealed class AnalyzedEventTreeNode : AnalyzerEntityTreeNode
 	{
 		readonly IEvent analyzedEvent;
@@ -54,16 +51,12 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
 			if (TryFindBackingField(analyzedEvent, out var backingField))
 				this.Children.Add(new AnalyzedFieldTreeNode(backingField));
 
-			//foreach (var accessor in analyzedEvent.OtherMethods)
-			//	this.Children.Add(new AnalyzedAccessorTreeNode(accessor, null));
-
-			var analyzers = App.ExportProvider.GetExports<IAnalyzer, IAnalyzerMetadata>("Analyzer");
-			foreach (var lazy in analyzers.OrderBy(item => item.Metadata.Order))
+			foreach (var lazy in Analyzers)
 			{
 				var analyzer = lazy.Value;
 				if (analyzer.Show(analyzedEvent))
 				{
-					this.Children.Add(new AnalyzerSearchTreeNode(analyzedEvent, analyzer, lazy.Metadata.Header));
+					this.Children.Add(new AnalyzerSearchTreeNode(analyzedEvent, analyzer, lazy.Metadata?.Header));
 				}
 			}
 		}
@@ -73,7 +66,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
 			backingField = null;
 			foreach (var field in analyzedEvent.DeclaringTypeDefinition.GetFields(options: GetMemberOptions.IgnoreInheritedMembers))
 			{
-				if (field.Name == analyzedEvent.Name && field.Accessibility == Accessibility.Private)
+				if (field.Name == analyzedEvent.Name && field.Accessibility == Decompiler.TypeSystem.Accessibility.Private)
 				{
 					backingField = field;
 					return true;
diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs
index 1f2b9b9a3..7317841d1 100644
--- a/ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs
+++ b/ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs
@@ -17,11 +17,9 @@
 // DEALINGS IN THE SOFTWARE.
 
 using System;
-using System.Linq;
 
 using ICSharpCode.Decompiler.TypeSystem;
 using ICSharpCode.ILSpy.TreeNodes;
-using ICSharpCode.ILSpyX.Analyzers;
 
 namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
 {
@@ -41,13 +39,12 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
 
 		protected override void LoadChildren()
 		{
-			var analyzers = App.ExportProvider.GetExports<IAnalyzer, IAnalyzerMetadata>("Analyzer");
-			foreach (var lazy in analyzers.OrderBy(item => item.Metadata.Order))
+			foreach (var lazy in Analyzers)
 			{
 				var analyzer = lazy.Value;
 				if (analyzer.Show(analyzedField))
 				{
-					this.Children.Add(new AnalyzerSearchTreeNode(analyzedField, analyzer, lazy.Metadata.Header));
+					this.Children.Add(new AnalyzerSearchTreeNode(analyzedField, analyzer, lazy.Metadata?.Header));
 				}
 			}
 		}
diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs
index b7cee8ede..283b30a3f 100644
--- a/ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs
+++ b/ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs
@@ -17,11 +17,9 @@
 // DEALINGS IN THE SOFTWARE.
 
 using System;
-using System.Linq;
 
 using ICSharpCode.Decompiler.TypeSystem;
 using ICSharpCode.ILSpy.TreeNodes;
-using ICSharpCode.ILSpyX.Analyzers;
 
 namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
 {
@@ -43,8 +41,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
 
 		protected override void LoadChildren()
 		{
-			var analyzers = App.ExportProvider.GetExports<IAnalyzer, IAnalyzerMetadata>("Analyzer");
-			foreach (var lazy in analyzers.OrderBy(item => item.Metadata.Order))
+			foreach (var lazy in Analyzers)
 			{
 				var analyzer = lazy.Value;
 				if (analyzer.Show(analyzedMethod))
diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs
index 7f343fb78..a85096147 100644
--- a/ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs
+++ b/ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs
@@ -17,11 +17,9 @@
 // DEALINGS IN THE SOFTWARE.
 
 using System;
-using System.Linq;
 using System.Windows;
 
 using ICSharpCode.Decompiler.TypeSystem;
-using ICSharpCode.ILSpyX.Analyzers;
 using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions;
 
 namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
@@ -42,8 +40,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
 
 		protected override void LoadChildren()
 		{
-			var analyzers = App.ExportProvider.GetExports<IAnalyzer, IAnalyzerMetadata>("Analyzer");
-			foreach (var lazy in analyzers.OrderBy(item => item.Metadata.Order))
+			foreach (var lazy in Analyzers)
 			{
 				var analyzer = lazy.Value;
 				if (analyzer.Show(analyzedModule))
diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs
index d19bc3e73..7a4d2fdfa 100644
--- a/ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs
+++ b/ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs
@@ -17,11 +17,9 @@
 // DEALINGS IN THE SOFTWARE.
 
 using System;
-using System.Linq;
 
 using ICSharpCode.Decompiler.TypeSystem;
 using ICSharpCode.ILSpy.TreeNodes;
-using ICSharpCode.ILSpyX.Analyzers;
 
 namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
 {
@@ -48,11 +46,8 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
 				this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Getter, "get"));
 			if (analyzedProperty.CanSet)
 				this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Setter, "set"));
-			//foreach (var accessor in analyzedProperty.OtherMethods)
-			//	this.Children.Add(new AnalyzedPropertyAccessorTreeNode(accessor, null));
 
-			var analyzers = App.ExportProvider.GetExports<IAnalyzer, IAnalyzerMetadata>("Analyzer");
-			foreach (var lazy in analyzers.OrderBy(item => item.Metadata.Order))
+			foreach (var lazy in Analyzers)
 			{
 				var analyzer = lazy.Value;
 				if (analyzer.Show(analyzedProperty))
diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs
index 6836944eb..cb3ccc609 100644
--- a/ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs
+++ b/ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs
@@ -17,11 +17,9 @@
 // DEALINGS IN THE SOFTWARE.
 
 using System;
-using System.Linq;
 
 using ICSharpCode.Decompiler.TypeSystem;
 using ICSharpCode.ILSpy.TreeNodes;
-using ICSharpCode.ILSpyX.Analyzers;
 
 namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
 {
@@ -41,8 +39,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
 
 		protected override void LoadChildren()
 		{
-			var analyzers = App.ExportProvider.GetExports<IAnalyzer, IAnalyzerMetadata>("Analyzer");
-			foreach (var lazy in analyzers.OrderBy(item => item.Metadata.Order))
+			foreach (var lazy in Analyzers)
 			{
 				var analyzer = lazy.Value;
 				if (analyzer.Show(analyzedType))
diff --git a/ILSpy/App.xaml.cs b/ILSpy/App.xaml.cs
index 0ae19f9e1..05c077751 100644
--- a/ILSpy/App.xaml.cs
+++ b/ILSpy/App.xaml.cs
@@ -71,8 +71,10 @@ namespace ICSharpCode.ILSpy
 			var cmdArgs = Environment.GetCommandLineArgs().Skip(1);
 			CommandLineArguments = CommandLineArguments.Create(cmdArgs);
 
+			var settingsService = new SettingsService();
+
 			bool forceSingleInstance = (CommandLineArguments.SingleInstance ?? true)
-									   && !SettingsService.Instance.MiscSettings.AllowMultipleInstances;
+									   && !settingsService.MiscSettings.AllowMultipleInstances;
 			if (forceSingleInstance)
 			{
 				SingleInstance.Attach();  // will auto-exit for second instance
@@ -81,7 +83,7 @@ namespace ICSharpCode.ILSpy
 
 			InitializeComponent();
 
-			if (!InitializeDependencyInjection(SettingsService.Instance))
+			if (!InitializeDependencyInjection(settingsService))
 			{
 				// There is something completely wrong with DI, probably some service registration is missing => nothing we can do to recover, so stop and shut down.
 				Exit += (_, _) => MessageBox.Show(StartupExceptions.FormatExceptions(), "Sorry we crashed!");
@@ -106,7 +108,7 @@ namespace ICSharpCode.ILSpy
 			// Add data templates registered via MEF.
 			Resources.MergedDictionaries.Add(DataTemplateManager.CreateDynamicDataTemplates(ExportProvider));
 
-			var sessionSettings = SettingsService.Instance.SessionSettings;
+			var sessionSettings = settingsService.SessionSettings;
 			ThemeManager.Current.Theme = sessionSettings.Theme;
 			if (!string.IsNullOrEmpty(sessionSettings.CurrentCulture))
 			{
@@ -129,7 +131,7 @@ namespace ICSharpCode.ILSpy
 				MessageBox.Show(unknownArguments, "ILSpy Unknown Command Line Arguments Passed");
 			}
 
-			SettingsService.Instance.AssemblyListManager.CreateDefaultAssemblyLists();
+			settingsService.AssemblyListManager.CreateDefaultAssemblyLists();
 		}
 
 		public new static App Current => (App)Application.Current;
@@ -187,6 +189,10 @@ namespace ICSharpCode.ILSpy
 				services.BindExports(Assembly.GetExecutingAssembly());
 				// Add the settings service
 				services.AddSingleton(settingsService);
+				// Add the export provider
+				services.AddSingleton(_ => ExportProvider);
+				// Add the docking manager
+				services.AddSingleton(serviceProvider => serviceProvider.GetService<MainWindow>().DockManager);
 
 				var serviceProvider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true });
 
@@ -209,7 +215,7 @@ namespace ICSharpCode.ILSpy
 		{
 			base.OnStartup(e);
 
-			MainWindow = new();
+			MainWindow = ExportProvider.GetExportedValue<MainWindow>();
 			MainWindow.Show();
 		}
 
diff --git a/ILSpy/AssemblyTree/AssemblyTreeModel.cs b/ILSpy/AssemblyTree/AssemblyTreeModel.cs
index 954310192..59d3ba47e 100644
--- a/ILSpy/AssemblyTree/AssemblyTreeModel.cs
+++ b/ILSpy/AssemblyTree/AssemblyTreeModel.cs
@@ -47,6 +47,7 @@ using ICSharpCode.ILSpyX;
 using ICSharpCode.ILSpyX.Settings;
 using ICSharpCode.ILSpyX.TreeView;
 
+using TomsToolbox.Composition;
 using TomsToolbox.Essentials;
 using TomsToolbox.Wpf;
 
@@ -67,9 +68,20 @@ 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()
+		public AssemblyTreeModel(AboutPage aboutPage, SearchPaneModel searchPaneModel, SettingsService settingsService, LanguageService languageService, IExportProvider exportProvider)
 		{
+			this.aboutPage = aboutPage;
+			this.searchPaneModel = searchPaneModel;
+			this.settingsService = settingsService;
+			this.languageService = languageService;
+			this.exportProvider = exportProvider;
+
 			Title = Resources.Assemblies;
 			ContentId = PaneContentId;
 			IsCloseable = false;
@@ -80,7 +92,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 
 			refreshThrottle = new DispatcherThrottle(DispatcherPriority.Background, RefreshInternal);
 
-			AssemblyList = SettingsService.Instance.CreateEmptyAssemblyList();
+			AssemblyList = settingsService.CreateEmptyAssemblyList();
 		}
 
 		private void Settings_PropertyChanged(object? sender, PropertyChangedEventArgs e)
@@ -151,7 +163,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 		{
 			LoadAssemblies(args.AssembliesToLoad, commandLineLoadedAssemblies, focusNode: false);
 			if (args.Language != null)
-				LanguageService.Instance.Language = LanguageService.Instance.GetLanguage(args.Language);
+				languageService.Language = languageService.GetLanguage(args.Language);
 			return true;
 		}
 
@@ -161,7 +173,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 		/// </summary>
 		private async Task HandleCommandLineArgumentsAfterShowList(CommandLineArguments args, ISettingsProvider? spySettings = null)
 		{
-			var sessionSettings = SettingsService.Instance.SessionSettings;
+			var sessionSettings = settingsService.SessionSettings;
 
 			var relevantAssemblies = commandLineLoadedAssemblies.ToList();
 			commandLineLoadedAssemblies.Clear(); // clear references once we don't need them anymore
@@ -170,10 +182,8 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 
 			if (args.Search != null)
 			{
-				var searchPane = App.ExportProvider.GetExportedValue<SearchPaneModel>();
-
-				searchPane.SearchTerm = args.Search;
-				searchPane.Show();
+				this.searchPaneModel.SearchTerm = args.Search;
+				this.searchPaneModel.Show();
 			}
 		}
 
@@ -290,11 +300,11 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 						SelectNode(node);
 
 						// only if not showing the about page, perform the update check:
-						await MainWindow.Instance.ShowMessageIfUpdatesAvailableAsync(spySettings);
+						await App.Current.MainWindow.ShowMessageIfUpdatesAvailableAsync(spySettings);
 					}
 					else
 					{
-						DockWorkspace.Instance.ActiveTabPage.ShowTextView(AboutPage.Display);
+						DockWorkspace.Instance.ActiveTabPage.ShowTextView(aboutPage.Display);
 					}
 				}
 			}
@@ -363,11 +373,11 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 
 		public void Initialize()
 		{
-			AssemblyList = SettingsService.Instance.LoadInitialAssemblyList();
+			AssemblyList = settingsService.LoadInitialAssemblyList();
 
 			HandleCommandLineArguments(App.CommandLineArguments);
 
-			var loadPreviousAssemblies = SettingsService.Instance.MiscSettings.LoadPreviousAssemblies;
+			var loadPreviousAssemblies = settingsService.MiscSettings.LoadPreviousAssemblies;
 			if (AssemblyList.GetAssemblies().Length == 0
 				&& AssemblyList.ListName == AssemblyListManager.DefaultListName
 				&& loadPreviousAssemblies)
@@ -377,7 +387,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 
 			ShowAssemblyList(AssemblyList);
 
-			var sessionSettings = SettingsService.Instance.SessionSettings;
+			var sessionSettings = settingsService.SessionSettings;
 			if (sessionSettings.ActiveAutoLoadedAssembly != null
 				&& File.Exists(sessionSettings.ActiveAutoLoadedAssembly))
 			{
@@ -389,7 +399,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 
 		private async Task OpenAssemblies()
 		{
-			await HandleCommandLineArgumentsAfterShowList(App.CommandLineArguments, SettingsService.Instance.SpySettings);
+			await HandleCommandLineArgumentsAfterShowList(App.CommandLineArguments, settingsService.SpySettings);
 
 			if (FormatExceptions(App.StartupExceptions.ToArray(), out var output))
 			{
@@ -416,7 +426,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 
 		private void ShowAssemblyList(string name)
 		{
-			AssemblyList list = SettingsService.Instance.AssemblyListManager.LoadList(name);
+			AssemblyList list = settingsService.AssemblyListManager.LoadList(name);
 			//Only load a new list when it is a different one
 			if (list.ListName != AssemblyList.ListName)
 			{
@@ -655,8 +665,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 					}
 					if (protocol != "decompile")
 					{
-						var protocolHandlers = App.ExportProvider.GetExportedValues<IProtocolHandler>();
-						foreach (var handler in protocolHandlers)
+						foreach (var handler in exportProvider.GetExportedValues<IProtocolHandler>())
 						{
 							var node = handler.Resolve(protocol, file, unresolvedEntity.Handle, out bool newTabPage);
 							if (node != null)
@@ -793,7 +802,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 				return;
 			}
 
-			var options = LanguageService.Instance.CreateDecompilationOptions(activeTabPage);
+			var options = activeTabPage.CreateDecompilationOptions();
 			options.TextViewState = newState;
 			activeTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(this.CurrentLanguage, this.SelectedNodes, options));
 		}
@@ -803,9 +812,9 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 			DecompileSelectedNodes(DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState);
 		}
 
-		public Language CurrentLanguage => LanguageService.Instance.Language;
+		public Language CurrentLanguage => languageService.Language;
 
-		public LanguageVersion? CurrentLanguageVersion => LanguageService.Instance.LanguageVersion;
+		public LanguageVersion? CurrentLanguageVersion => languageService.LanguageVersion;
 
 		public IEnumerable<ILSpyTreeNode> SelectedNodes {
 			get {
@@ -858,7 +867,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 				if (e.Uri.Host == "aboutpage")
 				{
 					RecordHistory();
-					DockWorkspace.Instance.ActiveTabPage.ShowTextView(AboutPage.Display);
+					DockWorkspace.Instance.ActiveTabPage.ShowTextView(aboutPage.Display);
 					e.Handled = true;
 					return;
 				}
@@ -912,7 +921,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
 			{
 				var path = GetPathForNode(SelectedItem);
 
-				ShowAssemblyList(SettingsService.Instance.AssemblyListManager.LoadList(AssemblyList.ListName));
+				ShowAssemblyList(settingsService.AssemblyListManager.LoadList(AssemblyList.ListName));
 				SelectNode(FindNodeByPath(path, true), inNewTabPage: false);
 
 				RefreshDecompiledView();
diff --git a/ILSpy/Commands/CheckForUpdatesCommand.cs b/ILSpy/Commands/CheckForUpdatesCommand.cs
index 75ab702e7..5ac9b1fc6 100644
--- a/ILSpy/Commands/CheckForUpdatesCommand.cs
+++ b/ILSpy/Commands/CheckForUpdatesCommand.cs
@@ -25,16 +25,11 @@ namespace ICSharpCode.ILSpy
 {
 	[ExportMainMenuCommand(ParentMenuID = nameof(Resources._Help), Header = nameof(Resources._CheckUpdates), MenuOrder = 5000)]
 	[Shared]
-	sealed class CheckForUpdatesCommand : SimpleCommand
+	sealed class CheckForUpdatesCommand(SettingsService settingsService) : SimpleCommand
 	{
-		public override bool CanExecute(object parameter)
-		{
-			return base.CanExecute(parameter);
-		}
-
 		public override async void Execute(object parameter)
 		{
-			await MainWindow.Instance.ShowMessageIfUpdatesAvailableAsync(SettingsService.Instance.SpySettings, forceCheck: true);
+			await App.Current.MainWindow.ShowMessageIfUpdatesAvailableAsync(settingsService.SpySettings, forceCheck: true);
 		}
 	}
 }
diff --git a/ILSpy/Commands/DecompileAllCommand.cs b/ILSpy/Commands/DecompileAllCommand.cs
index 72aae604c..06c093a44 100644
--- a/ILSpy/Commands/DecompileAllCommand.cs
+++ b/ILSpy/Commands/DecompileAllCommand.cs
@@ -27,24 +27,19 @@ using System.Linq;
 using System.Threading.Tasks;
 
 using ICSharpCode.Decompiler;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.Docking;
 using ICSharpCode.ILSpy.Properties;
 using ICSharpCode.ILSpy.TextView;
+using ICSharpCode.ILSpy.ViewModels;
 using ICSharpCode.ILSpyX;
 
 namespace ICSharpCode.ILSpy
 {
 	[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDecompile), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)]
 	[Shared]
-	sealed class DecompileAllCommand : SimpleCommand
+	sealed class DecompileAllCommand(AssemblyTreeModel assemblyTreeModel, LanguageService languageService) : SimpleCommand
 	{
-		private readonly IReadOnlyCollection<IResourceFileHandler> resourceFileHandlers;
-
-		public DecompileAllCommand(IEnumerable<IResourceFileHandler> resourceFileHandlers)
-		{
-			this.resourceFileHandlers = resourceFileHandlers.ToArray();
-		}
-
 		public override bool CanExecute(object parameter)
 		{
 			return System.IO.Directory.Exists("c:\\temp\\decompiled");
@@ -55,7 +50,7 @@ namespace ICSharpCode.ILSpy
 			Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
 				AvalonEditTextOutput output = new AvalonEditTextOutput();
 				Parallel.ForEach(
-					Partitioner.Create(MainWindow.Instance.AssemblyTreeModel.AssemblyList.GetAssemblies(), loadBalance: true),
+					Partitioner.Create(assemblyTreeModel.AssemblyList.GetAssemblies(), loadBalance: true),
 					new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
 					delegate (LoadedAssembly asm) {
 						if (!asm.HasLoadError)
@@ -66,10 +61,10 @@ namespace ICSharpCode.ILSpy
 							{
 								try
 								{
-									var options = LanguageService.Instance.CreateDecompilationOptions(DockWorkspace.Instance.ActiveTabPage);
+									var options = DockWorkspace.Instance.ActiveTabPage.CreateDecompilationOptions();
 									options.CancellationToken = ct;
 									options.FullDecompilation = true;
-									new CSharpLanguage(resourceFileHandlers).DecompileAssembly(asm, new PlainTextOutput(writer), options);
+									new CSharpLanguage().DecompileAssembly(asm, new PlainTextOutput(writer), options);
 								}
 								catch (Exception ex)
 								{
@@ -96,15 +91,15 @@ namespace ICSharpCode.ILSpy
 
 	[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDecompile100x), MenuCategory = nameof(Resources.Open), MenuOrder = 2.6)]
 	[Shared]
-	sealed class Decompile100TimesCommand : SimpleCommand
+	sealed class Decompile100TimesCommand(AssemblyTreeModel assemblyTreeModel, LanguageService languageService) : SimpleCommand
 	{
 		public override void Execute(object parameter)
 		{
 			const int numRuns = 100;
-			var language = LanguageService.Instance.Language;
-			var nodes = MainWindow.Instance.AssemblyTreeModel.SelectedNodes.ToArray();
+			var language = languageService.Language;
+			var nodes = assemblyTreeModel.SelectedNodes.ToArray();
 			DockWorkspace dockWorkspace = DockWorkspace.Instance;
-			var options = LanguageService.Instance.CreateDecompilationOptions(dockWorkspace.ActiveTabPage);
+			var options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions();
 			dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
 				options.CancellationToken = ct;
 				Stopwatch w = Stopwatch.StartNew();
diff --git a/ILSpy/Commands/DisassembleAllCommand.cs b/ILSpy/Commands/DisassembleAllCommand.cs
index fea58925d..8b81e87b2 100644
--- a/ILSpy/Commands/DisassembleAllCommand.cs
+++ b/ILSpy/Commands/DisassembleAllCommand.cs
@@ -24,14 +24,16 @@ using System.Composition;
 using System.Diagnostics;
 using System.Threading.Tasks;
 
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.Properties;
 using ICSharpCode.ILSpy.TextView;
+using ICSharpCode.ILSpy.ViewModels;
 
 namespace ICSharpCode.ILSpy
 {
 	[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDisassemble), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)]
 	[Shared]
-	sealed class DisassembleAllCommand : SimpleCommand
+	sealed class DisassembleAllCommand(AssemblyTreeModel assemblyTreeModel, LanguageService languageService) : SimpleCommand
 	{
 		public override bool CanExecute(object parameter)
 		{
@@ -45,7 +47,7 @@ namespace ICSharpCode.ILSpy
 			dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
 				AvalonEditTextOutput output = new();
 				Parallel.ForEach(
-					Partitioner.Create(MainWindow.Instance.AssemblyTreeModel.AssemblyList.GetAssemblies(), loadBalance: true),
+					Partitioner.Create(assemblyTreeModel.AssemblyList.GetAssemblies(), loadBalance: true),
 					new() { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
 					asm => {
 						if (!asm.HasLoadError)
@@ -56,7 +58,7 @@ namespace ICSharpCode.ILSpy
 							{
 								try
 								{
-									var options = LanguageService.Instance.CreateDecompilationOptions(dockWorkspace.ActiveTabPage);
+									var options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions();
 									options.FullDecompilation = true;
 									options.CancellationToken = ct;
 									new ILLanguage().DecompileAssembly(asm, new Decompiler.PlainTextOutput(writer), options);
diff --git a/ILSpy/Commands/ExitCommand.cs b/ILSpy/Commands/ExitCommand.cs
index 1ba8a34e1..67f698bfe 100644
--- a/ILSpy/Commands/ExitCommand.cs
+++ b/ILSpy/Commands/ExitCommand.cs
@@ -27,7 +27,7 @@ namespace ICSharpCode.ILSpy
 	{
 		public override void Execute(object parameter)
 		{
-			MainWindow.Instance.Close();
+			App.Current.MainWindow.Close();
 		}
 	}
 }
\ No newline at end of file
diff --git a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs
index d31a3160c..63f38d95f 100644
--- a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs
+++ b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs
@@ -29,10 +29,12 @@ using ICSharpCode.Decompiler.CSharp;
 using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
 using ICSharpCode.Decompiler.DebugInfo;
 using ICSharpCode.Decompiler.Metadata;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.Docking;
 using ICSharpCode.ILSpy.Properties;
 using ICSharpCode.ILSpy.TextView;
 using ICSharpCode.ILSpy.TreeNodes;
+using ICSharpCode.ILSpy.ViewModels;
 using ICSharpCode.ILSpyX;
 
 using Microsoft.Win32;
@@ -41,14 +43,14 @@ namespace ICSharpCode.ILSpy
 {
 	[ExportContextMenuEntry(Header = nameof(Resources.GeneratePortable))]
 	[Shared]
-	class GeneratePdbContextMenuEntry : IContextMenuEntry
+	class GeneratePdbContextMenuEntry(LanguageService languageService) : IContextMenuEntry
 	{
 		public void Execute(TextViewContext context)
 		{
 			var assembly = (context.SelectedTreeNodes?.FirstOrDefault() as AssemblyTreeNode)?.LoadedAssembly;
 			if (assembly == null)
 				return;
-			GeneratePdbForAssembly(assembly);
+			GeneratePdbForAssembly(assembly, languageService);
 		}
 
 		public bool IsEnabled(TextViewContext context) => true;
@@ -60,7 +62,7 @@ namespace ICSharpCode.ILSpy
 				&& tn.LoadedAssembly.IsLoadedAsValidAssembly;
 		}
 
-		internal static void GeneratePdbForAssembly(LoadedAssembly assembly)
+		internal static void GeneratePdbForAssembly(LoadedAssembly assembly, LanguageService languageService)
 		{
 			var file = assembly.GetMetadataFileOrNull() as PEFile;
 			if (!PortablePdbWriter.HasCodeViewDebugDirectoryEntry(file))
@@ -75,7 +77,7 @@ namespace ICSharpCode.ILSpy
 			if (dlg.ShowDialog() != true)
 				return;
 			DockWorkspace dockWorkspace = DockWorkspace.Instance;
-			DecompilationOptions options = LanguageService.Instance.CreateDecompilationOptions(dockWorkspace.ActiveTabPage);
+			DecompilationOptions options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions();
 			string fileName = dlg.FileName;
 			dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
 				AvalonEditTextOutput output = new AvalonEditTextOutput();
@@ -108,21 +110,21 @@ namespace ICSharpCode.ILSpy
 
 	[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.GeneratePortable), MenuCategory = nameof(Resources.Save))]
 	[Shared]
-	class GeneratePdbMainMenuEntry : SimpleCommand
+	class GeneratePdbMainMenuEntry(AssemblyTreeModel assemblyTreeModel, LanguageService languageService) : SimpleCommand
 	{
 		public override bool CanExecute(object parameter)
 		{
-			return MainWindow.Instance.AssemblyTreeModel.SelectedNodes?.Count() == 1
-				&& MainWindow.Instance.AssemblyTreeModel.SelectedNodes?.FirstOrDefault() is AssemblyTreeNode tn
+			return assemblyTreeModel.SelectedNodes?.Count() == 1
+				&& assemblyTreeModel.SelectedNodes?.FirstOrDefault() is AssemblyTreeNode tn
 				&& !tn.LoadedAssembly.HasLoadError;
 		}
 
 		public override void Execute(object parameter)
 		{
-			var assembly = (MainWindow.Instance.AssemblyTreeModel.SelectedNodes?.FirstOrDefault() as AssemblyTreeNode)?.LoadedAssembly;
+			var assembly = (assemblyTreeModel.SelectedNodes?.FirstOrDefault() as AssemblyTreeNode)?.LoadedAssembly;
 			if (assembly == null)
 				return;
-			GeneratePdbContextMenuEntry.GeneratePdbForAssembly(assembly);
+			GeneratePdbContextMenuEntry.GeneratePdbForAssembly(assembly, languageService);
 		}
 	}
 }
diff --git a/ILSpy/Commands/ILSpyCommands.cs b/ILSpy/Commands/ILSpyCommands.cs
deleted file mode 100644
index 50ace9379..000000000
--- a/ILSpy/Commands/ILSpyCommands.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2018 Siegfried Pammer
-// 
-// Permission is hereby granted, free of charge, to any person obtaining a copy of this
-// software and associated documentation files (the "Software"), to deal in the Software
-// without restriction, including without limitation the rights to use, copy, modify, merge,
-// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
-// to whom the Software is furnished to do so, subject to the following conditions:
-// 
-// The above copyright notice and this permission notice shall be included in all copies or
-// substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
-// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Input;
-
-using ICSharpCode.ILSpy.Analyzers;
-using ICSharpCode.ILSpy.Commands;
-
-namespace ICSharpCode.ILSpy
-{
-	static class ILSpyCommands
-	{
-		public static readonly AnalyzeCommand Analyze = new AnalyzeCommand();
-		public static readonly ManageAssemblyListsCommand ManageAssemblyListsCommand = new ManageAssemblyListsCommand();
-		public static readonly SetThemeCommand SetTheme = new SetThemeCommand();
-	}
-}
diff --git a/ILSpy/Commands/ManageAssemblyListsCommand.cs b/ILSpy/Commands/ManageAssemblyListsCommand.cs
index 63ab2f037..8172f2182 100644
--- a/ILSpy/Commands/ManageAssemblyListsCommand.cs
+++ b/ILSpy/Commands/ManageAssemblyListsCommand.cs
@@ -25,12 +25,12 @@ 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 : SimpleCommand
+	sealed class ManageAssemblyListsCommand(SettingsService settingsService) : SimpleCommand
 	{
 		public override void Execute(object parameter)
 		{
-			ManageAssemblyListsDialog dlg = new ManageAssemblyListsDialog();
-			dlg.Owner = MainWindow.Instance;
+			ManageAssemblyListsDialog dlg = new ManageAssemblyListsDialog(settingsService);
+			dlg.Owner = App.Current.MainWindow;
 			dlg.ShowDialog();
 		}
 	}
diff --git a/ILSpy/Commands/OpenFromGacCommand.cs b/ILSpy/Commands/OpenFromGacCommand.cs
index c7fd898d0..ba65bfe21 100644
--- a/ILSpy/Commands/OpenFromGacCommand.cs
+++ b/ILSpy/Commands/OpenFromGacCommand.cs
@@ -43,7 +43,7 @@ namespace ICSharpCode.ILSpy
 		public override void Execute(object parameter)
 		{
 			OpenFromGacDialog dlg = new OpenFromGacDialog {
-				Owner = MainWindow.Instance
+				Owner = App.Current.MainWindow
 			};
 
 			if (dlg.ShowDialog() == true)
diff --git a/ILSpy/Commands/Pdb2XmlCommand.cs b/ILSpy/Commands/Pdb2XmlCommand.cs
index d4b9a7fae..14cfbeabe 100644
--- a/ILSpy/Commands/Pdb2XmlCommand.cs
+++ b/ILSpy/Commands/Pdb2XmlCommand.cs
@@ -26,6 +26,7 @@ using System.Threading.Tasks;
 
 using ICSharpCode.AvalonEdit.Highlighting;
 using ICSharpCode.Decompiler;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.Properties;
 using ICSharpCode.ILSpy.TextView;
 using ICSharpCode.ILSpy.TreeNodes;
@@ -36,18 +37,18 @@ namespace ICSharpCode.ILSpy
 {
 	[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDumpPDBAsXML), MenuCategory = nameof(Resources.Open), MenuOrder = 2.6)]
 	[Shared]
-	sealed class Pdb2XmlCommand : SimpleCommand
+	sealed class Pdb2XmlCommand(AssemblyTreeModel assemblyTreeModel) : SimpleCommand
 	{
 		public override bool CanExecute(object parameter)
 		{
-			var selectedNodes = MainWindow.Instance.AssemblyTreeModel.SelectedNodes;
+			var selectedNodes = assemblyTreeModel.SelectedNodes;
 			return selectedNodes?.Any() == true
 				&& selectedNodes.All(n => n is AssemblyTreeNode asm && !asm.LoadedAssembly.HasLoadError);
 		}
 
 		public override void Execute(object parameter)
 		{
-			Execute(MainWindow.Instance.AssemblyTreeModel.SelectedNodes.OfType<AssemblyTreeNode>());
+			Execute(assemblyTreeModel.SelectedNodes.OfType<AssemblyTreeNode>());
 		}
 
 		internal static void Execute(IEnumerable<AssemblyTreeNode> nodes)
diff --git a/ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs b/ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs
index 42673e99a..da67fd550 100644
--- a/ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs
+++ b/ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs
@@ -26,15 +26,8 @@ namespace ICSharpCode.ILSpy
 {
 	[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._RemoveAssembliesWithLoadErrors), MenuCategory = nameof(Resources.Remove), MenuOrder = 2.6)]
 	[Shared]
-	class RemoveAssembliesWithLoadErrors : SimpleCommand
+	class RemoveAssembliesWithLoadErrors(AssemblyTreeModel assemblyTreeModel) : SimpleCommand
 	{
-		private readonly AssemblyTreeModel assemblyTreeModel;
-
-		public RemoveAssembliesWithLoadErrors(AssemblyTreeModel assemblyTreeModel)
-		{
-			this.assemblyTreeModel = assemblyTreeModel;
-		}
-
 		public override bool CanExecute(object parameter)
 		{
 			return assemblyTreeModel.AssemblyList.GetAssemblies().Any(l => l.HasLoadError);
@@ -46,7 +39,7 @@ namespace ICSharpCode.ILSpy
 			{
 				if (!assembly.HasLoadError)
 					continue;
-				var node = MainWindow.Instance.AssemblyTreeModel.FindAssemblyNode(assembly);
+				var node = assemblyTreeModel.FindAssemblyNode(assembly);
 				if (node != null && node.CanDelete())
 					node.Delete();
 			}
diff --git a/ILSpy/Commands/SaveCodeContextMenuEntry.cs b/ILSpy/Commands/SaveCodeContextMenuEntry.cs
index 14f9fdf50..2dd646450 100644
--- a/ILSpy/Commands/SaveCodeContextMenuEntry.cs
+++ b/ILSpy/Commands/SaveCodeContextMenuEntry.cs
@@ -35,11 +35,11 @@ namespace ICSharpCode.ILSpy.TextView
 {
 	[ExportContextMenuEntry(Header = nameof(Resources._SaveCode), Category = nameof(Resources.Save), Icon = "Images/Save")]
 	[Shared]
-	sealed class SaveCodeContextMenuEntry : IContextMenuEntry
+	sealed class SaveCodeContextMenuEntry(LanguageService languageService) : IContextMenuEntry
 	{
 		public void Execute(TextViewContext context)
 		{
-			Execute(context.SelectedTreeNodes);
+			Execute(context.SelectedTreeNodes, languageService);
 		}
 
 		public bool IsEnabled(TextViewContext context) => true;
@@ -57,12 +57,11 @@ namespace ICSharpCode.ILSpy.TextView
 				|| (selectedNodes.Count > 1 && (selectedNodes.All(n => n is AssemblyTreeNode) || selectedNodes.All(n => n is IMemberTreeNode)));
 		}
 
-		public static void Execute(IReadOnlyList<SharpTreeNode> selectedNodes)
+		public static void Execute(IReadOnlyList<SharpTreeNode> selectedNodes, LanguageService languageService)
 		{
-			var settingsService = SettingsService.Instance;
 			var dockWorkspace = Docking.DockWorkspace.Instance;
 
-			var currentLanguage = LanguageService.Instance.Language;
+			var currentLanguage = languageService.Language;
 			var tabPage = dockWorkspace.ActiveTabPage;
 			tabPage.ShowTextView(textView => {
 				if (selectedNodes.Count == 1 && selectedNodes[0] is ILSpyTreeNode singleSelection)
@@ -88,7 +87,7 @@ namespace ICSharpCode.ILSpy.TextView
 
 				// Fallback: if nobody was able to handle the request, use default behavior.
 				// try to save all nodes to disk.
-				var options = LanguageService.Instance.CreateDecompilationOptions(dockWorkspace.ActiveTabPage);
+				var options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions();
 				options.FullDecompilation = true;
 				textView.SaveToDisk(currentLanguage, selectedNodes.OfType<ILSpyTreeNode>(), options);
 			});
diff --git a/ILSpy/Commands/SaveCommand.cs b/ILSpy/Commands/SaveCommand.cs
index 3b1927e21..3281df456 100644
--- a/ILSpy/Commands/SaveCommand.cs
+++ b/ILSpy/Commands/SaveCommand.cs
@@ -28,16 +28,8 @@ namespace ICSharpCode.ILSpy
 {
 	[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._SaveCode), MenuIcon = "Images/Save", MenuCategory = nameof(Resources.Save), MenuOrder = 0)]
 	[Shared]
-	sealed class SaveCommand : CommandWrapper
+	sealed class SaveCommand(AssemblyTreeModel assemblyTreeModel, LanguageService languageService) : CommandWrapper(ApplicationCommands.Save)
 	{
-		private AssemblyTreeModel assemblyTreeModel;
-
-		public SaveCommand(AssemblyTreeModel assemblyTreeModel)
-			: base(ApplicationCommands.Save)
-		{
-			this.assemblyTreeModel = assemblyTreeModel;
-		}
-
 		protected override void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
 		{
 			e.Handled = true;
@@ -46,7 +38,7 @@ namespace ICSharpCode.ILSpy
 
 		protected override void OnExecute(object sender, ExecutedRoutedEventArgs e)
 		{
-			SaveCodeContextMenuEntry.Execute(assemblyTreeModel.SelectedNodes.ToList());
+			SaveCodeContextMenuEntry.Execute(assemblyTreeModel.SelectedNodes.ToList(), languageService);
 		}
 	}
 }
diff --git a/ILSpy/Commands/SelectPdbContextMenuEntry.cs b/ILSpy/Commands/SelectPdbContextMenuEntry.cs
index e64ec3881..9c63a8b8c 100644
--- a/ILSpy/Commands/SelectPdbContextMenuEntry.cs
+++ b/ILSpy/Commands/SelectPdbContextMenuEntry.cs
@@ -21,6 +21,7 @@ using System.IO;
 using System.Linq;
 
 using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.Properties;
 using ICSharpCode.ILSpy.TreeNodes;
 
@@ -29,7 +30,7 @@ namespace ICSharpCode.ILSpy
 {
 	[ExportContextMenuEntry(Header = nameof(Resources.SelectPDB))]
 	[Shared]
-	class SelectPdbContextMenuEntry : IContextMenuEntry
+	class SelectPdbContextMenuEntry(AssemblyTreeModel assemblyTreeModel) : IContextMenuEntry
 	{
 		public async void Execute(TextViewContext context)
 		{
@@ -48,10 +49,10 @@ namespace ICSharpCode.ILSpy
 				await assembly.LoadDebugInfo(dlg.FileName);
 			}
 
-			var node = (AssemblyTreeNode)MainWindow.Instance.AssemblyTreeModel.FindNodeByPath(new[] { assembly.FileName }, true);
+			var node = (AssemblyTreeNode)assemblyTreeModel.FindNodeByPath(new[] { assembly.FileName }, true);
 			node.UpdateToolTip();
-			MainWindow.Instance.AssemblyTreeModel.SelectNode(node);
-			MainWindow.Instance.AssemblyTreeModel.RefreshDecompiledView();
+			assemblyTreeModel.SelectNode(node);
+			assemblyTreeModel.RefreshDecompiledView();
 		}
 
 		public bool IsEnabled(TextViewContext context) => true;
diff --git a/ILSpy/Commands/SetThemeCommand.cs b/ILSpy/Commands/SetThemeCommand.cs
index 1f9a1226c..006faa107 100644
--- a/ILSpy/Commands/SetThemeCommand.cs
+++ b/ILSpy/Commands/SetThemeCommand.cs
@@ -1,13 +1,17 @@
 
+using System.Composition;
+
 namespace ICSharpCode.ILSpy.Commands
 {
-	public class SetThemeCommand : SimpleCommand
+	[Export]
+	[Shared]
+	public class SetThemeCommand(SettingsService settingsService) : SimpleCommand
 	{
 		public override void Execute(object parameter)
 		{
 			if (parameter is string theme)
 			{
-				SettingsService.Instance.SessionSettings.Theme = theme;
+				settingsService.SessionSettings.Theme = theme;
 			}
 		}
 	}
diff --git a/ILSpy/Commands/SortAssemblyListCommand.cs b/ILSpy/Commands/SortAssemblyListCommand.cs
index f3a55682d..a9434f9c2 100644
--- a/ILSpy/Commands/SortAssemblyListCommand.cs
+++ b/ILSpy/Commands/SortAssemblyListCommand.cs
@@ -20,6 +20,7 @@ using System;
 using System.Collections.Generic;
 using System.Composition;
 
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.Properties;
 using ICSharpCode.ILSpyX;
 using ICSharpCode.ILSpyX.TreeView;
@@ -29,22 +30,22 @@ namespace ICSharpCode.ILSpy
 	[ExportMainMenuCommand(ParentMenuID = nameof(Resources._View), Header = nameof(Resources.SortAssembly_listName), MenuIcon = "Images/Sort", MenuCategory = nameof(Resources.View))]
 	[ExportToolbarCommand(ToolTip = nameof(Resources.SortAssemblyListName), ToolbarIcon = "Images/Sort", ToolbarCategory = nameof(Resources.View))]
 	[Shared]
-	sealed class SortAssemblyListCommand : SimpleCommand
+	sealed class SortAssemblyListCommand(AssemblyTreeModel assemblyTreeModel) : SimpleCommand
 	{
 		public override void Execute(object parameter)
 		{
-			MainWindow.Instance.AssemblyTreeModel.SortAssemblyList();
+			assemblyTreeModel.SortAssemblyList();
 		}
 	}
 
 	[ExportMainMenuCommand(ParentMenuID = nameof(Resources._View), Header = nameof(Resources._CollapseTreeNodes), MenuIcon = "Images/CollapseAll", MenuCategory = nameof(Resources.View))]
 	[ExportToolbarCommand(ToolTip = nameof(Resources.CollapseTreeNodes), ToolbarIcon = "Images/CollapseAll", ToolbarCategory = nameof(Resources.View))]
 	[Shared]
-	sealed class CollapseAllCommand : SimpleCommand
+	sealed class CollapseAllCommand(AssemblyTreeModel assemblyTreeModel) : SimpleCommand
 	{
 		public override void Execute(object parameter)
 		{
-			MainWindow.Instance.AssemblyTreeModel.CollapseAll();
+			assemblyTreeModel.CollapseAll();
 
 		}
 	}
diff --git a/ILSpy/ContextMenuEntry.cs b/ILSpy/ContextMenuEntry.cs
index 5d80ca5f1..1402a09b0 100644
--- a/ILSpy/ContextMenuEntry.cs
+++ b/ILSpy/ContextMenuEntry.cs
@@ -31,6 +31,7 @@ using ICSharpCode.ILSpyX.TreeView;
 
 using TomsToolbox.Composition;
 using TomsToolbox.Essentials;
+using TomsToolbox.Wpf.Composition;
 
 namespace ICSharpCode.ILSpy
 {
@@ -222,7 +223,7 @@ namespace ICSharpCode.ILSpy
 
 		private ContextMenuProvider(Control control)
 		{
-			entries = App.ExportProvider.GetExports<IContextMenuEntry, IContextMenuEntryMetadata>().ToArray();
+			entries = control.GetExportProvider().GetExports<IContextMenuEntry, IContextMenuEntryMetadata>().ToArray();
 
 			this.control = control;
 		}
diff --git a/ILSpy/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs
index b196f8e79..af59d3f59 100644
--- a/ILSpy/Docking/DockWorkspace.cs
+++ b/ILSpy/Docking/DockWorkspace.cs
@@ -32,6 +32,7 @@ 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;
@@ -44,15 +45,21 @@ namespace ICSharpCode.ILSpy.Docking
 {
 	public class DockWorkspace : ObservableObject, ILayoutUpdateStrategy
 	{
-		private static SessionSettings SessionSettings => SettingsService.Instance.SessionSettings;
+		private static readonly IExportProvider exportProvider = App.ExportProvider;
 
-		private readonly IExportProvider exportProvider = App.ExportProvider;
+		private static SettingsService SettingsService => exportProvider.GetExportedValue<SettingsService>();
+
+		private static LanguageService LanguageService => exportProvider.GetExportedValue<LanguageService>();
+
+		private static SessionSettings SessionSettings => SettingsService.SessionSettings;
 
 		public static readonly DockWorkspace Instance = new();
 
 		private readonly ObservableCollection<TabPageModel> tabPages = [];
 
-		private DockingManager dockingManager;
+		private DockingManager DockingManager => exportProvider.GetExportedValue<DockingManager>();
+
+		AssemblyTreeModel AssemblyTreeModel => exportProvider.GetExportedValue<AssemblyTreeModel>();
 
 		private DockWorkspace()
 		{
@@ -123,7 +130,7 @@ namespace ICSharpCode.ILSpy.Docking
 
 		public void AddTabPage(TabPageModel tabPage = null)
 		{
-			tabPages.Add(tabPage ?? new TabPageModel());
+			tabPages.Add(tabPage ?? new TabPageModel(AssemblyTreeModel, SettingsService, LanguageService));
 		}
 
 		public ReadOnlyObservableCollection<TabPageModel> TabPages { get; }
@@ -170,29 +177,28 @@ namespace ICSharpCode.ILSpy.Docking
 				{
 					if (state.DecompiledNodes != null)
 					{
-						MainWindow.Instance.AssemblyTreeModel.SelectNodes(state.DecompiledNodes);
+						AssemblyTreeModel.SelectNodes(state.DecompiledNodes);
 					}
 					else
 					{
-						MainWindow.Instance.AssemblyTreeModel.NavigateTo(new(state.ViewedUri, null));
+						AssemblyTreeModel.NavigateTo(new(state.ViewedUri, null));
 					}
 				}
 			}
 		}
 
 		public PaneModel ActivePane {
-			get => dockingManager?.ActiveContent as PaneModel;
+			get => DockingManager?.ActiveContent as PaneModel;
 			set {
-				if (dockingManager is not null)
-					dockingManager.ActiveContent = value;
+				if (DockingManager is not null)
+					DockingManager.ActiveContent = value;
 			}
 		}
 
-		public void InitializeLayout(DockingManager manager)
+		public void InitializeLayout()
 		{
-			this.dockingManager = manager;
-			manager.LayoutUpdateStrategy = this;
-			XmlLayoutSerializer serializer = new XmlLayoutSerializer(manager);
+			DockingManager.LayoutUpdateStrategy = this;
+			XmlLayoutSerializer serializer = new XmlLayoutSerializer(DockingManager);
 			serializer.LayoutSerializationCallback += LayoutSerializationCallback;
 			try
 			{
@@ -254,8 +260,8 @@ namespace ICSharpCode.ILSpy.Docking
 			}
 			CloseAllTabs();
 			SessionSettings.DockLayout.Reset();
-			InitializeLayout(MainWindow.Instance.dockManager);
-			MainWindow.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)MainWindow.Instance.AssemblyTreeModel.RefreshDecompiledView);
+			InitializeLayout();
+			App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, AssemblyTreeModel.RefreshDecompiledView);
 		}
 
 		static readonly PropertyInfo previousContainerProperty = typeof(LayoutContent).GetProperty("PreviousContainer", BindingFlags.NonPublic | BindingFlags.Instance);
diff --git a/ILSpy/ExtensionMethods.cs b/ILSpy/ExtensionMethods.cs
index 1d49f41ad..bd460826c 100644
--- a/ILSpy/ExtensionMethods.cs
+++ b/ILSpy/ExtensionMethods.cs
@@ -76,10 +76,11 @@ namespace ICSharpCode.ILSpy
 			return result;
 		}
 
-		public static ICompilation? GetTypeSystemWithCurrentOptionsOrNull(this MetadataFile file)
+		public static ICompilation? GetTypeSystemWithCurrentOptionsOrNull(this MetadataFile file, SettingsService settingsService)
 		{
-			return LoadedAssemblyExtensions.GetLoadedAssembly(file)
-				.GetTypeSystemOrNull(DecompilerTypeSystem.GetOptions(SettingsService.Instance.DecompilerSettings));
+			return file
+				.GetLoadedAssembly()
+				.GetTypeSystemOrNull(DecompilerTypeSystem.GetOptions(settingsService.DecompilerSettings));
 		}
 
 		#region DPI independence
diff --git a/ILSpy/Languages/CSharpILMixedLanguage.cs b/ILSpy/Languages/CSharpILMixedLanguage.cs
index 93752eaef..77ec68982 100644
--- a/ILSpy/Languages/CSharpILMixedLanguage.cs
+++ b/ILSpy/Languages/CSharpILMixedLanguage.cs
@@ -43,13 +43,13 @@ namespace ICSharpCode.ILSpy
 
 	[Export(typeof(Language))]
 	[Shared]
-	class CSharpILMixedLanguage : ILLanguage
+	class CSharpILMixedLanguage(SettingsService settingsService) : ILLanguage
 	{
 		public override string Name => "IL with C#";
 
 		protected override ReflectionDisassembler CreateDisassembler(ITextOutput output, DecompilationOptions options)
 		{
-			var displaySettings = SettingsService.Instance.DisplaySettings;
+			var displaySettings = settingsService.DisplaySettings;
 			return new ReflectionDisassembler(output,
 				new MixedMethodBodyDisassembler(output, options) {
 					DetectControlStructure = detectControlStructure,
diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs
index 5725e0555..c59a3fec6 100644
--- a/ILSpy/Languages/CSharpLanguage.cs
+++ b/ILSpy/Languages/CSharpLanguage.cs
@@ -38,6 +38,7 @@ using ICSharpCode.Decompiler.Metadata;
 using ICSharpCode.Decompiler.Output;
 using ICSharpCode.Decompiler.Solution;
 using ICSharpCode.Decompiler.TypeSystem;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.TextView;
 using ICSharpCode.ILSpy.TreeNodes;
 using ICSharpCode.ILSpyX;
@@ -55,25 +56,18 @@ namespace ICSharpCode.ILSpy
 	[Shared]
 	public class CSharpLanguage : Language
 	{
-		readonly IReadOnlyCollection<IResourceFileHandler> resourceFileHandlers;
-
 		string name = "C#";
 		bool showAllMembers = false;
 		int transformCount = int.MaxValue;
 
-		public CSharpLanguage(IEnumerable<IResourceFileHandler> resourceFileHandlers)
-		{
-			this.resourceFileHandlers = resourceFileHandlers.ToArray();
-		}
-
 #if DEBUG
-		internal static IEnumerable<CSharpLanguage> GetDebugLanguages(IReadOnlyCollection<IResourceFileHandler> resourceFileHandlers)
+		internal static IEnumerable<CSharpLanguage> GetDebugLanguages()
 		{
 			string lastTransformName = "no transforms";
 			int transformCount = 0;
 			foreach (var transform in CSharpDecompiler.GetAstTransforms())
 			{
-				yield return new CSharpLanguage(resourceFileHandlers) {
+				yield return new() {
 					transformCount = transformCount,
 					name = "C# - " + lastTransformName,
 					showAllMembers = true
@@ -81,7 +75,7 @@ namespace ICSharpCode.ILSpy
 				lastTransformName = "after " + transform.GetType().Name;
 				transformCount++;
 			}
-			yield return new CSharpLanguage(resourceFileHandlers) {
+			yield return new() {
 				name = "C# - " + lastTransformName,
 				showAllMembers = true
 			};
@@ -364,15 +358,15 @@ namespace ICSharpCode.ILSpy
 
 		void AddReferenceWarningMessage(MetadataFile module, ITextOutput output)
 		{
-			var loadedAssembly = MainWindow.Instance.AssemblyTreeModel.AssemblyList.GetAssemblies().FirstOrDefault(la => la.GetMetadataFileOrNull() == module);
+			var loadedAssembly = AssemblyTreeModel.AssemblyList.GetAssemblies().FirstOrDefault(la => la.GetMetadataFileOrNull() == module);
 			if (loadedAssembly == null || !loadedAssembly.LoadedAssemblyReferencesInfo.HasErrors)
 				return;
 			string line1 = Properties.Resources.WarningSomeAssemblyReference;
 			string line2 = Properties.Resources.PropertyManuallyMissingReferencesListLoadedAssemblies;
 			AddWarningMessage(module, output, line1, line2, Properties.Resources.ShowAssemblyLoad, Images.ViewCode, delegate {
-				ILSpyTreeNode assemblyNode = MainWindow.Instance.AssemblyTreeModel.FindTreeNode(module);
+				ILSpyTreeNode assemblyNode = AssemblyTreeModel.FindTreeNode(module);
 				assemblyNode.EnsureLazyChildren();
-				MainWindow.Instance.AssemblyTreeModel.SelectNode(assemblyNode.Children.OfType<ReferenceFolderTreeNode>().Single());
+				AssemblyTreeModel.SelectNode(assemblyNode.Children.OfType<ReferenceFolderTreeNode>().Single());
 			});
 		}
 
@@ -436,7 +430,7 @@ namespace ICSharpCode.ILSpy
 				{
 					options.DecompilerSettings.UseSdkStyleProjectFormat = false;
 				}
-				var decompiler = new ILSpyWholeProjectDecompiler(assembly, options, resourceFileHandlers) {
+				var decompiler = new ILSpyWholeProjectDecompiler(assembly, options) {
 					ProgressIndicator = options.Progress
 				};
 				return decompiler.DecompileProject(module, options.SaveAsProjectDirectory, new TextOutputWriter(output), options.CancellationToken);
@@ -549,20 +543,18 @@ namespace ICSharpCode.ILSpy
 		{
 			readonly LoadedAssembly assembly;
 			readonly DecompilationOptions options;
-			private readonly IReadOnlyCollection<IResourceFileHandler> resourceFileHandlers;
 
-			public ILSpyWholeProjectDecompiler(LoadedAssembly assembly, DecompilationOptions options, IReadOnlyCollection<IResourceFileHandler> resourceFileHandlers)
+			public ILSpyWholeProjectDecompiler(LoadedAssembly assembly, DecompilationOptions options)
 				: base(options.DecompilerSettings, assembly.GetAssemblyResolver(options.DecompilerSettings.AutoLoadAssemblyReferences, options.DecompilerSettings.ApplyWindowsRuntimeProjections), null, assembly.GetAssemblyReferenceClassifier(options.DecompilerSettings.ApplyWindowsRuntimeProjections), assembly.GetDebugInfoOrNull())
 			{
 				this.assembly = assembly;
 				this.options = options;
-				this.resourceFileHandlers = resourceFileHandlers;
 			}
 
 			protected override IEnumerable<ProjectItemInfo> WriteResourceToFile(string fileName, string resourceName, Stream entryStream)
 			{
 				var context = new ResourceFileHandlerContext(options);
-				foreach (var handler in resourceFileHandlers)
+				foreach (var handler in ResourceFileHandlers)
 				{
 					if (handler.CanHandle(fileName, context))
 					{
@@ -576,19 +568,19 @@ namespace ICSharpCode.ILSpy
 			}
 		}
 
-		static CSharpAmbience CreateAmbience()
+		CSharpAmbience CreateAmbience()
 		{
 			CSharpAmbience ambience = new CSharpAmbience();
 			// Do not forget to update CSharpAmbienceTests.ILSpyMainTreeViewTypeFlags, if this ever changes.
 			ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList | ConversionFlags.PlaceReturnTypeAfterParameterList;
-			if (SettingsService.Instance.DecompilerSettings.LiftNullables)
+			if (SettingsService.DecompilerSettings.LiftNullables)
 			{
 				ambience.ConversionFlags |= ConversionFlags.UseNullableSpecifierForValueTypes;
 			}
 			return ambience;
 		}
 
-		static string EntityToString(IEntity entity, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
+		string EntityToString(IEntity entity, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
 		{
 			// Do not forget to update CSharpAmbienceTests, if this ever changes.
 			var ambience = CreateAmbience();
@@ -784,7 +776,7 @@ namespace ICSharpCode.ILSpy
 		public override bool ShowMember(IEntity member)
 		{
 			MetadataFile assembly = member.ParentModule.MetadataFile;
-			return showAllMembers || !CSharpDecompiler.MemberIsHidden(assembly, member.MetadataToken, SettingsService.Instance.DecompilerSettings);
+			return showAllMembers || !CSharpDecompiler.MemberIsHidden(assembly, member.MetadataToken, SettingsService.DecompilerSettings);
 		}
 
 		public override RichText GetRichTextTooltip(IEntity entity)
@@ -793,7 +785,7 @@ namespace ICSharpCode.ILSpy
 			var output = new StringWriter();
 			var decoratedWriter = new TextWriterTokenWriter(output);
 			var writer = new CSharpHighlightingTokenWriter(TokenWriter.InsertRequiredSpaces(decoratedWriter), locatable: decoratedWriter);
-			var settings = SettingsService.Instance.DecompilerSettings;
+			var settings = SettingsService.DecompilerSettings;
 			if (!settings.LiftNullables)
 			{
 				flags &= ~ConversionFlags.UseNullableSpecifierForValueTypes;
diff --git a/ILSpy/Languages/ILLanguage.cs b/ILSpy/Languages/ILLanguage.cs
index a857268f1..31494f7d8 100644
--- a/ILSpy/Languages/ILLanguage.cs
+++ b/ILSpy/Languages/ILLanguage.cs
@@ -31,6 +31,7 @@ using ICSharpCode.Decompiler.TypeSystem;
 using ICSharpCode.Decompiler.Util;
 using ICSharpCode.ILSpy.Docking;
 using ICSharpCode.ILSpy.TextView;
+using ICSharpCode.ILSpy.ViewModels;
 using ICSharpCode.ILSpyX;
 
 namespace ICSharpCode.ILSpy
@@ -58,7 +59,7 @@ namespace ICSharpCode.ILSpy
 
 		protected virtual ReflectionDisassembler CreateDisassembler(ITextOutput output, DecompilationOptions options)
 		{
-			var displaySettings = SettingsService.Instance.DisplaySettings;
+			var displaySettings = SettingsService.DisplaySettings;
 			output.IndentationString = options.DecompilerSettings.CSharpFormattingOptions.IndentationString;
 			return new ReflectionDisassembler(output, options.CancellationToken) {
 				DetectControlStructure = detectControlStructure,
@@ -198,10 +199,9 @@ namespace ICSharpCode.ILSpy
 		public override RichText GetRichTextTooltip(IEntity entity)
 		{
 			var output = new AvalonEditTextOutput() { IgnoreNewLineAndIndent = true };
-			var settingsService = SettingsService.Instance;
 			var dockWorkspace = DockWorkspace.Instance;
 
-			var disasm = CreateDisassembler(output, LanguageService.Instance.CreateDecompilationOptions(dockWorkspace.ActiveTabPage));
+			var disasm = CreateDisassembler(output, dockWorkspace.ActiveTabPage.CreateDecompilationOptions());
 			MetadataFile module = entity.ParentModule?.MetadataFile;
 			if (module == null)
 			{
diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs
index 26410fe24..59511f9cb 100644
--- a/ILSpy/Languages/Language.cs
+++ b/ILSpy/Languages/Language.cs
@@ -18,6 +18,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Reflection.Metadata;
 using System.Reflection.PortableExecutable;
 using System.Text;
@@ -29,6 +30,7 @@ using ICSharpCode.Decompiler.Solution;
 using ICSharpCode.Decompiler.TypeSystem;
 using ICSharpCode.Decompiler.TypeSystem.Implementation;
 using ICSharpCode.Decompiler.Util;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpyX;
 using ICSharpCode.ILSpyX.Abstractions;
 
@@ -42,6 +44,12 @@ namespace ICSharpCode.ILSpy
 	/// </remarks>
 	public abstract class Language : ILanguage
 	{
+		public static SettingsService SettingsService { get; } = App.ExportProvider.GetExportedValue<SettingsService>();
+
+		public static AssemblyTreeModel AssemblyTreeModel { get; } = App.ExportProvider.GetExportedValue<AssemblyTreeModel>();
+
+		public static ICollection<IResourceFileHandler> ResourceFileHandlers { get; } = App.ExportProvider.GetExportedValues<IResourceFileHandler>().ToArray();
+
 		/// <summary>
 		/// Gets the name of the language (as shown in the UI)
 		/// </summary>
diff --git a/ILSpy/Languages/LanguageService.cs b/ILSpy/Languages/LanguageService.cs
index e060c117d..f5282853c 100644
--- a/ILSpy/Languages/LanguageService.cs
+++ b/ILSpy/Languages/LanguageService.cs
@@ -21,26 +21,23 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Composition;
 using System.Linq;
 
-using ICSharpCode.Decompiler;
-using ICSharpCode.ILSpy.ViewModels;
 using ICSharpCode.ILSpyX;
 
 using TomsToolbox.Wpf;
 
 namespace ICSharpCode.ILSpy
 {
+	[Export]
+	[Shared]
 	public class LanguageService : ObservableObjectBase
 	{
-		public static readonly LanguageService Instance = new(App.ExportProvider.GetExportedValues<Language>(), App.ExportProvider.GetExportedValues<IResourceFileHandler>(), SettingsService.Instance);
-
 		private readonly LanguageSettings languageSettings;
-		private readonly SettingsService settingsService;
 
-		public LanguageService(IEnumerable<Language> languages, IEnumerable<IResourceFileHandler> resourceFileHandlers, SettingsService settingsService)
+		public LanguageService(IEnumerable<Language> languages, SettingsService settingsService)
 		{
-			this.settingsService = settingsService;
 			languageSettings = settingsService.SessionSettings.LanguageSettings;
 
 			var sortedLanguages = languages.ToList();
@@ -48,7 +45,7 @@ namespace ICSharpCode.ILSpy
 			sortedLanguages.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
 #if DEBUG
 			sortedLanguages.AddRange(ILAstLanguage.GetDebugLanguages());
-			sortedLanguages.AddRange(CSharpLanguage.GetDebugLanguages(resourceFileHandlers.ToArray()));
+			sortedLanguages.AddRange(CSharpLanguage.GetDebugLanguages());
 #endif
 			AllLanguages = sortedLanguages.AsReadOnly();
 
@@ -143,10 +140,5 @@ namespace ICSharpCode.ILSpy
 				languageSettings.LanguageVersionId = languageVersion?.Version;
 			}
 		}
-
-		public DecompilationOptions CreateDecompilationOptions(TabPageModel tabPage)
-		{
-			return new(LanguageVersion, settingsService.DecompilerSettings, settingsService.DisplaySettings) { Progress = tabPage.Content as IProgress<DecompilationProgress> };
-		}
 	}
 }
diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml
index bbefc63fc..2aece0f63 100644
--- a/ILSpy/MainWindow.xaml
+++ b/ILSpy/MainWindow.xaml
@@ -18,6 +18,8 @@
 		xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes"
 		xmlns:toms="urn:TomsToolbox"
 		xmlns:viewModels="clr-namespace:ICSharpCode.ILSpy.ViewModels"
+		xmlns:composition="urn:TomsToolbox.Composition"
+		xmlns:commands="clr-namespace:ICSharpCode.ILSpy.Commands"
 		d:DataContext="{d:DesignInstance local:MainWindowViewModel}">
 	<Window.Resources>
 
@@ -36,7 +38,7 @@
 	</b:Interaction.Behaviors>
 
 	<Window.InputBindings>
-		<KeyBinding Key="R" Modifiers="Control" Command="{x:Static local:ILSpyCommands.Analyze}" />
+		<KeyBinding Key="R" Modifiers="Control" Command="{Binding AnalyzeCommand}" />
 		<KeyBinding Key="Z" Modifiers="Control" Command="{x:Static NavigationCommands.BrowseBack}" />
 	</Window.InputBindings>
 
@@ -60,7 +62,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="{x:Static local:ILSpyCommands.SetTheme}" />
+							<Setter Property="Command" Value="{composition:Import {x:Type commands:SetThemeCommand}}" />
 							<Setter Property="CommandParameter" Value="{Binding}" />
 							<Setter Property="IsCheckable" Value="True" />
 							<!-- Required by AvalonDock's MenuItem style to show the checkmark -->
@@ -123,7 +125,7 @@
 						  ToolTip="{x:Static properties:Resources.SelectAssemblyListDropdownTooltip}"
 						  SelectedItem="{Binding SessionSettings.ActiveAssemblyList}" />
 			</Grid>
-			<Button Command="{x:Static local:ILSpyCommands.ManageAssemblyListsCommand}"
+			<Button Command="{composition:Import {x:Type local:ManageAssemblyListsCommand}}"
 					ToolTip="{x:Static properties:Resources.ManageAssemblyLists}">
 				<Image Width="16" Height="16" Source="{controls:XamlResource Images/AssemblyList}"
 					   Style="{StaticResource DarkModeAwareImageStyle}" />
@@ -186,7 +188,7 @@
 			</StatusBarItem>
 		</StatusBar>
 
-		<avalondock:DockingManager x:Name="dockManager"
+		<avalondock:DockingManager x:Name="DockManager"
 								   DataContext="{Binding Workspace}"
 								   AnchorablesSource="{Binding ToolPanes}"
 								   DocumentsSource="{Binding TabPages}"
diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs
index 981da03e8..e9d392717 100644
--- a/ILSpy/MainWindow.xaml.cs
+++ b/ILSpy/MainWindow.xaml.cs
@@ -17,7 +17,9 @@
 // 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;
@@ -30,7 +32,6 @@ using System.Windows.Threading;
 using AvalonDock.Layout.Serialization;
 
 using ICSharpCode.ILSpy.AssemblyTree;
-using ICSharpCode.ILSpy.Docking;
 using ICSharpCode.ILSpy.TreeNodes;
 using ICSharpCode.ILSpy.Updates;
 using ICSharpCode.ILSpyX.FileLoaders;
@@ -44,25 +45,22 @@ namespace ICSharpCode.ILSpy
 	/// <summary>
 	/// The main window of the application.
 	/// </summary>
-	partial class MainWindow : Window
+	[Export]
+	[Shared]
+#pragma warning disable MEF003 // Main window is a singleton
+	partial class MainWindow
 	{
-		static MainWindow instance;
+		private readonly AssemblyTreeModel assemblyTreeModel;
+		private readonly IEnumerable<IFileLoader> fileLoaders;
+		private readonly MenuService menuService;
+		private readonly SettingsService settingsService;
 
-		private readonly MainWindowViewModel mainWindowViewModel = new();
-
-		public static MainWindow Instance {
-			get { return instance; }
-		}
-
-		public AssemblyTreeModel AssemblyTreeModel {
-			get {
-				return App.ExportProvider.GetExportedValue<AssemblyTreeModel>();
-			}
-		}
-
-		public MainWindow()
+		public MainWindow(MainWindowViewModel mainWindowViewModel, AssemblyTreeModel assemblyTreeModel, IEnumerable<IFileLoader> fileLoaders, MenuService menuService, SettingsService settingsService)
 		{
-			instance = this;
+			this.assemblyTreeModel = assemblyTreeModel;
+			this.fileLoaders = fileLoaders;
+			this.menuService = menuService;
+			this.settingsService = settingsService;
 
 			// Make sure Images are initialized on the UI thread.
 			this.Icon = Images.ILSpyIcon;
@@ -74,12 +72,12 @@ namespace ICSharpCode.ILSpy
 			InitFileLoaders();
 
 			Dispatcher.BeginInvoke(DispatcherPriority.Background, () => {
-				mainWindowViewModel.Workspace.InitializeLayout(dockManager);
-				MenuService.Instance.Init(mainMenu, toolBar, InputBindings);
+				mainWindowViewModel.Workspace.InitializeLayout();
+				menuService.Init(mainMenu, toolBar, InputBindings);
 
 				Dispatcher.BeginInvoke(DispatcherPriority.Background, () => {
-					AssemblyTreeModel.Initialize();
-					AssemblyTreeModel.Show();
+					assemblyTreeModel.Initialize();
+					assemblyTreeModel.Show();
 				});
 			});
 		}
@@ -97,9 +95,8 @@ namespace ICSharpCode.ILSpy
 		void InitFileLoaders()
 		{
 			// TODO
-			foreach (var loader in App.ExportProvider.GetExportedValues<IFileLoader>())
+			foreach (var loader in fileLoaders)
 			{
-
 			}
 		}
 
@@ -113,7 +110,7 @@ namespace ICSharpCode.ILSpy
 
 			var source = PresentationSource.FromVisual(this);
 
-			var sessionSettings = SettingsService.Instance.SessionSettings;
+			var sessionSettings = settingsService.SessionSettings;
 
 			// Validate and Set Window Bounds
 			var windowBounds = Rect.Transform(sessionSettings.WindowBounds, source?.CompositionTarget?.TransformToDevice ?? Matrix.Identity);
@@ -185,7 +182,7 @@ namespace ICSharpCode.ILSpy
 			else
 			{
 				updatePanel.Visibility = Visibility.Collapsed;
-				string downloadUrl = await NotifyOfUpdatesStrategy.CheckForUpdatesAsync(SettingsService.Instance.SpySettings);
+				string downloadUrl = await NotifyOfUpdatesStrategy.CheckForUpdatesAsync(settingsService.SpySettings);
 				AdjustUpdateUIAfterCheck(downloadUrl, true);
 			}
 		}
@@ -243,22 +240,22 @@ namespace ICSharpCode.ILSpy
 			base.OnStateChanged(e);
 			// store window state in settings only if it's not minimized
 			if (this.WindowState != WindowState.Minimized)
-				SettingsService.Instance.SessionSettings.WindowState = this.WindowState;
+				settingsService.SessionSettings.WindowState = this.WindowState;
 		}
 
 		protected override void OnClosing(CancelEventArgs e)
 		{
 			base.OnClosing(e);
 
-			var snapshot = SettingsService.Instance.CreateSnapshot();
+			var snapshot = settingsService.CreateSnapshot();
 
 			var sessionSettings = snapshot.GetSettings<SessionSettings>();
 
-			sessionSettings.ActiveAssemblyList = AssemblyTreeModel.AssemblyList.ListName;
-			sessionSettings.ActiveTreeViewPath = AssemblyTreeModel.SelectedPath;
-			sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(AssemblyTreeModel.SelectedItem);
+			sessionSettings.ActiveAssemblyList = assemblyTreeModel.AssemblyList.ListName;
+			sessionSettings.ActiveTreeViewPath = assemblyTreeModel.SelectedPath;
+			sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(assemblyTreeModel.SelectedItem);
 			sessionSettings.WindowBounds = this.RestoreBounds;
-			sessionSettings.DockLayout.Serialize(new XmlLayoutSerializer(dockManager));
+			sessionSettings.DockLayout.Serialize(new XmlLayoutSerializer(DockManager));
 
 			snapshot.Save();
 		}
diff --git a/ILSpy/MainWindowViewModel.cs b/ILSpy/MainWindowViewModel.cs
index 8611f3189..b5f22165d 100644
--- a/ILSpy/MainWindowViewModel.cs
+++ b/ILSpy/MainWindowViewModel.cs
@@ -16,6 +16,10 @@
 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 // DEALINGS IN THE SOFTWARE.
 
+using System.Composition;
+
+using ICSharpCode.ILSpy.Analyzers;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.Docking;
 using ICSharpCode.ILSpyX;
 
@@ -23,11 +27,15 @@ using TomsToolbox.Wpf;
 
 namespace ICSharpCode.ILSpy
 {
-	class MainWindowViewModel : ObservableObject
+	[Export]
+	[Shared]
+	public class MainWindowViewModel(AssemblyTreeModel assemblyTreeModel, AnalyzerTreeViewModel analyzerTreeViewModel, SettingsService settingsService, LanguageService languageService) : ObservableObject
 	{
 		public DockWorkspace Workspace => DockWorkspace.Instance;
-		public SessionSettings SessionSettings => SettingsService.Instance.SessionSettings;
-		public LanguageService LanguageService => LanguageService.Instance;
-		public AssemblyListManager AssemblyListManager => SettingsService.Instance.AssemblyListManager;
+		public SessionSettings SessionSettings => settingsService.SessionSettings;
+		public LanguageService LanguageService => languageService;
+		public AssemblyListManager AssemblyListManager => settingsService.AssemblyListManager;
+
+		public AnalyzeCommand AnalyzeCommand { get; } = new(assemblyTreeModel, analyzerTreeViewModel);
 	}
 }
diff --git a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs
index 14dabb3e0..821a25476 100644
--- a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs
+++ b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs
@@ -94,7 +94,7 @@ namespace ICSharpCode.ILSpy.Metadata
 
 			IEntity IMemberTreeNode.Member {
 				get {
-					return ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule)?.GetDefinition(handle);
+					return ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull(SettingsService)?.MainModule)?.GetDefinition(handle);
 				}
 			}
 
diff --git a/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs
index c8e8e310d..eb48eaec2 100644
--- a/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs
+++ b/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs
@@ -95,7 +95,7 @@ namespace ICSharpCode.ILSpy.Metadata
 
 			public string NameTooltip => $"{MetadataTokens.GetHeapOffset(fieldDef.Name):X} \"{Name}\"";
 
-			IEntity IMemberTreeNode.Member => ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule)?.GetDefinition(handle);
+			IEntity IMemberTreeNode.Member => ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull(SettingsService)?.MainModule)?.GetDefinition(handle);
 
 			[ColumnInfo("X8", Kind = ColumnKind.HeapOffset)]
 			public int Signature => MetadataTokens.GetHeapOffset(fieldDef.Signature);
diff --git a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs
index cfea5f2ae..775661855 100644
--- a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs
+++ b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs
@@ -131,7 +131,7 @@ namespace ICSharpCode.ILSpy.Metadata
 				}
 			}
 
-			IEntity IMemberTreeNode.Member => ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule)?.GetDefinition(handle);
+			IEntity IMemberTreeNode.Member => ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull(SettingsService)?.MainModule)?.GetDefinition(handle);
 
 			public MethodDefEntry(MetadataFile metadataFile, MethodDefinitionHandle handle)
 			{
diff --git a/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs b/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs
index 05ff38b71..255892ea1 100644
--- a/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs
+++ b/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs
@@ -92,7 +92,7 @@ namespace ICSharpCode.ILSpy.Metadata
 
 			public string NameTooltip => $"{MetadataTokens.GetHeapOffset(propertyDef.Name):X} \"{Name}\"";
 
-			IEntity IMemberTreeNode.Member => ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule).GetDefinition(handle);
+			IEntity IMemberTreeNode.Member => ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull(SettingsService)?.MainModule)?.GetDefinition(handle);
 
 			[ColumnInfo("X8", Kind = ColumnKind.HeapOffset)]
 			public int Signature => MetadataTokens.GetHeapOffset(propertyDef.Signature);
diff --git a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs
index 129f76907..e7aa7bbd4 100644
--- a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs
+++ b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs
@@ -173,7 +173,7 @@ namespace ICSharpCode.ILSpy.Metadata
 				}
 			}
 
-			IEntity IMemberTreeNode.Member => ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule).GetDefinition(handle);
+			IEntity IMemberTreeNode.Member => ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull(SettingsService)?.MainModule)?.GetDefinition(handle);
 
 			public TypeDefEntry(MetadataFile metadataFile, TypeDefinitionHandle handle)
 			{
diff --git a/ILSpy/Metadata/DebugMetadataTablesTreeNode.cs b/ILSpy/Metadata/DebugMetadataTablesTreeNode.cs
index d3067d713..6fcbd7dc0 100644
--- a/ILSpy/Metadata/DebugMetadataTablesTreeNode.cs
+++ b/ILSpy/Metadata/DebugMetadataTablesTreeNode.cs
@@ -58,7 +58,7 @@ namespace ICSharpCode.ILSpy.Metadata
 			if (ShowTable(TableIndex.CustomDebugInformation))
 				this.Children.Add(new CustomDebugInformationTableTreeNode(metadataFile));
 
-			bool ShowTable(TableIndex table) => !SettingsService.Instance.DisplaySettings.HideEmptyMetadataTables || metadataFile.Metadata.GetTableRowCount(table) > 0;
+			bool ShowTable(TableIndex table) => !SettingsService.DisplaySettings.HideEmptyMetadataTables || metadataFile.Metadata.GetTableRowCount(table) > 0;
 		}
 
 		public override bool View(TabPageModel tabPage)
diff --git a/ILSpy/Metadata/MetadataProtocolHandler.cs b/ILSpy/Metadata/MetadataProtocolHandler.cs
index c3ffae68e..61cb28a36 100644
--- a/ILSpy/Metadata/MetadataProtocolHandler.cs
+++ b/ILSpy/Metadata/MetadataProtocolHandler.cs
@@ -21,20 +21,21 @@ using System.Linq;
 using System.Reflection.Metadata;
 
 using ICSharpCode.Decompiler.Metadata;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.TreeNodes;
 
 namespace ICSharpCode.ILSpy.Metadata
 {
 	[Export(typeof(IProtocolHandler))]
 	[Shared]
-	class MetadataProtocolHandler : IProtocolHandler
+	class MetadataProtocolHandler(AssemblyTreeModel assemblyTreeModel) : IProtocolHandler
 	{
 		public ILSpyTreeNode Resolve(string protocol, MetadataFile module, Handle handle, out bool newTabPage)
 		{
 			newTabPage = true;
 			if (protocol != "metadata")
 				return null;
-			var assemblyTreeNode = MainWindow.Instance.AssemblyTreeModel.FindTreeNode(module) as AssemblyTreeNode;
+			var assemblyTreeNode = assemblyTreeModel.FindTreeNode(module) as AssemblyTreeNode;
 			if (assemblyTreeNode == null)
 				return null;
 			var mxNode = assemblyTreeNode.Children.OfType<MetadataTreeNode>().FirstOrDefault();
diff --git a/ILSpy/Metadata/MetadataTablesTreeNode.cs b/ILSpy/Metadata/MetadataTablesTreeNode.cs
index 1fb55be81..c40868120 100644
--- a/ILSpy/Metadata/MetadataTablesTreeNode.cs
+++ b/ILSpy/Metadata/MetadataTablesTreeNode.cs
@@ -50,7 +50,7 @@ namespace ICSharpCode.ILSpy.Metadata
 			}
 		}
 
-		internal static bool ShowTable(TableIndex table, MetadataReader metadata) => !SettingsService.Instance.DisplaySettings.HideEmptyMetadataTables || metadata.GetTableRowCount(table) > 0;
+		internal static bool ShowTable(TableIndex table, MetadataReader metadata) => !SettingsService.DisplaySettings.HideEmptyMetadataTables || metadata.GetTableRowCount(table) > 0;
 
 		internal static MetadataTableTreeNode CreateTableTreeNode(TableIndex table, MetadataFile metadataFile)
 		{
diff --git a/ILSpy/Options/OptionsDialog.xaml.cs b/ILSpy/Options/OptionsDialog.xaml.cs
index 1e56f8683..aa596a1d7 100644
--- a/ILSpy/Options/OptionsDialog.xaml.cs
+++ b/ILSpy/Options/OptionsDialog.xaml.cs
@@ -29,9 +29,9 @@ namespace ICSharpCode.ILSpy.Options
 	/// </summary>
 	public sealed partial class OptionsDialog
 	{
-		public OptionsDialog()
+		public OptionsDialog(SettingsService settingsService)
 		{
-			DataContext = new OptionsDialogViewModel();
+			DataContext = new OptionsDialogViewModel(settingsService);
 			InitializeComponent();
 		}
 	}
@@ -59,19 +59,12 @@ namespace ICSharpCode.ILSpy.Options
 
 	[ExportMainMenuCommand(ParentMenuID = nameof(Resources._View), Header = nameof(Resources._Options), MenuCategory = nameof(Resources.Options), MenuOrder = 999)]
 	[Shared]
-	sealed class ShowOptionsCommand : SimpleCommand
+	sealed class ShowOptionsCommand(AssemblyTreeModel assemblyTreeModel, SettingsService settingsService) : SimpleCommand
 	{
-		private readonly AssemblyTreeModel assemblyTreeModel;
-
-		public ShowOptionsCommand(AssemblyTreeModel assemblyTreeModel)
-		{
-			this.assemblyTreeModel = assemblyTreeModel;
-		}
-
 		public override void Execute(object parameter)
 		{
-			OptionsDialog dlg = new() {
-				Owner = MainWindow.Instance,
+			OptionsDialog dlg = new(settingsService) {
+				Owner = App.Current.MainWindow,
 			};
 			if (dlg.ShowDialog() == true)
 			{
diff --git a/ILSpy/Options/OptionsDialogViewModel.cs b/ILSpy/Options/OptionsDialogViewModel.cs
index 0a82d2357..22cfd0100 100644
--- a/ILSpy/Options/OptionsDialogViewModel.cs
+++ b/ILSpy/Options/OptionsDialogViewModel.cs
@@ -46,9 +46,9 @@ namespace ICSharpCode.ILSpy.Options
 			.ExceptNullItems()
 			.ToArray();
 
-		public OptionsDialogViewModel()
+		public OptionsDialogViewModel(SettingsService settingsService)
 		{
-			this.snapshot = SettingsService.Instance.CreateSnapshot();
+			this.snapshot = settingsService.CreateSnapshot();
 
 			foreach (var optionPage in optionPages)
 			{
diff --git a/ILSpy/Search/SearchPane.xaml.cs b/ILSpy/Search/SearchPane.xaml.cs
index 2702dac2f..9cd20e370 100644
--- a/ILSpy/Search/SearchPane.xaml.cs
+++ b/ILSpy/Search/SearchPane.xaml.cs
@@ -33,9 +33,11 @@ using System.Windows.Media;
 using System.Windows.Threading;
 
 using ICSharpCode.ILSpy.AppEnv;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.Docking;
 using ICSharpCode.ILSpy.ViewModels;
 using ICSharpCode.ILSpyX;
+using ICSharpCode.ILSpyX.Abstractions;
 using ICSharpCode.ILSpyX.Extensions;
 using ICSharpCode.ILSpyX.Search;
 
@@ -57,13 +59,20 @@ namespace ICSharpCode.ILSpy.Search
 		RunningSearch currentSearch;
 		bool runSearchOnNextShow;
 		IComparer<SearchResult> resultsComparer;
+		readonly AssemblyTreeModel assemblyTreeModel;
+		readonly ITreeNodeFactory treeNodeFactory;
+		readonly SettingsService settingsService;
 
 		public ObservableCollection<SearchResult> Results { get; } = [];
 
 		string SearchTerm => searchBox.Text;
 
-		public SearchPane()
+		public SearchPane(AssemblyTreeModel assemblyTreeModel, ITreeNodeFactory treeNodeFactory, SettingsService settingsService)
 		{
+			this.assemblyTreeModel = assemblyTreeModel;
+			this.treeNodeFactory = treeNodeFactory;
+			this.settingsService = settingsService;
+
 			InitializeComponent();
 
 			ContextMenuProvider.Add(listBox);
@@ -214,7 +223,7 @@ namespace ICSharpCode.ILSpy.Search
 
 			var timer = Stopwatch.StartNew();
 			int resultsAdded = 0;
-			while (Results.Count < MAX_RESULTS && timer.ElapsedMilliseconds < MAX_REFRESH_TIME_MS && currentSearch.resultQueue.TryTake(out var result))
+			while (Results.Count < MAX_RESULTS && timer.ElapsedMilliseconds < MAX_REFRESH_TIME_MS && currentSearch.ResultQueue.TryTake(out var result))
 			{
 				Results.InsertSorted(result, resultsComparer);
 				++resultsAdded;
@@ -243,8 +252,7 @@ namespace ICSharpCode.ILSpy.Search
 				currentSearch = null;
 			}
 
-			MainWindow mainWindow = MainWindow.Instance;
-			resultsComparer = SettingsService.Instance.DisplaySettings.SortResults ?
+			resultsComparer = settingsService.DisplaySettings.SortResults ?
 				SearchResult.ComparerByFitness :
 				SearchResult.ComparerByName;
 			Results.Clear();
@@ -254,9 +262,12 @@ namespace ICSharpCode.ILSpy.Search
 			{
 
 				searchProgressBar.IsIndeterminate = true;
-				startedSearch = new(await mainWindow.AssemblyTreeModel.AssemblyList.GetAllAssemblies(), searchTerm,
-					(SearchMode)searchModeComboBox.SelectedIndex, mainWindow.AssemblyTreeModel.CurrentLanguage,
-					SettingsService.Instance.SessionSettings.LanguageSettings.ShowApiLevel);
+				startedSearch = new(await assemblyTreeModel.AssemblyList.GetAllAssemblies(), 
+					searchTerm,
+					(SearchMode)searchModeComboBox.SelectedIndex, 
+					assemblyTreeModel.CurrentLanguage,
+					treeNodeFactory, 
+					settingsService);
 				currentSearch = startedSearch;
 
 				await startedSearch.Run();
@@ -285,15 +296,20 @@ namespace ICSharpCode.ILSpy.Search
 			readonly SearchMode searchMode;
 			readonly Language language;
 			readonly ApiVisibility apiVisibility;
-			public readonly IProducerConsumerCollection<SearchResult> resultQueue = new ConcurrentQueue<SearchResult>();
+			readonly ITreeNodeFactory treeNodeFactory;
+			readonly SettingsService settingsService;
+			
+			public IProducerConsumerCollection<SearchResult> ResultQueue { get; } = new ConcurrentQueue<SearchResult>();
 
 			public RunningSearch(IList<LoadedAssembly> assemblies, string searchTerm, SearchMode searchMode,
-				Language language, ApiVisibility apiVisibility)
+				Language language, ITreeNodeFactory treeNodeFactory, SettingsService settingsService)
 			{
 				this.assemblies = assemblies;
 				this.language = language;
 				this.searchMode = searchMode;
-				this.apiVisibility = apiVisibility;
+				this.apiVisibility = settingsService.SessionSettings.LanguageSettings.ShowApiLevel;
+				this.treeNodeFactory = treeNodeFactory;
+				this.settingsService = settingsService;
 				this.searchRequest = Parse(searchTerm);
 			}
 
@@ -454,8 +470,8 @@ namespace ICSharpCode.ILSpy.Search
 				request.Keywords = keywords.ToArray();
 				request.RegEx = regex;
 				request.SearchResultFactory = new SearchResultFactory(language);
-				request.TreeNodeFactory = new TreeNodeFactory();
-				request.DecompilerSettings = SettingsService.Instance.DecompilerSettings;
+				request.TreeNodeFactory = this.treeNodeFactory;
+				request.DecompilerSettings = settingsService.DecompilerSettings;
 
 				return request;
 			}
@@ -504,29 +520,29 @@ namespace ICSharpCode.ILSpy.Search
 				switch (request.Mode)
 				{
 					case SearchMode.TypeAndMember:
-						return new MemberSearchStrategy(language, apiVisibility, request, resultQueue);
+						return new MemberSearchStrategy(language, apiVisibility, request, ResultQueue);
 					case SearchMode.Type:
-						return new MemberSearchStrategy(language, apiVisibility, request, resultQueue, MemberSearchKind.Type);
+						return new MemberSearchStrategy(language, apiVisibility, request, ResultQueue, MemberSearchKind.Type);
 					case SearchMode.Member:
-						return new MemberSearchStrategy(language, apiVisibility, request, resultQueue, request.MemberSearchKind);
+						return new MemberSearchStrategy(language, apiVisibility, request, ResultQueue, request.MemberSearchKind);
 					case SearchMode.Literal:
-						return new LiteralSearchStrategy(language, apiVisibility, request, resultQueue);
+						return new LiteralSearchStrategy(language, apiVisibility, request, ResultQueue);
 					case SearchMode.Method:
-						return new MemberSearchStrategy(language, apiVisibility, request, resultQueue, MemberSearchKind.Method);
+						return new MemberSearchStrategy(language, apiVisibility, request, ResultQueue, MemberSearchKind.Method);
 					case SearchMode.Field:
-						return new MemberSearchStrategy(language, apiVisibility, request, resultQueue, MemberSearchKind.Field);
+						return new MemberSearchStrategy(language, apiVisibility, request, ResultQueue, MemberSearchKind.Field);
 					case SearchMode.Property:
-						return new MemberSearchStrategy(language, apiVisibility, request, resultQueue, MemberSearchKind.Property);
+						return new MemberSearchStrategy(language, apiVisibility, request, ResultQueue, MemberSearchKind.Property);
 					case SearchMode.Event:
-						return new MemberSearchStrategy(language, apiVisibility, request, resultQueue, MemberSearchKind.Event);
+						return new MemberSearchStrategy(language, apiVisibility, request, ResultQueue, MemberSearchKind.Event);
 					case SearchMode.Token:
-						return new MetadataTokenSearchStrategy(language, apiVisibility, request, resultQueue);
+						return new MetadataTokenSearchStrategy(language, apiVisibility, request, ResultQueue);
 					case SearchMode.Resource:
-						return new ResourceSearchStrategy(apiVisibility, request, resultQueue);
+						return new ResourceSearchStrategy(apiVisibility, request, ResultQueue);
 					case SearchMode.Assembly:
-						return new AssemblySearchStrategy(request, resultQueue, AssemblySearchKind.NameOrFileName);
+						return new AssemblySearchStrategy(request, ResultQueue, AssemblySearchKind.NameOrFileName);
 					case SearchMode.Namespace:
-						return new NamespaceSearchStrategy(request, resultQueue);
+						return new NamespaceSearchStrategy(request, ResultQueue);
 				}
 
 				return null;
diff --git a/ILSpy/Search/SearchPaneModel.cs b/ILSpy/Search/SearchPaneModel.cs
index d598c0359..5d3988822 100644
--- a/ILSpy/Search/SearchPaneModel.cs
+++ b/ILSpy/Search/SearchPaneModel.cs
@@ -37,11 +37,14 @@ namespace ICSharpCode.ILSpy.Search
 	[Export]
 	public class SearchPaneModel : ToolPaneModel
 	{
-		private string searchTerm;
 		public const string PaneContentId = "searchPane";
 
-		public SearchPaneModel()
+		private readonly SettingsService settingsService;
+		private string searchTerm;
+
+		public SearchPaneModel(SettingsService settingsService)
 		{
+			this.settingsService = settingsService;
 			ContentId = PaneContentId;
 			Title = Properties.Resources.SearchPane_Search;
 			Icon = "Images/Search";
@@ -64,7 +67,7 @@ namespace ICSharpCode.ILSpy.Search
 			new() { Mode = SearchMode.Namespace, Image = Images.Namespace, Name = "Namespace" }
 		];
 
-		public SessionSettings SessionSettings => SettingsService.Instance.SessionSettings;
+		public SessionSettings SessionSettings => settingsService.SessionSettings;
 
 		public string SearchTerm {
 			get => searchTerm;
diff --git a/ILSpy/Search/SearchResultFactory.cs b/ILSpy/Search/SearchResultFactory.cs
index f466436c7..e81c23402 100644
--- a/ILSpy/Search/SearchResultFactory.cs
+++ b/ILSpy/Search/SearchResultFactory.cs
@@ -16,6 +16,8 @@
 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 // DEALINGS IN THE SOFTWARE.
 using System;
+using System.Collections.Generic;
+using System.Composition;
 using System.Windows.Media;
 
 using ICSharpCode.Decompiler.Metadata;
@@ -158,6 +160,8 @@ namespace ICSharpCode.ILSpy.Search
 		}
 	}
 
+	[Export(typeof(ITreeNodeFactory))]
+	[Shared]
 	internal class TreeNodeFactory : ITreeNodeFactory
 	{
 		public ITreeNode Create(Resource resource)
diff --git a/ILSpy/SolutionWriter.cs b/ILSpy/SolutionWriter.cs
index ca36873fb..ff0035330 100644
--- a/ILSpy/SolutionWriter.cs
+++ b/ILSpy/SolutionWriter.cs
@@ -30,6 +30,7 @@ using ICSharpCode.Decompiler.Solution;
 using ICSharpCode.Decompiler.Util;
 using ICSharpCode.ILSpy.Docking;
 using ICSharpCode.ILSpy.TextView;
+using ICSharpCode.ILSpy.ViewModels;
 using ICSharpCode.ILSpyX;
 
 namespace ICSharpCode.ILSpy
@@ -213,7 +214,7 @@ namespace ICSharpCode.ILSpy
 				using (var projectFileWriter = new StreamWriter(projectFileName))
 				{
 					var projectFileOutput = new PlainTextOutput(projectFileWriter);
-					var options = LanguageService.Instance.CreateDecompilationOptions(DockWorkspace.Instance.ActiveTabPage);
+					var options = DockWorkspace.Instance.ActiveTabPage.CreateDecompilationOptions();
 					options.FullDecompilation = true;
 					options.CancellationToken = ct;
 					options.SaveAsProjectDirectory = targetDirectory;
diff --git a/ILSpy/TaskHelper.cs b/ILSpy/TaskHelper.cs
index 5b33bff3c..58b2ed1f6 100644
--- a/ILSpy/TaskHelper.cs
+++ b/ILSpy/TaskHelper.cs
@@ -197,7 +197,7 @@ namespace ICSharpCode.ILSpy
 		/// </summary>
 		public static void HandleExceptions(this Task task)
 		{
-			task.Catch<Exception>(exception => MainWindow.Instance.Dispatcher.BeginInvoke(new Action(delegate {
+			task.Catch<Exception>(exception => App.Current.Dispatcher.BeginInvoke(new Action(delegate {
 				AvalonEditTextOutput output = new();
 				output.Write(exception.ToString());
 				Docking.DockWorkspace.Instance.ShowText(output);
diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs
index dc54ddc08..efd21d60c 100644
--- a/ILSpy/TextView/DecompilerTextView.cs
+++ b/ILSpy/TextView/DecompilerTextView.cs
@@ -74,6 +74,9 @@ namespace ICSharpCode.ILSpy.TextView
 	/// </summary>
 	public sealed partial class DecompilerTextView : UserControl, IHaveState, IProgress<DecompilationProgress>
 	{
+		readonly AssemblyTreeModel assemblyTreeModel;
+		readonly SettingsService settingsService;
+		private readonly LanguageService languageService;
 		readonly ReferenceElementGenerator referenceElementGenerator;
 		readonly UIElementGenerator uiElementGenerator;
 		readonly List<VisualLineElementGenerator?> activeCustomElementGenerators = new List<VisualLineElementGenerator?>();
@@ -94,8 +97,12 @@ namespace ICSharpCode.ILSpy.TextView
 		readonly List<ITextMarker> localReferenceMarks = new List<ITextMarker>();
 
 		#region Constructor
-		public DecompilerTextView()
+		public DecompilerTextView(TabPageModel tabPage)
 		{
+			this.assemblyTreeModel = tabPage.AssemblyTreeModel;
+			this.settingsService = tabPage.SettingsService;
+			this.languageService = tabPage.LanguageService;
+
 			RegisterHighlighting();
 
 			InitializeComponent();
@@ -113,9 +120,9 @@ namespace ICSharpCode.ILSpy.TextView
 			textEditor.TextArea.Caret.PositionChanged += HighlightBrackets;
 			textEditor.MouseMove += TextEditorMouseMove;
 			textEditor.MouseLeave += TextEditorMouseLeave;
-			textEditor.SetBinding(Control.FontFamilyProperty, new Binding { Source = SettingsService.Instance.DisplaySettings, Path = new PropertyPath("SelectedFont") });
-			textEditor.SetBinding(Control.FontSizeProperty, new Binding { Source = SettingsService.Instance.DisplaySettings, Path = new PropertyPath("SelectedFontSize") });
-			textEditor.SetBinding(TextEditor.WordWrapProperty, new Binding { Source = SettingsService.Instance.DisplaySettings, Path = new PropertyPath("EnableWordWrap") });
+			textEditor.SetBinding(Control.FontFamilyProperty, new Binding { Source = settingsService.DisplaySettings, Path = new PropertyPath("SelectedFont") });
+			textEditor.SetBinding(Control.FontSizeProperty, new Binding { Source = settingsService.DisplaySettings, Path = new PropertyPath("SelectedFontSize") });
+			textEditor.SetBinding(TextEditor.WordWrapProperty, new Binding { Source = settingsService.DisplaySettings, Path = new PropertyPath("EnableWordWrap") });
 
 			// disable Tab editing command (useless for read-only editor); allow using tab for focus navigation instead
 			RemoveEditCommand(EditingCommands.TabForward);
@@ -130,7 +137,7 @@ namespace ICSharpCode.ILSpy.TextView
 
 			// SearchPanel
 			SearchPanel searchPanel = SearchPanel.Install(textEditor.TextArea);
-			searchPanel.RegisterCommands(Application.Current.MainWindow.CommandBindings);
+			searchPanel.RegisterCommands(App.Current.MainWindow.CommandBindings);
 			searchPanel.SetResourceReference(SearchPanel.MarkerBrushProperty, ResourceKeys.SearchResultBackgroundBrush);
 			searchPanel.Loaded += (_, _) => {
 				// HACK: fix search text box
@@ -207,14 +214,14 @@ namespace ICSharpCode.ILSpy.TextView
 			{
 				if (margin is LineNumberMargin || margin is System.Windows.Shapes.Line)
 				{
-					margin.Visibility = SettingsService.Instance.DisplaySettings.ShowLineNumbers ? Visibility.Visible : Visibility.Collapsed;
+					margin.Visibility = settingsService.DisplaySettings.ShowLineNumbers ? Visibility.Visible : Visibility.Collapsed;
 				}
 			}
 		}
 
 		void SetHighlightCurrentLine()
 		{
-			textEditor.Options.HighlightCurrentLine = SettingsService.Instance.DisplaySettings.HighlightCurrentLine;
+			textEditor.Options.HighlightCurrentLine = settingsService.DisplaySettings.HighlightCurrentLine;
 		}
 
 		#endregion
@@ -400,10 +407,12 @@ namespace ICSharpCode.ILSpy.TextView
 
 		object? GenerateTooltip(ReferenceSegment segment)
 		{
+			var fontSize = settingsService.DisplaySettings.SelectedFontSize;
+
 			if (segment.Reference is ICSharpCode.Decompiler.Disassembler.OpCodeInfo code)
 			{
 				XmlDocumentationProvider docProvider = XmlDocLoader.MscorlibDocumentation;
-				DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), LanguageService.Instance.Language.SyntaxHighlighting);
+				DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), languageService.Language.SyntaxHighlighting, settingsService.DisplaySettings);
 				renderer.AddSignatureBlock($"{code.Name} (0x{code.Code:x})");
 				if (docProvider != null)
 				{
@@ -413,18 +422,18 @@ namespace ICSharpCode.ILSpy.TextView
 						renderer.AddXmlDocumentation(documentation, null, null);
 					}
 				}
-				return new FlowDocumentTooltip(renderer.CreateDocument());
+				return new FlowDocumentTooltip(renderer.CreateDocument(), fontSize);
 			}
 			else if (segment.Reference is IEntity entity)
 			{
 				var document = CreateTooltipForEntity(entity);
 				if (document == null)
 					return null;
-				return new FlowDocumentTooltip(document);
+				return new FlowDocumentTooltip(document, fontSize);
 			}
 			else if (segment.Reference is EntityReference unresolvedEntity)
 			{
-				var module = unresolvedEntity.ResolveAssembly(MainWindow.Instance.AssemblyTreeModel.AssemblyList);
+				var module = unresolvedEntity.ResolveAssembly(assemblyTreeModel.AssemblyList);
 				if (module == null)
 					return null;
 				var typeSystem = new DecompilerTypeSystem(module,
@@ -441,7 +450,7 @@ namespace ICSharpCode.ILSpy.TextView
 					var document = CreateTooltipForEntity(resolved);
 					if (document == null)
 						return null;
-					return new FlowDocumentTooltip(document);
+					return new FlowDocumentTooltip(document, fontSize);
 				}
 				catch (BadImageFormatException)
 				{
@@ -451,10 +460,10 @@ namespace ICSharpCode.ILSpy.TextView
 			return null;
 		}
 
-		static FlowDocument? CreateTooltipForEntity(IEntity resolved)
+		FlowDocument? CreateTooltipForEntity(IEntity resolved)
 		{
-			Language currentLanguage = LanguageService.Instance.Language;
-			DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), currentLanguage.SyntaxHighlighting);
+			Language currentLanguage = languageService.Language;
+			DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), currentLanguage.SyntaxHighlighting, settingsService.DisplaySettings);
 			RichText richText = currentLanguage.GetRichTextTooltip(resolved);
 			if (richText == null)
 			{
@@ -484,7 +493,7 @@ namespace ICSharpCode.ILSpy.TextView
 
 			IEntity? ResolveReference(string idString)
 			{
-				return AssemblyTreeModel.FindEntityInRelevantAssemblies(idString, MainWindow.Instance.AssemblyTreeModel.AssemblyList.GetAssemblies());
+				return AssemblyTreeModel.FindEntityInRelevantAssemblies(idString, assemblyTreeModel.AssemblyList.GetAssemblies());
 			}
 		}
 
@@ -492,13 +501,12 @@ namespace ICSharpCode.ILSpy.TextView
 		{
 			readonly FlowDocumentScrollViewer viewer;
 
-			public FlowDocumentTooltip(FlowDocument document)
+			public FlowDocumentTooltip(FlowDocument document, double fontSize)
 			{
 				TextOptions.SetTextFormattingMode(this, TextFormattingMode.Display);
-				double fontSize = SettingsService.Instance.DisplaySettings.SelectedFontSize;
 				viewer = new FlowDocumentScrollViewer() {
 					Width = document.MinPageWidth + fontSize * 5,
-					MaxWidth = MainWindow.Instance.ActualWidth
+					MaxWidth = App.Current.MainWindow.ActualWidth
 				};
 				viewer.Document = document;
 				Border border = new Border {
@@ -542,9 +550,9 @@ namespace ICSharpCode.ILSpy.TextView
 		#region Highlight brackets
 		void HighlightBrackets(object? sender, EventArgs e)
 		{
-			if (SettingsService.Instance.DisplaySettings.HighlightMatchingBraces)
+			if (settingsService.DisplaySettings.HighlightMatchingBraces)
 			{
-				var result = LanguageService.Instance.Language.BracketSearcher.SearchBracket(textEditor.Document, textEditor.CaretOffset);
+				var result = languageService.Language.BracketSearcher.SearchBracket(textEditor.Document, textEditor.CaretOffset);
 				bracketHighlightRenderer.SetHighlight(result);
 			}
 			else
@@ -564,7 +572,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 = MainWindow.Instance.TaskbarItemInfo;
+				var taskBar = App.Current.MainWindow.TaskbarItemInfo;
 				if (taskBar != null)
 				{
 					taskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Normal;
@@ -594,7 +602,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 = MainWindow.Instance.TaskbarItemInfo;
+				var taskBar = App.Current.MainWindow.TaskbarItemInfo;
 				if (taskBar != null)
 				{
 					taskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Indeterminate;
@@ -633,7 +641,7 @@ namespace ICSharpCode.ILSpy.TextView
 						progressBar.IsIndeterminate = false;
 						progressText.Text = null;
 						progressText.Visibility = Visibility.Collapsed;
-						var taskBar = MainWindow.Instance.TaskbarItemInfo;
+						var taskBar = App.Current.MainWindow.TaskbarItemInfo;
 						if (taskBar != null)
 						{
 							taskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
@@ -764,7 +772,7 @@ namespace ICSharpCode.ILSpy.TextView
 			{
 				if (state != null)
 				{
-					state.RestoreFoldings(textOutput.Foldings, SettingsService.Instance.DisplaySettings.ExpandMemberDefinitions);
+					state.RestoreFoldings(textOutput.Foldings, settingsService.DisplaySettings.ExpandMemberDefinitions);
 					textEditor.ScrollToVerticalOffset(state.VerticalOffset);
 					textEditor.ScrollToHorizontalOffset(state.HorizontalOffset);
 				}
@@ -788,7 +796,7 @@ namespace ICSharpCode.ILSpy.TextView
 			}
 			currentAddress = textOutput.Address;
 			currentTitle = textOutput.Title;
-			expandMemberDefinitions = SettingsService.Instance.DisplaySettings.ExpandMemberDefinitions;
+			expandMemberDefinitions = settingsService.DisplaySettings.ExpandMemberDefinitions;
 		}
 		#endregion
 
diff --git a/ILSpy/TextView/DocumentationUIBuilder.cs b/ILSpy/TextView/DocumentationUIBuilder.cs
index 4d0c5eb78..56b53b98f 100644
--- a/ILSpy/TextView/DocumentationUIBuilder.cs
+++ b/ILSpy/TextView/DocumentationUIBuilder.cs
@@ -19,15 +19,12 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
-using System.IO;
 using System.Linq;
 using System.Text;
-using System.Text.RegularExpressions;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Documents;
 using System.Windows.Media;
-using System.Xml;
 using System.Xml.Linq;
 
 using ICSharpCode.AvalonEdit.Document;
@@ -47,14 +44,16 @@ namespace ICSharpCode.ILSpy.TextView
 	{
 		readonly IAmbience ambience;
 		readonly IHighlightingDefinition highlightingDefinition;
+		readonly DisplaySettings displaySettings;
 		readonly FlowDocument document;
 		BlockCollection blockCollection;
 		InlineCollection inlineCollection;
 
-		public DocumentationUIBuilder(IAmbience ambience, IHighlightingDefinition highlightingDefinition)
+		public DocumentationUIBuilder(IAmbience ambience, IHighlightingDefinition highlightingDefinition, DisplaySettings displaySettings)
 		{
 			this.ambience = ambience;
 			this.highlightingDefinition = highlightingDefinition;
+			this.displaySettings = displaySettings;
 			this.document = new FlowDocument();
 			this.blockCollection = document.Blocks;
 
@@ -115,12 +114,12 @@ namespace ICSharpCode.ILSpy.TextView
 			// Paragraph sadly does not support TextWrapping.NoWrap
 			var text = new TextBlock {
 				FontFamily = GetCodeFont(),
-				FontSize = SettingsService.Instance.DisplaySettings.SelectedFontSize,
+				FontSize = displaySettings.SelectedFontSize,
 				TextAlignment = TextAlignment.Left
 			};
 			text.Inlines.AddRange(richText.CreateRuns(document));
 			text.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
-			this.document.MinPageWidth = Math.Min(text.DesiredSize.Width, MainWindow.Instance.ActualWidth);
+			this.document.MinPageWidth = Math.Min(text.DesiredSize.Width, App.Current.MainWindow.ActualWidth);
 			block.Inlines.AddRange(richText.CreateRuns(document));
 			block.FontFamily = GetCodeFont();
 			block.TextAlignment = TextAlignment.Left;
@@ -435,7 +434,7 @@ namespace ICSharpCode.ILSpy.TextView
 
 		FontFamily GetCodeFont()
 		{
-			return SettingsService.Instance.DisplaySettings.SelectedFont;
+			return displaySettings.SelectedFont;
 		}
 
 		public void AddInline(Inline inline)
diff --git a/ILSpy/Themes/WindowStyleManagerBehavior.cs b/ILSpy/Themes/WindowStyleManagerBehavior.cs
index 681fbf97e..1db815ffe 100644
--- a/ILSpy/Themes/WindowStyleManagerBehavior.cs
+++ b/ILSpy/Themes/WindowStyleManagerBehavior.cs
@@ -27,6 +27,7 @@ using ICSharpCode.ILSpy.Options;
 
 using TomsToolbox.Essentials;
 using TomsToolbox.Wpf;
+using TomsToolbox.Wpf.Composition;
 using TomsToolbox.Wpf.Interactivity;
 
 namespace ICSharpCode.ILSpy.Themes
@@ -42,7 +43,7 @@ namespace ICSharpCode.ILSpy.Themes
 		{
 			base.OnAttached();
 
-			SettingsService.Instance.DisplaySettings.PropertyChanged += DisplaySettings_PropertyChanged;
+			MessageBus<SettingsChangedEventArgs>.Subscribers += (sender, e) => DisplaySettings_PropertyChanged(sender, e);
 
 			_foreground = AssociatedObject.Track(Control.ForegroundProperty);
 			_background = AssociatedObject.Track(Control.BackgroundProperty);
@@ -50,7 +51,7 @@ namespace ICSharpCode.ILSpy.Themes
 			_foreground.Changed += Color_Changed;
 			_background.Changed += Color_Changed;
 
-			UpdateWindowStyle();
+			UpdateWindowStyle(AssociatedObject.GetExportProvider().GetExportedValue<SettingsService>().DisplaySettings);
 			ApplyThemeToWindowCaption();
 		}
 
@@ -60,8 +61,6 @@ namespace ICSharpCode.ILSpy.Themes
 
 			_foreground.Changed -= Color_Changed;
 			_background.Changed -= Color_Changed;
-
-			SettingsService.Instance.DisplaySettings.PropertyChanged -= DisplaySettings_PropertyChanged;
 		}
 
 		private void Color_Changed(object sender, EventArgs e)
@@ -69,12 +68,14 @@ namespace ICSharpCode.ILSpy.Themes
 			ApplyThemeToWindowCaption();
 		}
 
-		private void UpdateWindowStyle()
+		private void UpdateWindowStyle(DisplaySettings displaySettings)
 		{
 			var window = AssociatedObject;
 
-			if (SettingsService.Instance.DisplaySettings.StyleWindowTitleBar)
+			if (displaySettings.StyleWindowTitleBar)
+			{
 				window.Style = (Style)window.FindResource(TomsToolbox.Wpf.Styles.ResourceKeys.WindowStyle);
+			}
 		}
 
 		private static void ShowRestartNotification()
@@ -84,16 +85,19 @@ namespace ICSharpCode.ILSpy.Themes
 
 		private void DisplaySettings_PropertyChanged(object sender, PropertyChangedEventArgs e)
 		{
-			if (e.PropertyName == nameof(DisplaySettings.StyleWindowTitleBar))
-			{
-				if (!SettingsService.Instance.DisplaySettings.StyleWindowTitleBar)
-				{
-					restartNotificationThrottle.Tick();
-					return;
-				}
+			if (sender is not DisplaySettings displaySettings)
+				return;
+
+			if (e.PropertyName != nameof(DisplaySettings.StyleWindowTitleBar))
+				return;
 
-				UpdateWindowStyle();
+			if (!displaySettings.StyleWindowTitleBar)
+			{
+				restartNotificationThrottle.Tick();
+				return;
 			}
+
+			UpdateWindowStyle(displaySettings);
 		}
 
 		private void ApplyThemeToWindowCaption()
diff --git a/ILSpy/TreeNodes/AssemblyListTreeNode.cs b/ILSpy/TreeNodes/AssemblyListTreeNode.cs
index e8a984087..3f5b5342e 100644
--- a/ILSpy/TreeNodes/AssemblyListTreeNode.cs
+++ b/ILSpy/TreeNodes/AssemblyListTreeNode.cs
@@ -17,6 +17,7 @@
 // DEALINGS IN THE SOFTWARE.
 
 using System;
+using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.Linq;
 using System.Windows;
@@ -25,6 +26,7 @@ using ICSharpCode.Decompiler;
 using ICSharpCode.Decompiler.Metadata;
 using ICSharpCode.Decompiler.TypeSystem;
 using ICSharpCode.Decompiler.Util;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpyX;
 using ICSharpCode.ILSpyX.TreeView;
 using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions;
@@ -45,7 +47,10 @@ namespace ICSharpCode.ILSpy.TreeNodes
 
 		public AssemblyListTreeNode(AssemblyList assemblyList)
 		{
-			this.assemblyList = assemblyList ?? throw new ArgumentNullException(nameof(assemblyList));
+			ArgumentNullException.ThrowIfNull(assemblyList);
+
+			this.assemblyList = assemblyList;
+			
 			BindToObservableCollection(assemblyList);
 		}
 
@@ -107,8 +112,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
 					.Distinct()
 					.ToArray();
 				assemblyList.Move(assemblies, index);
-				var nodes = assemblies.SelectArray(MainWindow.Instance.AssemblyTreeModel.FindTreeNode);
-				MainWindow.Instance.AssemblyTreeModel.SelectNodes(nodes);
+				var nodes = assemblies.SelectArray(AssemblyTreeModel.FindTreeNode);
+				AssemblyTreeModel.SelectNodes(nodes);
 			}
 		}
 
diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs
index 57a326353..c62af32d8 100644
--- a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs
+++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs
@@ -85,11 +85,11 @@ namespace ICSharpCode.ILSpy.TreeNodes
 		{
 			this.Children.Add(new AssemblyReferenceReferencedTypesTreeNode(module, r));
 
-			var resolver = parentAssembly.LoadedAssembly.GetAssemblyResolver(SettingsService.Instance.DecompilerSettings.AutoLoadAssemblyReferences);
+			var resolver = parentAssembly.LoadedAssembly.GetAssemblyResolver(SettingsService.DecompilerSettings.AutoLoadAssemblyReferences);
 			var referencedModule = resolver.Resolve(r);
 			if (referencedModule != null)
 			{
-				var module = (MetadataModule)referencedModule.GetTypeSystemWithCurrentOptionsOrNull().MainModule;
+				var module = (MetadataModule)referencedModule.GetTypeSystemWithCurrentOptionsOrNull(SettingsService)?.MainModule;
 				foreach (var childRef in referencedModule.AssemblyReferences)
 					this.Children.Add(new AssemblyReferenceTreeNode(module, childRef, parentAssembly));
 			}
diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs
index ac1187d36..7812767c8 100644
--- a/ILSpy/TreeNodes/AssemblyTreeNode.cs
+++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs
@@ -263,7 +263,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 				ns.Children.Clear();
 			}
 			namespaces.Clear();
-			bool useNestedStructure = SettingsService.Instance.DisplaySettings.UseNestedNamespaceNodes;
+			bool useNestedStructure = SettingsService.DisplaySettings.UseNestedNamespaceNodes;
 			foreach (var type in assembly.TopLevelTypeDefinitions.OrderBy(t => t.ReflectionName, NaturalStringComparer.Instance))
 			{
 				var ns = GetOrCreateNamespaceTreeNode(type.Namespace);
@@ -329,7 +329,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 				ns.Children.Clear();
 			}
 			namespaces.Clear();
-			bool useNestedStructure = SettingsService.Instance.DisplaySettings.UseNestedNamespaceNodes;
+			bool useNestedStructure = SettingsService.DisplaySettings.UseNestedNamespaceNodes;
 			foreach (var type in assembly.TopLevelTypeDefinitions.OrderBy(t => t.ReflectionName, NaturalStringComparer.Instance))
 			{
 				var ns = GetOrCreateNamespaceTreeNode(type.Namespace);
@@ -547,7 +547,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 			dlg.Filter = language.Name + " project|*" + language.ProjectFileExtension + "|" + language.Name + " single file|*" + language.FileExtension + "|All files|*.*";
 			if (dlg.ShowDialog() == true)
 			{
-				var options = LanguageService.Instance.CreateDecompilationOptions(DockWorkspace.Instance.ActiveTabPage);
+				var options = DockWorkspace.Instance.ActiveTabPage.CreateDecompilationOptions();
 				options.FullDecompilation = true;
 				if (dlg.FilterIndex == 1)
 				{
@@ -608,7 +608,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 
 	[ExportContextMenuEntry(Header = nameof(Resources._Reload), Icon = "images/Refresh")]
 	[Shared]
-	sealed class ReloadAssembly : IContextMenuEntry
+	sealed class ReloadAssembly(AssemblyTreeModel assemblyTreeModel) : IContextMenuEntry
 	{
 		public bool IsVisible(TextViewContext context)
 		{
@@ -636,14 +636,14 @@ namespace ICSharpCode.ILSpy.TreeNodes
 					la.AssemblyList.ReloadAssembly(la.FileName);
 				}
 			}
-			MainWindow.Instance.AssemblyTreeModel.SelectNodes(paths.Select(p => MainWindow.Instance.AssemblyTreeModel.FindNodeByPath(p, true)).ToArray());
-			MainWindow.Instance.AssemblyTreeModel.RefreshDecompiledView();
+			assemblyTreeModel.SelectNodes(paths.Select(p => assemblyTreeModel.FindNodeByPath(p, true)).ToArray());
+			assemblyTreeModel.RefreshDecompiledView();
 		}
 	}
 
 	[ExportContextMenuEntry(Header = nameof(Resources._LoadDependencies), Category = nameof(Resources.Dependencies))]
 	[Shared]
-	sealed class LoadDependencies : IContextMenuEntry
+	sealed class LoadDependencies(AssemblyTreeModel assemblyTreeModel) : IContextMenuEntry
 	{
 		public bool IsVisible(TextViewContext context)
 		{
@@ -677,13 +677,13 @@ namespace ICSharpCode.ILSpy.TreeNodes
 				}
 			}
 			await Task.WhenAll(tasks);
-			MainWindow.Instance.AssemblyTreeModel.RefreshDecompiledView();
+			assemblyTreeModel.RefreshDecompiledView();
 		}
 	}
 
 	[ExportContextMenuEntry(Header = nameof(Resources._AddMainList), Category = nameof(Resources.Dependencies))]
 	[Shared]
-	sealed class AddToMainList : IContextMenuEntry
+	sealed class AddToMainList(AssemblyTreeModel assemblyTreeModel) : IContextMenuEntry
 	{
 		public bool IsVisible(TextViewContext context)
 		{
@@ -712,7 +712,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
 					node.RaisePropertyChanged(nameof(ILSpyTreeNode.IsAutoLoaded));
 				}
 			}
-			MainWindow.Instance.AssemblyTreeModel.AssemblyList.RefreshSave();
+			
+			assemblyTreeModel.AssemblyList.RefreshSave();
 		}
 	}
 
diff --git a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs
index bf1366fc1..ac068f24c 100644
--- a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs
+++ b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs
@@ -55,7 +55,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 				return FilterResult.Hidden;
 			if (settings.SearchTermMatches(type.Name))
 			{
-				if (type.DeclaringType != null && (settings.ShowApiLevel != ApiVisibility.All || !LanguageService.Instance.Language.ShowMember(type)))
+				if (type.DeclaringType != null && (settings.ShowApiLevel != ApiVisibility.All || !LanguageService.Language.ShowMember(type)))
 					return FilterResult.Hidden;
 				else
 					return FilterResult.Match;
diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs
index 3770a2424..ad75b1b5b 100644
--- a/ILSpy/TreeNodes/EventTreeNode.cs
+++ b/ILSpy/TreeNodes/EventTreeNode.cs
@@ -51,8 +51,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
 
 		private IEvent GetEventDefinition()
 		{
-			return ((MetadataModule)EventDefinition.ParentModule.MetadataFile
-				?.GetTypeSystemWithCurrentOptionsOrNull()
+			return ((MetadataModule)EventDefinition.ParentModule?.MetadataFile
+				?.GetTypeSystemWithCurrentOptionsOrNull(SettingsService)
 				?.MainModule)?.GetDefinition((EventDefinitionHandle)EventDefinition.MetadataToken) ?? EventDefinition;
 		}
 
@@ -72,7 +72,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 		{
 			if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI)
 				return FilterResult.Hidden;
-			if (settings.SearchTermMatches(EventDefinition.Name) && (settings.ShowApiLevel == ApiVisibility.All || LanguageService.Instance.Language.ShowMember(EventDefinition)))
+			if (settings.SearchTermMatches(EventDefinition.Name) && (settings.ShowApiLevel == ApiVisibility.All || LanguageService.Language.ShowMember(EventDefinition)))
 				return FilterResult.Match;
 			else
 				return FilterResult.Hidden;
diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs
index 913418d7e..26319a03d 100644
--- a/ILSpy/TreeNodes/FieldTreeNode.cs
+++ b/ILSpy/TreeNodes/FieldTreeNode.cs
@@ -43,8 +43,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
 
 		private IField GetFieldDefinition()
 		{
-			return ((MetadataModule)FieldDefinition.ParentModule.MetadataFile
-				?.GetTypeSystemWithCurrentOptionsOrNull()
+			return ((MetadataModule)FieldDefinition.ParentModule?.MetadataFile
+				?.GetTypeSystemWithCurrentOptionsOrNull(SettingsService)
 				?.MainModule)?.GetDefinition((FieldDefinitionHandle)FieldDefinition.MetadataToken) ?? FieldDefinition;
 		}
 
@@ -73,7 +73,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 		{
 			if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI)
 				return FilterResult.Hidden;
-			if (settings.SearchTermMatches(FieldDefinition.Name) && (settings.ShowApiLevel == ApiVisibility.All || LanguageService.Instance.Language.ShowMember(FieldDefinition)))
+			if (settings.SearchTermMatches(FieldDefinition.Name) && (settings.ShowApiLevel == ApiVisibility.All || LanguageService.Language.ShowMember(FieldDefinition)))
 				return FilterResult.Match;
 			else
 				return FilterResult.Hidden;
diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs
index a65b88300..dbaa2d6ed 100644
--- a/ILSpy/TreeNodes/ILSpyTreeNode.cs
+++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs
@@ -16,7 +16,6 @@
 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 // DEALINGS IN THE SOFTWARE.
 
-using System;
 using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.ComponentModel;
@@ -27,6 +26,7 @@ using System.Windows.Threading;
 
 using ICSharpCode.Decompiler;
 using ICSharpCode.Decompiler.TypeSystem;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpyX.Abstractions;
 using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions;
 using ICSharpCode.ILSpyX.TreeView;
@@ -45,9 +45,17 @@ namespace ICSharpCode.ILSpy.TreeNodes
 			MessageBus<SettingsChangedEventArgs>.Subscribers += (sender, e) => Settings_Changed(sender, e);
 		}
 
-		LanguageSettings LanguageSettings => SettingsService.Instance.SessionSettings.LanguageSettings;
+		LanguageSettings LanguageSettings => SettingsService.SessionSettings.LanguageSettings;
 
-		public Language Language => LanguageService.Instance.Language;
+		public Language Language => LanguageService.Language;
+
+		public static AssemblyTreeModel AssemblyTreeModel { get; } = App.ExportProvider.GetExportedValue<AssemblyTreeModel>();
+
+		public static ICollection<IResourceNodeFactory> ResourceNodeFactories { get; } = App.ExportProvider.GetExportedValues<IResourceNodeFactory>().ToArray();
+
+		public static SettingsService SettingsService { get; } = App.ExportProvider.GetExportedValue<SettingsService>();
+
+		public static LanguageService LanguageService { get; } = App.ExportProvider.GetExportedValue<LanguageService>();
 
 		public virtual FilterResult Filter(LanguageSettings settings)
 		{
@@ -71,8 +79,11 @@ namespace ICSharpCode.ILSpy.TreeNodes
 
 		public override void ActivateItemSecondary(IPlatformRoutedEventArgs e)
 		{
-			MainWindow.Instance.AssemblyTreeModel.SelectNode(this, inNewTabPage: true);
-			MainWindow.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)MainWindow.Instance.AssemblyTreeModel.RefreshDecompiledView);
+			var assemblyTreeModel = AssemblyTreeModel;
+
+			assemblyTreeModel.SelectNode(this, inNewTabPage: true);
+			
+			App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, assemblyTreeModel.RefreshDecompiledView);
 		}
 
 		/// <summary>
@@ -87,6 +98,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
 
 		public override void OnChildrenChanged(NotifyCollectionChangedEventArgs e)
 		{
+			base.OnChildrenChanged(e);
+
 			if (e.NewItems != null)
 			{
 				if (IsVisible)
@@ -99,7 +112,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
 					childrenNeedFiltering = true;
 				}
 			}
-			base.OnChildrenChanged(e);
 		}
 
 		void ApplyFilterToChild(ILSpyTreeNode child)
@@ -162,11 +174,11 @@ namespace ICSharpCode.ILSpy.TreeNodes
 
 		protected string GetSuffixString(EntityHandle handle)
 		{
-			if (!SettingsService.Instance.DisplaySettings.ShowMetadataTokens)
+			if (!SettingsService.DisplaySettings.ShowMetadataTokens)
 				return string.Empty;
 
 			int token = MetadataTokens.GetToken(handle);
-			if (SettingsService.Instance.DisplaySettings.ShowMetadataTokensInBase10)
+			if (SettingsService.DisplaySettings.ShowMetadataTokensInBase10)
 				return " @" + token;
 			return " @" + token.ToString("x8");
 		}
diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs
index e6bef5f10..f3d2a6daa 100644
--- a/ILSpy/TreeNodes/MethodTreeNode.cs
+++ b/ILSpy/TreeNodes/MethodTreeNode.cs
@@ -43,8 +43,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
 
 		private IMethod GetMethodDefinition()
 		{
-			return ((MetadataModule)MethodDefinition.ParentModule.MetadataFile
-				?.GetTypeSystemWithCurrentOptionsOrNull()
+			return ((MetadataModule)MethodDefinition.ParentModule?.MetadataFile
+				?.GetTypeSystemWithCurrentOptionsOrNull(SettingsService)
 				?.MainModule)?.GetDefinition((MethodDefinitionHandle)MethodDefinition.MetadataToken) ?? MethodDefinition;
 		}
 
@@ -103,7 +103,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 		{
 			if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI)
 				return FilterResult.Hidden;
-			if (settings.SearchTermMatches(MethodDefinition.Name) && (settings.ShowApiLevel == ApiVisibility.All || LanguageService.Instance.Language.ShowMember(MethodDefinition)))
+			if (settings.SearchTermMatches(MethodDefinition.Name) && (settings.ShowApiLevel == ApiVisibility.All || LanguageService.Language.ShowMember(MethodDefinition)))
 				return FilterResult.Match;
 			else
 				return FilterResult.Hidden;
@@ -127,7 +127,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 
 		public override string ToString()
 		{
-			return LanguageService.Instance.ILLanguage.MethodToString(MethodDefinition, false, false, false);
+			return LanguageService.ILLanguage.MethodToString(MethodDefinition, false, false, false);
 		}
 	}
 }
diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs
index e9539c887..df1a9f473 100644
--- a/ILSpy/TreeNodes/PropertyTreeNode.cs
+++ b/ILSpy/TreeNodes/PropertyTreeNode.cs
@@ -53,8 +53,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
 
 		private IProperty GetPropertyDefinition()
 		{
-			return ((MetadataModule)PropertyDefinition.ParentModule.MetadataFile
-				?.GetTypeSystemWithCurrentOptionsOrNull()
+			return ((MetadataModule)PropertyDefinition.ParentModule?.MetadataFile
+				?.GetTypeSystemWithCurrentOptionsOrNull(SettingsService)
 				?.MainModule)?.GetDefinition((PropertyDefinitionHandle)PropertyDefinition.MetadataToken) ?? PropertyDefinition;
 		}
 
@@ -75,7 +75,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 		{
 			if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI)
 				return FilterResult.Hidden;
-			if (settings.SearchTermMatches(PropertyDefinition.Name) && (settings.ShowApiLevel == ApiVisibility.All || LanguageService.Instance.Language.ShowMember(PropertyDefinition)))
+			if (settings.SearchTermMatches(PropertyDefinition.Name) && (settings.ShowApiLevel == ApiVisibility.All || LanguageService.Language.ShowMember(PropertyDefinition)))
 				return FilterResult.Match;
 			else
 				return FilterResult.Hidden;
@@ -104,7 +104,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 
 		public override string ToString()
 		{
-			return LanguageService.Instance.ILLanguage.PropertyToString(PropertyDefinition, false, false, false);
+			return LanguageService.ILLanguage.PropertyToString(PropertyDefinition, false, false, false);
 		}
 	}
 }
diff --git a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs
index 355a96537..c8c4dd8d0 100644
--- a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs
+++ b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs
@@ -49,7 +49,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 		protected override void LoadChildren()
 		{
 			var metadata = module.Metadata;
-			var metadataModule = (MetadataModule)module.GetTypeSystemWithCurrentOptionsOrNull().MainModule;
+			var metadataModule = (MetadataModule)module.GetTypeSystemWithCurrentOptionsOrNull(SettingsService)?.MainModule;
 			foreach (var r in module.AssemblyReferences.OrderBy(r => r.Name))
 				this.Children.Add(new AssemblyReferenceTreeNode(metadataModule, r, parentAssembly));
 			foreach (var r in metadata.GetModuleReferences().OrderBy(r => metadata.GetString(metadata.GetModuleReference(r).Name)))
diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
index 5786c88c8..92e319802 100644
--- a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
+++ b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
@@ -22,7 +22,6 @@ using System.IO;
 using ICSharpCode.Decompiler;
 using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
 using ICSharpCode.Decompiler.Metadata;
-using ICSharpCode.ILSpyX.Abstractions;
 
 using Microsoft.Win32;
 
@@ -58,7 +57,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 		public static ILSpyTreeNode Create(Resource resource)
 		{
 			ILSpyTreeNode result = null;
-			foreach (var factory in App.ExportProvider.GetExportedValues<IResourceNodeFactory>())
+			foreach (var factory in ResourceNodeFactories)
 			{
 				result = factory.CreateNode(resource) as ILSpyTreeNode;
 				if (result != null)
diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs
index 620fe9bf2..a79b5c654 100644
--- a/ILSpy/TreeNodes/TypeTreeNode.cs
+++ b/ILSpy/TreeNodes/TypeTreeNode.cs
@@ -49,8 +49,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
 		{
 			return ((MetadataModule)ParentAssemblyNode.LoadedAssembly
 				.GetMetadataFileOrNull()
-				?.GetTypeSystemWithCurrentOptionsOrNull()
-				?.MainModule).GetDefinition((SRM.TypeDefinitionHandle)TypeDefinition.MetadataToken);
+				?.GetTypeSystemWithCurrentOptionsOrNull(SettingsService)
+				?.MainModule)?.GetDefinition((SRM.TypeDefinitionHandle)TypeDefinition.MetadataToken);
 		}
 
 		public override bool IsPublicAPI {
@@ -73,7 +73,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
 				return FilterResult.Hidden;
 			if (settings.SearchTermMatches(TypeDefinition.Name))
 			{
-				if (settings.ShowApiLevel == ApiVisibility.All || LanguageService.Instance.Language.ShowMember(TypeDefinition))
+				if (settings.ShowApiLevel == ApiVisibility.All || LanguageService.Language.ShowMember(TypeDefinition))
 					return FilterResult.Match;
 				else
 					return FilterResult.Hidden;
diff --git a/ILSpy/Util/MenuService.cs b/ILSpy/Util/MenuService.cs
index 6b1d2e29d..340dd7fb9 100644
--- a/ILSpy/Util/MenuService.cs
+++ b/ILSpy/Util/MenuService.cs
@@ -17,6 +17,7 @@
 // DEALINGS IN THE SOFTWARE.
 
 using System.Collections.Generic;
+using System.Composition;
 using System.Globalization;
 using System.Linq;
 using System.Windows;
@@ -35,10 +36,10 @@ using TomsToolbox.Wpf.Converters;
 
 namespace ICSharpCode.ILSpy.Util
 {
-	internal class MenuService
+	[Export]
+	[Shared]
+	public class MenuService(IExportProvider exportProvider)
 	{
-		public static readonly MenuService Instance = new();
-
 		private readonly DockWorkspace dockWorkspace = DockWorkspace.Instance;
 
 		public void Init(Menu mainMenu, ToolBar toolBar, InputBindingCollection inputBindings)
@@ -48,9 +49,9 @@ namespace ICSharpCode.ILSpy.Util
 			InitToolbar(toolBar);
 		}
 
-		static void InitMainMenu(Menu mainMenu)
+		void InitMainMenu(Menu mainMenu)
 		{
-			var mainMenuCommands = App.ExportProvider.GetExports<ICommand, IMainMenuCommandMetadata>("MainMenuCommand");
+			var mainMenuCommands = exportProvider.GetExports<ICommand, IMainMenuCommandMetadata>("MainMenuCommand");
 			// Start by constructing the individual flat menus
 			var parentMenuItems = new Dictionary<string, MenuItem>();
 			var menuGroups = mainMenuCommands.OrderBy(c => c.Metadata?.MenuOrder).GroupBy(c => c.Metadata?.ParentMenuID).ToArray();
@@ -150,11 +151,11 @@ namespace ICSharpCode.ILSpy.Util
 			windowMenuItem.ItemsSource = allItems;
 		}
 
-		static void InitToolbar(ToolBar toolBar)
+		void InitToolbar(ToolBar toolBar)
 		{
 			int navigationPos = 0;
 			int openPos = 1;
-			var toolbarCommandsByTitle = App.ExportProvider.GetExports<ICommand, IToolbarCommandMetadata>("ToolbarCommand")
+			var toolbarCommandsByTitle = exportProvider.GetExports<ICommand, IToolbarCommandMetadata>("ToolbarCommand")
 				.OrderBy(c => c.Metadata?.ToolbarOrder)
 				.GroupBy(c => c.Metadata?.ToolbarCategory);
 
diff --git a/ILSpy/Util/SettingsService.cs b/ILSpy/Util/SettingsService.cs
index 46a132e75..6ce11ee33 100644
--- a/ILSpy/Util/SettingsService.cs
+++ b/ILSpy/Util/SettingsService.cs
@@ -19,6 +19,7 @@
 using System;
 using System.Collections.Concurrent;
 using System.ComponentModel;
+using System.Composition;
 using System.Xml.Linq;
 
 using ICSharpCode.Decompiler;
@@ -110,13 +111,9 @@ namespace ICSharpCode.ILSpy.Util
 		}
 	}
 
-	public class SettingsService : SettingsServiceBase
+	public class SettingsService() : SettingsServiceBase(LoadSettings())
 	{
-		public static readonly SettingsService Instance = new();
-
-		private SettingsService() : base(LoadSettings())
-		{
-		}
+		public static readonly SettingsService Instance = App.ExportProvider.GetExportedValue<SettingsService>();
 
 		public SessionSettings SessionSettings => GetSettings<SessionSettings>();
 
diff --git a/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs b/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs
index 76ab7aedb..9d73655d3 100644
--- a/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs
+++ b/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs
@@ -35,11 +35,13 @@ namespace ICSharpCode.ILSpy.ViewModels
 	{
 		private readonly AssemblyListManager manager;
 		private readonly Window parent;
+		private readonly SessionSettings sessionSettings;
 
-		public ManageAssemblyListsViewModel(Window parent)
+		public ManageAssemblyListsViewModel(Window parent, SettingsService settingsService)
 		{
-			this.manager = SettingsService.Instance.AssemblyListManager;
+			this.manager = settingsService.AssemblyListManager;
 			this.parent = parent;
+			this.sessionSettings = settingsService.SessionSettings;
 
 			NewCommand = new DelegateCommand(ExecuteNew);
 			CloneCommand = new DelegateCommand(ExecuteClone, CanExecuteClone);
@@ -174,7 +176,7 @@ namespace ICSharpCode.ILSpy.ViewModels
 				return;
 			manager.ClearAll();
 			manager.CreateDefaultAssemblyLists();
-			SettingsService.Instance.SessionSettings.ActiveAssemblyList = manager.AssemblyLists[0];
+			sessionSettings.ActiveAssemblyList = manager.AssemblyLists[0];
 		}
 
 		private void ExecuteDelete()
@@ -189,9 +191,9 @@ namespace ICSharpCode.ILSpy.ViewModels
 			if (manager.AssemblyLists.Count > 0)
 			{
 				SelectedAssemblyList = manager.AssemblyLists[Math.Max(0, index - 1)];
-				if (SettingsService.Instance.SessionSettings.ActiveAssemblyList == assemblyList)
+				if (sessionSettings.ActiveAssemblyList == assemblyList)
 				{
-					SettingsService.Instance.SessionSettings.ActiveAssemblyList = SelectedAssemblyList;
+					sessionSettings.ActiveAssemblyList = SelectedAssemblyList;
 				}
 			}
 		}
@@ -232,9 +234,9 @@ namespace ICSharpCode.ILSpy.ViewModels
 				string assemblyList = SelectedAssemblyList;
 				SelectedAssemblyList = dlg.ListName;
 				manager.RenameList(assemblyList, dlg.ListName);
-				if (SettingsService.Instance.SessionSettings.ActiveAssemblyList == assemblyList)
+				if (sessionSettings.ActiveAssemblyList == assemblyList)
 				{
-					SettingsService.Instance.SessionSettings.ActiveAssemblyList = manager.AssemblyLists[manager.AssemblyLists.Count - 1];
+					sessionSettings.ActiveAssemblyList = manager.AssemblyLists[manager.AssemblyLists.Count - 1];
 				}
 			}
 		}
@@ -272,7 +274,7 @@ namespace ICSharpCode.ILSpy.ViewModels
 
 		private void ExecuteSelectAssemblyList()
 		{
-			SettingsService.Instance.SessionSettings.ActiveAssemblyList = SelectedAssemblyList;
+			sessionSettings.ActiveAssemblyList = SelectedAssemblyList;
 			this.parent.Close();
 		}
 	}
diff --git a/ILSpy/ViewModels/TabPageModel.cs b/ILSpy/ViewModels/TabPageModel.cs
index 20e4bfb67..9d7366b41 100644
--- a/ILSpy/ViewModels/TabPageModel.cs
+++ b/ILSpy/ViewModels/TabPageModel.cs
@@ -21,6 +21,8 @@ using System.Linq;
 using System.Threading.Tasks;
 using System.Windows;
 
+using ICSharpCode.Decompiler;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.TextView;
 
 using TomsToolbox.Wpf;
@@ -29,8 +31,16 @@ namespace ICSharpCode.ILSpy.ViewModels
 {
 	public class TabPageModel : PaneModel
 	{
-		public TabPageModel()
+		public AssemblyTreeModel AssemblyTreeModel { get; }
+		public SettingsService SettingsService { get; }
+		public LanguageService LanguageService { get; }
+
+		public TabPageModel(AssemblyTreeModel assemblyTreeModel, SettingsService settingsService, LanguageService languageService)
 		{
+			AssemblyTreeModel = assemblyTreeModel;
+			SettingsService = settingsService;
+			LanguageService = languageService;
+
 			this.Title = Properties.Resources.NewTab;
 		}
 
@@ -72,7 +82,7 @@ namespace ICSharpCode.ILSpy.ViewModels
 		{
 			if (!(tabPage.Content is DecompilerTextView textView))
 			{
-				textView = new DecompilerTextView();
+				textView = new DecompilerTextView(tabPage);
 				tabPage.Content = textView;
 			}
 			tabPage.Title = Properties.Resources.Decompiling;
@@ -83,7 +93,7 @@ namespace ICSharpCode.ILSpy.ViewModels
 		{
 			if (!(tabPage.Content is DecompilerTextView textView))
 			{
-				textView = new DecompilerTextView();
+				textView = new DecompilerTextView(tabPage);
 				tabPage.Content = textView;
 			}
 			string oldTitle = tabPage.Title;
@@ -105,7 +115,7 @@ namespace ICSharpCode.ILSpy.ViewModels
 		{
 			if (!(tabPage.Content is DecompilerTextView textView))
 			{
-				textView = new DecompilerTextView();
+				textView = new DecompilerTextView(tabPage);
 				tabPage.Content = textView;
 			}
 			string oldTitle = tabPage.Title;
@@ -129,6 +139,11 @@ namespace ICSharpCode.ILSpy.ViewModels
 
 			focusable?.Focus();
 		}
+
+		public static DecompilationOptions CreateDecompilationOptions(this TabPageModel tabPage)
+		{
+			return new(tabPage.LanguageService.LanguageVersion, tabPage.SettingsService.DecompilerSettings, tabPage.SettingsService.DisplaySettings) { Progress = tabPage.Content as IProgress<DecompilationProgress> };
+		}
 	}
 
 	public interface IHaveState
diff --git a/ILSpy/Views/DebugSteps.xaml.cs b/ILSpy/Views/DebugSteps.xaml.cs
index 7957802e5..9442eff70 100644
--- a/ILSpy/Views/DebugSteps.xaml.cs
+++ b/ILSpy/Views/DebugSteps.xaml.cs
@@ -7,6 +7,7 @@ using System.Windows.Input;
 
 using ICSharpCode.Decompiler.IL;
 using ICSharpCode.Decompiler.IL.Transforms;
+using ICSharpCode.ILSpy.AssemblyTree;
 using ICSharpCode.ILSpy.Docking;
 using ICSharpCode.ILSpy.ViewModels;
 
@@ -18,6 +19,10 @@ namespace ICSharpCode.ILSpy
 	[NonShared]
 	public partial class DebugSteps : UserControl
 	{
+		private readonly AssemblyTreeModel assemblyTreeModel;
+		private readonly SettingsService settingsService;
+		private readonly LanguageService languageService;
+
 		static readonly ILAstWritingOptions writingOptions = new ILAstWritingOptions {
 			UseFieldSugar = true,
 			UseLogicOperationSugar = true
@@ -28,8 +33,12 @@ namespace ICSharpCode.ILSpy
 #if DEBUG
 		ILAstLanguage language;
 #endif
-		public DebugSteps()
+		public DebugSteps(AssemblyTreeModel assemblyTreeModel, SettingsService settingsService, LanguageService languageService)
 		{
+			this.assemblyTreeModel = assemblyTreeModel;
+			this.settingsService = settingsService;
+			this.languageService = languageService;
+			
 			InitializeComponent();
 
 #if DEBUG
@@ -38,7 +47,7 @@ namespace ICSharpCode.ILSpy
 
 			writingOptions.PropertyChanged += WritingOptions_PropertyChanged;
 
-			if (LanguageService.Instance.Language is ILAstLanguage l)
+			if (languageService.Language is ILAstLanguage l)
 			{
 				l.StepperUpdated += ILAstStepperUpdated;
 				language = l;
@@ -72,7 +81,7 @@ namespace ICSharpCode.ILSpy
 				{
 					language.StepperUpdated -= ILAstStepperUpdated;
 				}
-				if (LanguageService.Instance.Language is ILAstLanguage l)
+				if (languageService.Language is ILAstLanguage l)
 				{
 					l.StepperUpdated += ILAstStepperUpdated;
 					language = l;
@@ -123,10 +132,9 @@ namespace ICSharpCode.ILSpy
 		void DecompileAsync(int step, bool isDebug = false)
 		{
 			lastSelectedStep = step;
-			var window = MainWindow.Instance;
 			var state = DockWorkspace.Instance.ActiveTabPage.GetState();
-			DockWorkspace.Instance.ActiveTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(window.AssemblyTreeModel.CurrentLanguage, window.AssemblyTreeModel.SelectedNodes,
-				new DecompilationOptions(window.AssemblyTreeModel.CurrentLanguageVersion, SettingsService.Instance.DecompilerSettings, SettingsService.Instance.DisplaySettings) {
+			DockWorkspace.Instance.ActiveTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(assemblyTreeModel.CurrentLanguage, assemblyTreeModel.SelectedNodes,
+				new DecompilationOptions(assemblyTreeModel.CurrentLanguageVersion, settingsService.DecompilerSettings, settingsService.DisplaySettings) {
 					StepLimit = step,
 					IsDebug = isDebug,
 					TextViewState = state as TextView.DecompilerTextViewState
diff --git a/ILSpy/Views/ManageAssemblyLIstsDialog.xaml.cs b/ILSpy/Views/ManageAssemblyLIstsDialog.xaml.cs
index 111c3c7c7..93efc3e98 100644
--- a/ILSpy/Views/ManageAssemblyLIstsDialog.xaml.cs
+++ b/ILSpy/Views/ManageAssemblyLIstsDialog.xaml.cs
@@ -29,10 +29,10 @@ namespace ICSharpCode.ILSpy
 	/// </summary>
 	public partial class ManageAssemblyListsDialog : Window
 	{
-		public ManageAssemblyListsDialog()
+		public ManageAssemblyListsDialog(SettingsService settingsService)
 		{
 			InitializeComponent();
-			DataContext = new ManageAssemblyListsViewModel(this);
+			DataContext = new ManageAssemblyListsViewModel(this, settingsService);
 		}
 
 		private void PreconfiguredAssemblyListsMenuClick(object sender, RoutedEventArgs e)
diff --git a/TestPlugin/MainMenuCommand.cs b/TestPlugin/MainMenuCommand.cs
index cddde39fd..cd5c60c98 100644
--- a/TestPlugin/MainMenuCommand.cs
+++ b/TestPlugin/MainMenuCommand.cs
@@ -4,6 +4,7 @@
 using System.Composition;
 
 using ICSharpCode.ILSpy;
+using ICSharpCode.ILSpy.AssemblyTree;
 
 namespace TestPlugin
 {
@@ -19,11 +20,11 @@ namespace TestPlugin
 	// ToolbarOrder: controls the order in which the items appear (items are sorted by this value)
 	[ExportToolbarCommand(ToolTip = "Clears the current assembly list", ToolbarIcon = "Clear.png", ToolbarCategory = "Open", ToolbarOrder = 1.5)]
 	[Shared]
-	public class UnloadAllAssembliesCommand : SimpleCommand
+	public class UnloadAllAssembliesCommand(AssemblyTreeModel assemblyTreeModel) : SimpleCommand
 	{
 		public override void Execute(object parameter)
 		{
-			foreach (var loadedAssembly in MainWindow.Instance.AssemblyTreeModel.AssemblyList.GetAssemblies())
+			foreach (var loadedAssembly in assemblyTreeModel.AssemblyList.GetAssemblies())
 			{
 				loadedAssembly.AssemblyList.Unload(loadedAssembly);
 			}