English | 日本語 | 简体中文 | 繁體中文 | 한국어 |
Navigation and Workspace
ZYC.Framework separates what to open from where to show it. A URI describes the target content. A tab factory creates the tab instance for that URI. A workspace decides which tab surface receives the instance.
Mental Model
| Concept | Runtime role |
|---|---|
| URI | The address of a feature, file, page, or tool. Examples include zyc://..., file://..., and module-owned schemes. |
ITabItemFactory |
Checks whether it can handle a URI and creates an ITabItemInstance. |
ITabItemInstance |
Owns tab identity, title, icon, view, lifecycle, and close behavior. |
ITabManager |
Coordinates URI navigation, tab creation, reuse, focus, close, reload, move, and restore. |
WorkspaceNode |
Represents one node in the workspace layout tree. Leaf nodes contain navigation state. |
IParallelWorkspaceManager |
Splits, merges, focuses, swaps, resets, and applies workspace layouts. |
Navigation Flow
graph TD
A["Menu command, URI bar, startup URI, or module code"] --> B["ITabManager.NavigateAsync(...)"]
B --> C["Choose focused workspace or explicit workspaceId"]
C --> D["TabManager.InternalNavigateAsync(...)"]
D --> E["Find matching ITabItemFactory"]
E --> F["Reuse existing singleton tab when allowed"]
E --> G["Create and load new ITabItemInstance"]
G --> H["Attach tab to workspace"]
F --> I["Focus tab"]
H --> I
I --> J["Publish NavigateCompletedEvent"]
NavigateAsync(Uri) navigates in the currently focused workspace. NavigateAsync(Guid workspaceId, Uri uri) targets a specific workspace.
URI Routing and Factories
Factories are registered through ITabItemFactoryManager, usually from Module.LoadAsync:
public override Task LoadAsync(ILifetimeScope lifetimeScope)
{
lifetimeScope.RegisterTabItemFactory<ReportsTabItemFactory>();
return Task.CompletedTask;
}
TabItemFactoryBase uses TabItemRouteAttribute for route matching:
[RegisterSingleInstance]
[TabItemRoute(Host = ReportsModuleConstants.Host)]
internal class ReportsTabItemFactory : TabItemFactoryBase
{
public override async Task<ITabItemInstance> CreateTabItemInstanceAsync(
TabItemCreationContext context)
{
await Task.CompletedTask;
return context.Resolve<ReportsTabItem>(
new TypedParameter(
typeof(TabReference),
new TabReference(context.Uri)));
}
}
Important behavior:
- Factories are checked in manager order.
TabItemRouteAttributecan matchScheme,Host,Path, andPathMatch.TabItemFactoryBase.IsSingledefaults totrue.- If a singleton factory matches a URI that is already open, the existing tab is reused.
- If no factory matches, the host opens the built-in Not Found tab.
- If creation or loading throws, the host opens the built-in Error tab.
Workspace Selection
The focused workspace is stored in ParallelWorkspaceState.FocusedWorkspaceId. IParallelWorkspaceManager.GetFocusedWorkspace() resolves the active leaf node and falls back to the first available leaf if the stored id is no longer valid.
The UI changes focus when a workspace menu button, empty workspace surface, URI bar, or tab surface is interacted with. Module code can also target a workspace explicitly:
var workspace = parallelWorkspaceManager.GetFocusedWorkspace();
await tabManager.NavigateAsync(workspace.Id, ReportsModuleConstants.Uri);
Use the overload with workspaceId when a command is clearly tied to a specific workspace. Use the plain NavigateAsync(Uri) overload when the command should follow the user's current focus.
Workspace Layout Tree
The workspace layout is a tree of WorkspaceNode objects:
| Property | Meaning |
|---|---|
Id |
Stable workspace node identity. |
Left / Right |
Child nodes. A node with no children is a leaf workspace. |
IsHorizontal |
Split orientation for child nodes. |
Ratio |
Split ratio between child nodes. |
IsSplitterLocked |
Prevents splitter dragging while keeping the split visible. |
NavigationState |
Per-workspace tab URIs, focus URI, and history. |
IsNavigationBarVisible |
Controls whether the workspace navigation bar is visible. |
ParallelWorkspaceView is both the visual host and the IParallelWorkspaceManager implementation. Each leaf workspace resolves a TabManagerView, and each TabManagerView displays the tab collection for its WorkspaceNode.
Split, Merge, and Layout Operations
IParallelWorkspaceManager owns layout operations:
| Operation | Effect |
|---|---|
SplitHorizontalAsync |
Splits a leaf into side-by-side workspaces. |
SplitVerticalAsync |
Splits a leaf into stacked workspaces. |
MergeAsync |
Merges a workspace back into its parent structure when possible. |
MergeAllAsync |
Collapses the layout to one workspace. |
ToggleOrientationAsync |
Toggles a parent split orientation. |
SwapAsync |
Swaps a workspace with the related sibling position. |
ApplyLayoutAsync |
Rebuilds the layout from a saved WorkspaceNode tree. |
When a workspace is removed, ParallelWorkspaceView moves its tabs to a fallback workspace through ITabManager.MoveAllTabItemInstances(...) before detaching the workspace view.
State Restore
Startup restore happens after the workspace visual tree is ready:
ParallelWorkspaceViewcreates the rootWorkspaceView.- Each leaf workspace resolves a
TabManagerView. TabManager.RestoreStateAsync()reopens saved tab URIs from every leafNavigationState.- The previously focused URI is focused when possible.
TabManagerRestoreCompletedis published.
Startup URI handling waits for TabManagerRestoreCompleted, so protocol or command-line navigation does not race against tab/workspace restoration.
Moving Tabs Between Workspaces
Tabs can move in three ways:
ITabManager.MoveTabItemInstance(instance, from, to)moves one tab to another workspace.ITabManager.MoveTabItemInstance(source, target, position)reorders tabs or moves them relative to a target tab.ITabManager.MoveAllTabItemInstances(from, to)moves all tabs when a workspace is removed.
The built-in tab header context menu uses IMoveWorkspaceTabItemHeaderContextMenuItemManager to build the "move to workspace" targets. Drag/drop uses IDropActionProvider and DropOrchestrator; the built-in TabManagerDropProvider handles tab movement payloads.
Workspace Menus
There are two workspace menu surfaces:
| Surface | Manager | Notes |
|---|---|---|
| Workspace navigation drop-down | IWorkspaceMenuManager |
Active visible menu near the workspace index. Built-in items include reset, split, merge, swap, orientation toggle, and focus. |
| Workspace context menu manager | IWorkspaceContextMenuManager |
Provides recursive sorting by Anchor and Priority. Wire a view surface before assuming items appear in the current blank-area context menu. |
Use IWorkspaceMenuManager.RegisterItem<T>() for commands that should appear in the visible workspace menu. Use IWorkspaceContextMenuManager only when you are extending or wiring a context-menu surface.
Practical Rules
- Navigate by URI; do not instantiate views directly from menu commands.
- Register a tab factory for every URI surface a module owns.
- Use
NavigateAsync(Uri)for "open in focused workspace" behavior. - Use
NavigateAsync(Guid, Uri)when the command is workspace-specific. - Treat
WorkspaceNode.NavigationStateas the persisted tab state for that workspace. - Wait for
TabManagerRestoreCompletedbefore running startup navigation that depends on restored tabs. - Move tabs through
ITabManager, not by editing UI collections.