diff --git a/Directory.Packages.props b/Directory.Packages.props
index 4a8615af4..106d4c6a5 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -25,8 +25,10 @@
+
+
diff --git a/ILSpy.Tests/Docking/DockWorkspaceTests.cs b/ILSpy.Tests/Docking/DockWorkspaceTests.cs
new file mode 100644
index 000000000..9c204317e
--- /dev/null
+++ b/ILSpy.Tests/Docking/DockWorkspaceTests.cs
@@ -0,0 +1,48 @@
+// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using Avalonia.Headless.NUnit;
+
+using AwesomeAssertions;
+
+using ILSpy.AppEnv;
+using ILSpy.Docking;
+
+using NUnit.Framework;
+
+namespace ICSharpCode.ILSpy.Tests.Docking;
+
+// DockWorkspace is the MEF-shared wrapper that holds the Dock.Avalonia factory + the root
+// layout. If MEF can't construct it (e.g., missing AssemblyTreeModel / SearchPaneModel /
+// AnalyzerTreeViewModel imports), the entire dock surface never materializes. Asserting
+// at the export layer catches that before we wire it into the DockControl.
+[TestFixture]
+public class DockWorkspaceTests
+{
+ [AvaloniaTest]
+ public void DockWorkspace_resolves_and_exposes_root_layout_with_three_tool_panes()
+ {
+ var workspace = AppComposition.Current.GetExport();
+ workspace.Should().NotBeNull("DockWorkspace is [Export][Shared] in ILSpy.Docking.");
+
+ workspace.Layout.Should().NotBeNull("ILSpyDockFactory.CreateLayout() wires the root dock in the ctor.");
+ workspace.Factory.Should().NotBeNull();
+ workspace.ToolPaneMenuItems.Should().HaveCount(3,
+ "AssemblyTree, Search, and Analyzers are the three tool panes wired at this point.");
+ }
+}
diff --git a/ILSpy/Analyzers/AnalyzerTreeView.axaml b/ILSpy/Analyzers/AnalyzerTreeView.axaml
new file mode 100644
index 000000000..62dce9e1c
--- /dev/null
+++ b/ILSpy/Analyzers/AnalyzerTreeView.axaml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/ILSpy/Analyzers/AnalyzerTreeView.axaml.cs b/ILSpy/Analyzers/AnalyzerTreeView.axaml.cs
new file mode 100644
index 000000000..74f32f7fc
--- /dev/null
+++ b/ILSpy/Analyzers/AnalyzerTreeView.axaml.cs
@@ -0,0 +1,30 @@
+// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using Avalonia.Controls;
+
+namespace ILSpy.Analyzers
+{
+ public partial class AnalyzerTreeView : UserControl
+ {
+ public AnalyzerTreeView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/ILSpy/Analyzers/AnalyzerTreeViewModel.cs b/ILSpy/Analyzers/AnalyzerTreeViewModel.cs
new file mode 100644
index 000000000..c4f638ea3
--- /dev/null
+++ b/ILSpy/Analyzers/AnalyzerTreeViewModel.cs
@@ -0,0 +1,37 @@
+// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System.Composition;
+
+using ILSpy.ViewModels;
+
+namespace ILSpy.Analyzers
+{
+ [Export]
+ [Shared]
+ public class AnalyzerTreeViewModel : ToolPaneModel
+ {
+ public const string PaneContentId = "Analyzer";
+
+ public AnalyzerTreeViewModel()
+ {
+ Id = PaneContentId;
+ Title = "Analyzer";
+ }
+ }
+}
diff --git a/ILSpy/App.axaml b/ILSpy/App.axaml
index dd927346e..8b274a36e 100644
--- a/ILSpy/App.axaml
+++ b/ILSpy/App.axaml
@@ -2,14 +2,29 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ILSpy.App"
xmlns:local="using:ILSpy"
+ xmlns:tree="using:ILSpy.AssemblyTree"
+ xmlns:search="using:ILSpy.Search"
+ xmlns:analyzers="using:ILSpy.Analyzers"
+ xmlns:dockTheme="using:Dock.Avalonia.Themes.Fluent"
RequestedThemeVariant="Default">
+
+
+
+
+
+
+
+
+
-
+
+
+
\ No newline at end of file
diff --git a/ILSpy/AssemblyTree/AssemblyTreeModel.cs b/ILSpy/AssemblyTree/AssemblyTreeModel.cs
index 04cea8e00..3ea4677a4 100644
--- a/ILSpy/AssemblyTree/AssemblyTreeModel.cs
+++ b/ILSpy/AssemblyTree/AssemblyTreeModel.cs
@@ -18,6 +18,7 @@
using System;
using System.Composition;
+using System.Runtime.Serialization;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -26,21 +27,34 @@ using ICSharpCode.ILSpyX.Settings;
using ICSharpCode.ILSpyX.TreeView;
using ILSpy.TreeNodes;
+using ILSpy.ViewModels;
namespace ILSpy.AssemblyTree
{
[Export]
[Shared]
- public partial class AssemblyTreeModel : ObservableObject
+ public partial class AssemblyTreeModel : ToolPaneModel
{
+ public const string PaneContentId = "AssemblyTree";
+
[ObservableProperty]
+ [property: IgnoreDataMember]
private SharpTreeNode? root;
[ObservableProperty]
+ [property: IgnoreDataMember]
private SharpTreeNode? selectedItem;
+ [IgnoreDataMember]
public AssemblyList? AssemblyList { get; private set; }
+ public AssemblyTreeModel()
+ {
+ Id = PaneContentId;
+ Title = "Assemblies";
+ CanClose = false;
+ }
+
public void Initialize()
{
var settings = ILSpySettings.Load();
diff --git a/ILSpy/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs
new file mode 100644
index 000000000..cefcdcf23
--- /dev/null
+++ b/ILSpy/Docking/DockWorkspace.cs
@@ -0,0 +1,115 @@
+// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Composition;
+
+using Dock.Model.Controls;
+using Dock.Model.Core;
+using Dock.Model.Core.Events;
+
+using ILSpy.Analyzers;
+using ILSpy.AssemblyTree;
+using ILSpy.Search;
+using ILSpy.ViewModels;
+
+namespace ILSpy.Docking
+{
+ [Export]
+ [Shared]
+ public class DockWorkspace
+ {
+ readonly ILSpyDockFactory factory;
+
+ public IFactory Factory => factory;
+
+ public IRootDock Layout { get; }
+
+ public IReadOnlyList ToolPaneMenuItems { get; }
+
+ [ImportingConstructor]
+ public DockWorkspace(
+ AssemblyTreeModel assemblyTreeModel,
+ SearchPaneModel searchPaneModel,
+ AnalyzerTreeViewModel analyzerTreeViewModel)
+ {
+ factory = new ILSpyDockFactory(assemblyTreeModel, searchPaneModel, analyzerTreeViewModel);
+ Layout = factory.CreateLayout();
+ // Layout/factory initialization (locators, parent/factory wiring) is done by
+ // the DockControl in MainWindow.axaml via InitializeFactory/InitializeLayout.
+
+ ToolPaneMenuItems = new List {
+ new(assemblyTreeModel, factory),
+ new(searchPaneModel, factory),
+ new(analyzerTreeViewModel, factory),
+ };
+
+ factory.DockableAdded += OnDocumentMembershipChanged;
+ factory.DockableRemoved += OnDocumentMembershipChanged;
+ factory.DockableClosed += OnDocumentMembershipChanged;
+ factory.DockableClosing += OnDockableClosing;
+
+ // TODO: layout persistence (load on startup, save on exit). DockSerializer.SystemTextJson
+ // trips System.Text.Json's MaxDepth=64 limit on our layout because Dock's JsonConverterList
+ // calls JsonSerializer.Serialize per list element, which doesn't preserve the writer's
+ // reference-tracking state between calls — so Owner/Factory back-refs aren't deduplicated as
+ // $ref. Options when revisiting: try Dock.Serializer.Newtonsoft (better cycle handling),
+ // or build a custom JsonSerializerOptions with MaxDepth raised + replicate Dock's internal
+ // polymorphic resolver.
+ }
+
+ void OnDocumentMembershipChanged(object? sender, EventArgs e) => UpdateLastDocumentCanClose();
+
+ // Convert a tool pane's "close" (X button) into "hide" so the Window menu can restore it.
+ // Documents are still closed for real — they get garbage-collected.
+ void OnDockableClosing(object? sender, DockableClosingEventArgs e)
+ {
+ if (e.Dockable is ToolPaneModel toolPane)
+ {
+ e.Cancel = true;
+ factory.HideDockable(toolPane);
+ }
+ }
+
+ // When only one document is open, make it un-closeable so the user can't end up with an
+ // empty document area.
+ void UpdateLastDocumentCanClose()
+ {
+ var docs = factory.Documents?.VisibleDockables;
+ if (docs == null)
+ return;
+ bool canClose = docs.Count > 1;
+ foreach (var d in docs)
+ {
+ d.CanClose = canClose;
+ }
+ }
+
+ public TabPageModel OpenNewTab(TabPageModel tab)
+ {
+ if (factory.Documents != null)
+ {
+ factory.AddDockable(factory.Documents, tab);
+ factory.SetActiveDockable(tab);
+ factory.SetFocusedDockable(factory.Documents, tab);
+ }
+ return tab;
+ }
+ }
+}
diff --git a/ILSpy/Docking/ILSpyDockFactory.cs b/ILSpy/Docking/ILSpyDockFactory.cs
new file mode 100644
index 000000000..1ebd847fd
--- /dev/null
+++ b/ILSpy/Docking/ILSpyDockFactory.cs
@@ -0,0 +1,113 @@
+// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using Dock.Model.Controls;
+using Dock.Model.Core;
+using Dock.Model.Mvvm;
+using Dock.Model.Mvvm.Controls;
+
+using ILSpy.Analyzers;
+using ILSpy.AssemblyTree;
+using ILSpy.Search;
+
+namespace ILSpy.Docking
+{
+ public class ILSpyDockFactory : Factory
+ {
+ readonly AssemblyTreeModel assemblyTreeModel;
+ readonly SearchPaneModel searchPaneModel;
+ readonly AnalyzerTreeViewModel analyzerTreeViewModel;
+
+ public IDocumentDock? Documents { get; private set; }
+
+ public ILSpyDockFactory(
+ AssemblyTreeModel assemblyTreeModel,
+ SearchPaneModel searchPaneModel,
+ AnalyzerTreeViewModel analyzerTreeViewModel)
+ {
+ this.assemblyTreeModel = assemblyTreeModel;
+ this.searchPaneModel = searchPaneModel;
+ this.analyzerTreeViewModel = analyzerTreeViewModel;
+ }
+
+ public override IRootDock CreateLayout()
+ {
+ var leftToolDock = new ToolDock {
+ Id = "LeftTools",
+ Proportion = 0.25,
+ VisibleDockables = CreateList(assemblyTreeModel),
+ ActiveDockable = assemblyTreeModel,
+ Alignment = Alignment.Left,
+ };
+
+ var topToolDock = new ToolDock {
+ Id = "TopTools",
+ Proportion = 0.2,
+ VisibleDockables = CreateList(searchPaneModel),
+ ActiveDockable = searchPaneModel,
+ Alignment = Alignment.Top,
+ };
+
+ var documents = new DocumentDock {
+ Id = "Documents",
+ Title = "Documents",
+ IsCollapsable = false,
+ Proportion = 0.6,
+ };
+ Documents = documents;
+
+ var bottomToolDock = new ToolDock {
+ Id = "BottomTools",
+ Proportion = 0.2,
+ VisibleDockables = CreateList(analyzerTreeViewModel),
+ ActiveDockable = analyzerTreeViewModel,
+ Alignment = Alignment.Bottom,
+ };
+
+ var rightVertical = new ProportionalDock {
+ Id = "RightArea",
+ Orientation = Orientation.Vertical,
+ Proportion = 0.75,
+ VisibleDockables = CreateList(
+ topToolDock,
+ new ProportionalDockSplitter { Id = "TopSplitter" },
+ documents,
+ new ProportionalDockSplitter { Id = "BottomSplitter" },
+ bottomToolDock),
+ };
+
+ var horizontal = new ProportionalDock {
+ Id = "MainLayout",
+ Orientation = Orientation.Horizontal,
+ VisibleDockables = CreateList(
+ leftToolDock,
+ new ProportionalDockSplitter { Id = "LeftSplitter" },
+ rightVertical),
+ };
+
+ var root = CreateRootDock();
+ root.Id = "Root";
+ root.IsCollapsable = false;
+ root.VisibleDockables = CreateList(horizontal);
+ root.DefaultDockable = horizontal;
+ root.ActiveDockable = horizontal;
+
+ return root;
+ }
+ }
+}
diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj
index c6eb08ba5..105dbbcaf 100644
--- a/ILSpy/ILSpy.csproj
+++ b/ILSpy/ILSpy.csproj
@@ -34,7 +34,9 @@
+
+
diff --git a/ILSpy/Search/SearchPane.axaml b/ILSpy/Search/SearchPane.axaml
new file mode 100644
index 000000000..1a9912b6c
--- /dev/null
+++ b/ILSpy/Search/SearchPane.axaml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/ILSpy/Search/SearchPane.axaml.cs b/ILSpy/Search/SearchPane.axaml.cs
new file mode 100644
index 000000000..18d818f9a
--- /dev/null
+++ b/ILSpy/Search/SearchPane.axaml.cs
@@ -0,0 +1,30 @@
+// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using Avalonia.Controls;
+
+namespace ILSpy.Search
+{
+ public partial class SearchPane : UserControl
+ {
+ public SearchPane()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/ILSpy/Search/SearchPaneModel.cs b/ILSpy/Search/SearchPaneModel.cs
new file mode 100644
index 000000000..ce2b4bef2
--- /dev/null
+++ b/ILSpy/Search/SearchPaneModel.cs
@@ -0,0 +1,37 @@
+// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System.Composition;
+
+using ILSpy.ViewModels;
+
+namespace ILSpy.Search
+{
+ [Export]
+ [Shared]
+ public class SearchPaneModel : ToolPaneModel
+ {
+ public const string PaneContentId = "Search";
+
+ public SearchPaneModel()
+ {
+ Id = PaneContentId;
+ Title = "Search";
+ }
+ }
+}
diff --git a/ILSpy/ViewModels/MainWindowViewModel.cs b/ILSpy/ViewModels/MainWindowViewModel.cs
index 545e5bd3b..50da5d803 100644
--- a/ILSpy/ViewModels/MainWindowViewModel.cs
+++ b/ILSpy/ViewModels/MainWindowViewModel.cs
@@ -18,7 +18,11 @@
using System.Composition;
+using Dock.Model.Controls;
+using Dock.Model.Core;
+
using ILSpy.AssemblyTree;
+using ILSpy.Docking;
namespace ILSpy.ViewModels
{
@@ -28,12 +32,21 @@ namespace ILSpy.ViewModels
{
public AssemblyTreeModel AssemblyTreeModel { get; }
+ public DockWorkspace DockWorkspace { get; }
+
+ public IFactory DockFactory => DockWorkspace.Factory;
+
+ public IRootDock DockLayout => DockWorkspace.Layout;
+
+ public System.Collections.Generic.IReadOnlyList ToolPaneMenuItems => DockWorkspace.ToolPaneMenuItems;
+
public string Title => "ILSpy";
[ImportingConstructor]
- public MainWindowViewModel(AssemblyTreeModel assemblyTreeModel)
+ public MainWindowViewModel(AssemblyTreeModel assemblyTreeModel, DockWorkspace dockWorkspace)
{
AssemblyTreeModel = assemblyTreeModel;
+ DockWorkspace = dockWorkspace;
}
}
}
diff --git a/ILSpy/ViewModels/TabPageModel.cs b/ILSpy/ViewModels/TabPageModel.cs
new file mode 100644
index 000000000..7db529e09
--- /dev/null
+++ b/ILSpy/ViewModels/TabPageModel.cs
@@ -0,0 +1,26 @@
+// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using Dock.Model.Mvvm.Controls;
+
+namespace ILSpy.ViewModels
+{
+ public abstract class TabPageModel : Document
+ {
+ }
+}
diff --git a/ILSpy/ViewModels/ToolPaneMenuItem.cs b/ILSpy/ViewModels/ToolPaneMenuItem.cs
new file mode 100644
index 000000000..66d1a5791
--- /dev/null
+++ b/ILSpy/ViewModels/ToolPaneMenuItem.cs
@@ -0,0 +1,59 @@
+// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using CommunityToolkit.Mvvm.ComponentModel;
+
+using Dock.Model.Controls;
+using Dock.Model.Core;
+
+namespace ILSpy.ViewModels
+{
+ public class ToolPaneMenuItem : ObservableObject
+ {
+ readonly ToolPaneModel pane;
+ readonly IFactory factory;
+
+ public ToolPaneMenuItem(ToolPaneModel pane, IFactory factory)
+ {
+ this.pane = pane;
+ this.factory = factory;
+ factory.DockableHidden += OnFactoryVisibilityChanged;
+ factory.DockableRestored += OnFactoryVisibilityChanged;
+ }
+
+ public string? Title => pane.Title;
+
+ public bool IsPaneVisible {
+ get => pane.Owner is not IRootDock;
+ set {
+ if (value == IsPaneVisible)
+ return;
+ if (value)
+ factory.RestoreDockable(pane);
+ else
+ factory.HideDockable(pane);
+ OnPropertyChanged();
+ }
+ }
+
+ void OnFactoryVisibilityChanged(object? sender, System.EventArgs e)
+ {
+ OnPropertyChanged(nameof(IsPaneVisible));
+ }
+ }
+}
diff --git a/ILSpy/ViewModels/ToolPaneModel.cs b/ILSpy/ViewModels/ToolPaneModel.cs
new file mode 100644
index 000000000..7fce1a6ac
--- /dev/null
+++ b/ILSpy/ViewModels/ToolPaneModel.cs
@@ -0,0 +1,26 @@
+// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using Dock.Model.Mvvm.Controls;
+
+namespace ILSpy.ViewModels
+{
+ public abstract class ToolPaneModel : Tool
+ {
+ }
+}
diff --git a/ILSpy/Views/MainMenu.axaml b/ILSpy/Views/MainMenu.axaml
index f97224195..c27081e23 100644
--- a/ILSpy/Views/MainMenu.axaml
+++ b/ILSpy/Views/MainMenu.axaml
@@ -2,8 +2,10 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:vm="using:ILSpy.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="23"
- x:Class="ILSpy.MainMenu">
+ x:Class="ILSpy.MainMenu"
+ x:DataType="vm:MainWindowViewModel">
diff --git a/ILSpy/Views/MainWindow.axaml b/ILSpy/Views/MainWindow.axaml
index c2599e31f..021c95f04 100644
--- a/ILSpy/Views/MainWindow.axaml
+++ b/ILSpy/Views/MainWindow.axaml
@@ -2,6 +2,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ILSpy.ViewModels"
xmlns:local="using:ILSpy"
+ xmlns:tree="using:ILSpy.AssemblyTree"
+ xmlns:dock="using:Dock.Avalonia.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="500"
@@ -29,11 +31,9 @@
Margin="8,0" Name="statusLabel" Text="Stand by..." />
-
-
-
-
+