diff --git a/ILSpy/App.axaml b/ILSpy/App.axaml
index 0c93eef4e..7cda7895e 100644
--- a/ILSpy/App.axaml
+++ b/ILSpy/App.axaml
@@ -8,7 +8,6 @@
xmlns:compare="using:ILSpy.Compare"
xmlns:textView="using:ILSpy.TextView"
xmlns:metaViews="using:ILSpy.Views"
- xmlns:vm="using:ILSpy.ViewModels"
xmlns:dockTheme="using:Dock.Avalonia.Themes.Simple"
xmlns:recycling="using:Avalonia.Controls.Recycling"
xmlns:docking="using:ILSpy.Docking"
@@ -47,30 +46,9 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/ILSpy/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs
index 4c6e3c50a..a951a7000 100644
--- a/ILSpy/Docking/DockWorkspace.cs
+++ b/ILSpy/Docking/DockWorkspace.cs
@@ -144,7 +144,7 @@ namespace ILSpy.Docking
NavigateToHistoryCommand = new RelayCommand(NavigateToHistory,
entry => entry != null && (history.BackEntries.Contains(entry) || history.ForwardEntries.Contains(entry)));
ShowSearchCommand = new RelayCommand(ExecuteShowSearch);
- using (ILSpy.AppEnv.AppLog.Phase("ILSpyDockFactory ctor + CreateLayout"))
+ using (ILSpy.AppEnv.AppLog.Phase("ILSpyDockFactory ctor + CreateLayout + InitLayout"))
{
factory = new ILSpyDockFactory(toolPaneRegistry);
// Prefer the user's saved layout (ILSpy.Layout.json sidecar next to
@@ -152,6 +152,11 @@ namespace ILSpy.Docking
// or it failed to deserialize. The fallback path is the same shape the
// app uses on first launch.
Layout = factory.LoadLayout(GetLayoutFilePath()) ?? factory.CreateLayout();
+ // Build the layout, then InitLayout BEFORE the chrome ever sees it, rather than
+ // letting the DockControl's InitializeFactory / InitializeLayout flags run it
+ // post-template-apply. Wiring owners/factories/locators up front means the
+ // layout is fully resolvable before the first DeferredContentControl attaches.
+ factory.InitLayout(Layout);
}
assemblyTreeModel.PropertyChanged += OnAssemblyTreePropertyChanged;
@@ -160,8 +165,6 @@ namespace ILSpy.Docking
ShowSelectedNode();
};
languageService.PropertyChanged += OnLanguagePropertyChanged;
- // Layout/factory initialization (locators, parent/factory wiring) is done by
- // the DockControl in MainWindow.axaml via InitializeFactory/InitializeLayout.
ToolPaneMenuItems = toolPaneRegistry.Panes
.Select(p => new ToolPaneMenuItem(p.Pane, factory))
diff --git a/ILSpy/Docking/ILSpyDockFactory.cs b/ILSpy/Docking/ILSpyDockFactory.cs
index 8683a047f..0162fc97d 100644
--- a/ILSpy/Docking/ILSpyDockFactory.cs
+++ b/ILSpy/Docking/ILSpyDockFactory.cs
@@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
+using Dock.Avalonia.Controls;
using Dock.Model.Controls;
using Dock.Model.Core;
using Dock.Model.Mvvm;
@@ -49,6 +50,53 @@ namespace ILSpy.Docking
this.panes = registry.Panes;
}
+ ///
+ /// Populate the locator dictionaries with explicit id-to-factory mappings before the
+ /// base init wires owners/factories down the tree, rather than relying on
+ /// to populate them post-template-apply.
+ /// Wiring the locators (and the HostWindowLocator) up front means the layout is fully
+ /// resolvable before the chrome realises any content.
+ ///
+ ///
+ /// Tool panes are [Shared] MEF singletons, so the lambdas in
+ /// /
+ /// can safely return the cached pane instance for both context and dockable
+ /// lookup. Documents and the root dock get explicit entries for navigate-by-id
+ /// (root.Navigate.Execute("Documents")).
+ ///
+ ///
+ public override void InitLayout(IDockable layout)
+ {
+ ContextLocator = new Dictionary>();
+ DockableLocator = new Dictionary>();
+ HostWindowLocator = new Dictionary> {
+ [nameof(IDockWindow)] = () => new HostWindow()
+ };
+
+ // Tool panes: the VM is its own context (no separate model class), and the
+ // dockable IS the pane instance, so both locators map id → same singleton.
+ foreach (var entry in panes)
+ {
+ var pane = entry.Pane;
+ if (string.IsNullOrEmpty(pane.Id))
+ continue;
+ ContextLocator[pane.Id] = () => pane;
+ DockableLocator[pane.Id] = () => pane;
+ }
+
+ // Documents dock + the persistent main content tab — these are the structural
+ // slots ShowSelectedNode / OpenNewTab target. Carve-out tabs (per-node spawned
+ // ContentTabPages) don't get id mappings because they're transient.
+ if (Documents is { Id: { Length: > 0 } docsId } docs)
+ DockableLocator[docsId] = () => docs;
+ if (MainTab is { Id: { Length: > 0 } tabId } mainTab)
+ DockableLocator[tabId] = () => mainTab;
+ if (layout is IRootDock { Id: { Length: > 0 } rootId } root)
+ DockableLocator[rootId] = () => root;
+
+ base.InitLayout(layout);
+ }
+
///
/// Serializes to using
/// . Best-effort: any IO or serialization
diff --git a/ILSpy/ViewModels/MainWindowViewModel.cs b/ILSpy/ViewModels/MainWindowViewModel.cs
index 184db113f..52cc61c84 100644
--- a/ILSpy/ViewModels/MainWindowViewModel.cs
+++ b/ILSpy/ViewModels/MainWindowViewModel.cs
@@ -19,7 +19,6 @@
using System.Composition;
using Dock.Model.Controls;
-using Dock.Model.Core;
using ILSpy.AssemblyTree;
using ILSpy.Docking;
@@ -41,8 +40,6 @@ namespace ILSpy.ViewModels
public UpdatePanelViewModel UpdatePanel { get; }
- public IFactory DockFactory => DockWorkspace.Factory;
-
public IRootDock DockLayout => DockWorkspace.Layout;
public System.Collections.Generic.IReadOnlyList ToolPaneMenuItems => DockWorkspace.ToolPaneMenuItems;
diff --git a/ILSpy/Views/MainWindow.axaml b/ILSpy/Views/MainWindow.axaml
index b526e738c..3ed9d08ee 100644
--- a/ILSpy/Views/MainWindow.axaml
+++ b/ILSpy/Views/MainWindow.axaml
@@ -46,9 +46,6 @@
Margin="8,0" Name="statusLabel" Text="Stand by..." />
-
+