Native WinUI and Uno differences
UnoDock ships two builds in each package: net10.0 for Uno Platform (Skia desktop,
WebAssembly, mobile) and net10.0-windows10.0.26100 for native WinUI 3 on the Windows App
SDK. The public API, layout model and behavior are the same. This page lists where the two
platforms differ, what UnoDock does about each difference, and what an application has to
know when it targets native WinUI.
Building
| Uno Platform | Native WinUI | |
|---|---|---|
| Build tool | dotnet build |
Visual Studio MSBuild (msbuild) |
| Packaging in the samples | framework-dependent dotnet <app>.dll |
unpackaged (WindowsPackageType=None), self-contained Windows App SDK |
Uno.Sdk refuses to build a WinUI class library that contains XAML with dotnet build
(error UNOB0008), because the WinUI XAML compiler needs Visual Studio MSBuild. UnoDock's
packages are therefore packed on Windows with MSBuild, and the repository's native WinUI
heads build the same way:
msbuild samples/UnoDock.Gallery/UnoDock.Gallery.csproj -restore -p:Configuration=Release -p:Platform=x64 -p:UnoDockTargetFrameworks=net10.0-windows10.0.26100.0 -p:UnoDockLibraryFrameworks=net10.0-windows10.0.26100.0
Applications that only consume the UnoDock package are not affected: an application builds the way any WinUI application does.
On Windows, the package's dependency on Uno.WinUI contributes only the small
Uno.UI.Toolkit assembly; Uno's build tasks are excluded.
XAML markup for layouts
The WinUI XAML compiler writes markup as XAML Binary Format (XBF). Two of its limits affect layouts declared in markup, and UnoDock works around both in the WinUI build:
- Members of generic base classes. XBF cannot encode a property declared on a generic
type. Layout groups inherit
Children,DockWidth,DockMinWidth,FloatingLeft,IsMaximizedand the other positioning members fromLayoutGroup<T>andLayoutPositionableGroup<T>. The WinUI build declares these members again on each concrete group (LayoutPanel,LayoutAnchorablePaneGroup,LayoutDocumentPaneGroup,LayoutAnchorablePane,LayoutDocumentPane,LayoutAnchorGroup,LayoutAnchorSide), forwarding to the base. Without this, any layout in markup fails to compile withWMC0610: The XAML Binary Format (XBF) generator reported syntax error '0x07da'. - Interface-typed collections. The compiler checks markup items against a collection's
item type through base classes only, not interfaces.
LayoutPanelholdsILayoutPanelElementitems, so a pane could not be added in markup. In the WinUI build, the content property of each group isXamlChildren, an object-typed view ofChildrenthat checks each item's type when it is added.
Markup is therefore the same on both platforms, including the content syntax. Code always
uses Children. Property-element syntax for the collection (<LayoutPanel.Children>) does
not compile on native WinUI; omit it and place the children directly in the group.
Styles, templates and code-only subclasses
Native WinUI checks a Style's or ControlTemplate's TargetType through the
application's XAML type metadata, which only describes types that appear in markup. Uno
compares the .NET types instead. On WinUI, a class that derives from DockingManager and is
only created in code is therefore known only as Control. Applying a style or template with
TargetType="dock:DockingManager" to it throws COMException 0x800F1000 ("Cannot apply a
Style with TargetType 'UnoDock.DockingManager' to an object of type 'Control'"). On the UI
thread, that ends the process.
UnoDock handles the common case itself:
- The WinUI build ships a resource dictionary that names every public UnoDock control, so the library's own types are always described.
- A
DockingManagersubclass that the metadata does not describe gets the default template through a style that targetsControl, so it renders without any markup.
Two things still need the application's help:
- Custom styles or templates for a subclass. Name the subclass once in markup, for
example in a resource dictionary, before giving it a style or template that targets
DockingManager:
```xml
```
- Nested or private subclasses. These cannot be named in markup. Do not give them a
template that targets
DockingManager; they keep the default template.
Windows and the host
| Uno Platform | Native WinUI | |
|---|---|---|
| Enumerating application windows | ApplicationHelper.Windows |
not available |
| Finding the window that hosts an element | from the enumeration | from the XamlRoot's ContentIslandEnvironment.AppWindowId |
| Window registration | optional | optional (SystemCommands.RegisterWindow still works) |
UnoDock resolves the host window without registration on both platforms. On native WinUI it owns floating windows through the host's window handle, activates it for the navigator and maps drop targets through it.
Shutdown. Windows destroys owned windows before the owner's AppWindow raises
Destroying, and a WinUI window destroyed that way ends the process. UnoDock therefore
hides and releases its floating windows as soon as the host starts to hide, owns and shows
them again if the host reappears, and closes them once the host is gone. AppWindow APIs are
not called on a window that is being destroyed.
Framework behavior UnoDock relies on
| Uno Platform | Native WinUI | |
|---|---|---|
Exception thrown by an event handler or property-changed callback that the framework raises (DataContextChanged, RegisterPropertyChangedCallback, ...) |
returns to the code that changed the value | not returned to the caller |
Exception thrown by a DispatcherQueue callback |
does not end the process | ends the process without raising UnhandledException |
Loaded |
raised when the element joins the live tree | raised on a later dispatcher turn, after the layout pass; Unloaded likewise arrives after the change that removed the element, even when the element has been added back in the meantime |
A ContentPresenter's or ContentControl's element content, a TabViewItem's header |
becomes a visual child when assigned | becomes a visual child during the next layout pass |
Changed ContentTemplate |
expanded when set | expanded during the next layout pass |
ResourceDictionary.ContainsKey and Keys.Contains |
the dictionary's own entries | also merged and theme dictionaries, and the framework's theme resources (for example ButtonBackgroundPointerOver in any dictionary) |
XamlControlsResources theme dictionaries |
visible | resolved internally, for the application's theme only |
MenuFlyoutPresenterStyle |
applied whenever the menu opens | applied when the presenter is created; the presenter is reused |
One ResourceDictionary instance under several theme keys |
allowed | rejected: a dictionary has one parent |
Focus on an element that has not been laid out |
succeeds | fails |
VisualStateGroup.CurrentStateChanged for GoToState(..., false) |
raised during the call | raised on a later turn |
Focus() on an element that already has focus |
raises focus events again | raises none |
MenuFlyout.ShowAt while the target is applying a theme change |
opens | can be ignored |
{ThemeResource} in a Style setter |
resolved from the styled element's resources | resolved from the dictionary that defines the style |
ControlTemplate without TargetType that uses {TemplateBinding} on a member Control does not declare (such as Content) |
binds | layout fails with 0x80004005 |
| Elements of a window's content tree after the window closes | reusable | unusable anywhere (0x800F1000), even when the window's Content was cleared first; only elements removed from that tree before Close() survive |
CornerRadius with negative or non-finite values |
accepted | the constructor throws ArgumentException |
| Relative URIs | accepted | WinRT URIs are absolute |
Automation pattern of ToggleMenuFlyoutItem |
Invoke | Toggle |
UnoDock keeps its behavior the same on both platforms where it can:
- Callback exceptions. UnoDock still releases partial state when a callback fails, but on WinUI such failures do not reach it. Application callbacks that must report errors should not depend on exceptions passing through the framework.
- Deferred rendering. UnoDock renders layout changes on a
DispatcherQueuecallback. If an application handler throws during that render (a template selector, a style selector, a content factory), it reachesDockingManager.RenderingFailedfirst. On native WinUI, the process then ends; handle errors in those callbacks. - Closing floating windows. A native floating window hosts its control in a disposable
root element, and the control leaves that root before the window closes, so its panes and
your content can be docked or floated again. Do the same in your own windows: remove
content you will show again from the window's tree before calling
Close(). - Re-hosting views. UnoDock moves content between panes, auto-hide flyouts, floating
windows and tab strips. It records each content host, so it can release content that WinUI
has not connected yet. Do not give the same
UIElementcontent to another host while UnoDock shows it. Refresh()applies template changes; on native WinUI their visuals appear after the next layout pass (callUpdateLayout()to force it).- Automation after a window closes. Automation notifications queued for a tab, pane or splitter are dropped when its window has closed in the meantime; WinUI fails to raise them.
- Stale
Unloaded. UnoDock ignores anUnloadedevent that arrives while the element is back in its window's tree, so a flyout or manager that is removed and re-added in one turn is not torn down afterwards. - Coordinates.
DesktopWindowCoordinatesaccepts an element that is already connected under its window content but whoseLoadedevent has not been raised yet. - Focus. WinUI raises no focus events for some programmatic focus moves, and it moves
focus to the first element of a view once the navigator has left the tree. UnoDock records a
document's editor when focus moves within it, when its content stops being active and when
its view is hidden. After the navigator commits, it returns focus that WinUI moved elsewhere
in the same view to that editor. Restoring an editor, and
SetFocuson a splitter's automation peer, lay out newly shown content first. - Theme changes. WinUI can leave existing panes on their old theme after a runtime theme change, so UnoDock re-applies the docked area's theme when a pane disagrees with it. Palette changes reach controls in a pointer-over, pressed or disabled state by re-applying the control's theme and re-entering the state.
- Theme resources. UnoDock looks up density and palette overrides in each dictionary's own entries first, then in its merged dictionaries, on both platforms.
- Menus. A docking menu whose palette changes while it exists repaints its existing
presenter. While the menu is open its
MenuFlyoutPresenterStyleis left alone and replaced when the menu next opens: replacing the style of an open menu ends the process with an access violation inside WinUI on Windows ARM64. - Images.
UriSourceToBitmapImageConverterresolves a relative URI asms-appx:///(a file in the application package) on native WinUI.
Platform services
- High contrast.
AccessibilitySettings.HighContrastChangedthrows0x80070490in an unpackaged WinUI app. UnoDock reads high contrast withSPI_GETHIGHCONTRASTon every Windows host instead. Disposeon elements. Uno'sFrameworkElementhas aDispose()method and native WinUI's does not. Code that declares its ownDispose()on an element withnewbuilds on both; on WinUI the compiler reports thatnewis not needed (CS0109).- Browser workspaces.
UnoDock.Browserruns only on Uno's WebAssembly host.
Testing
The Gallery has a native WinUI head that runs every sample and the self-test suites under
the Windows App SDK. Test code finds windows through TestWindows, which uses Uno's window
enumeration on Uno and the windows registered with UnoDock on native WinUI.
Some tests are platform-specific:
- Suites that drive another host's native input (X11 XTEST, AppKit events) do not run on native WinUI.
- Tests that need a framework callback's exception to reach UnoDock, or an
Unloadedhandler to run during a change, are compiled only for Uno. - Tests that give a test-only
DockingManagersubclass another manager's template useUsingTemplateOf, which keeps the subclass template on native WinUI. - A few assertions compare what each platform can guarantee: brush colors instead of instances where WinUI resolves a style's theme resource elsewhere, the package URI of a relative image, and one dictionary per theme key.
CI runs the native WinUI Gallery on x64, in two shards, on every pull request (see
build and verification). Each suite writes the first-chance exceptions it raised
to exceptions-<suite>.log, because native WinUI ends the process on an exception in a
dispatcher callback without reporting it. Windows ARM64 is checked by hand on a
virtual machine, not in CI.