Behavior changes to re-test
Part of Migrating to Uno Platform 7.0.
Read this if your app builds and you are planning what to re-test, or something compiles but acts or looks different on 7.0: same API, different result.
Skip it if you have no re-testing to plan. If your app already ran on Skia on every target, you can skip the first section and start at Rendering backend and drawing libraries.
Rendering differences
Because rendering moves from Canvas/CALayer/CSS to Skia, expect subtle differences and
re-baseline visual tests:
- Rendering: gradients, corner radii, shadows/elevation, and anti-aliasing render via Skia — minor pixel differences are possible.
- Text: measurement, line-breaking, bidi, kerning, and
MaxLinesgo through HarfBuzz + SkiaSharp instead ofStaticLayout/UIKit text/DOM — possible layout shifts in text-dense UI. - Lists / scrolling: managed virtualization and scrolling replace
RecyclerView/UICollectionView/CSS overflow — fling curves, edge glows, and snap points may differ. - IME / keyboard: canvas-rendered text input with a platform IME bridge replaces the
native
EditText/UITextField— re-test CJK/emoji composition and selection visuals. - Pickers / command bars / Frame: Skia/WinUI-styled rather than native-styled — appearance changes on Android/iOS.
- Animation timing: Skia interpolation replaces
CABasicAnimation— standard easing matches; exotic timing may not.
See also: Rendering is Skia everywhere.
Rendering backend and drawing libraries
Vulkan is the default rendering backend
Vulkan is the default rendering backend on Android, Linux (X11) and Windows (Win32).
Before 7.0 Vulkan was opt-in. Devices without a usable Vulkan driver fall back to other
available backends, so no configuration is needed - but the GPU path most apps actually
run on has changed, so re-test rendering on your target devices, especially custom
SKCanvas drawing and native-element interop. Vulkan can also use more memory than
OpenGL, by an amount that depends on the GPU driver; see
Startup Time and Memory.
To disable Vulkan on desktop while keeping
fallback, use .UseX11(b => b.DisableRenderingBackends(X11RenderingBackend.Vulkan)) or
.UseWin32(b => b.DisableRenderingBackends(Win32RenderingBackend.Vulkan)). To select only
OpenGL, use ForceRenderingBackend with the matching OpenGL enum value; a forced backend
does not fall back if it cannot be created. The desktop UseVulkanOnX11,
UseVulkanOnWin32, UseOpenGLOnX11, UseOpenGLOnWin32 and PreferGLESOverGLOnX11 feature
flags are no longer available. On Android, use
.UseAndroid(b => b.UseVulkan(false)) or set
FeatureConfiguration.Rendering.UseVulkanOnSkiaAndroid = false before building the host.
See Vulkan Rendering Backend.
See also: Android head uses the host builder.
SkiaSharp 4 is the default
The Uno.Sdk now resolves the whole SkiaSharp package group —
SkiaSharp, SkiaSharp.Skottie, the SkiaSharp.Views.* packages and every
SkiaSharp.NativeAssets.* — at a 4.x version; on 6.x it was 3.x with 4 available opt-in
through SkiaSharpVersion. Apps that never touch SkiaSharp directly need no change. Apps
with their own SKCanvas/SKPaint drawing code may not compile: SkiaSharp 4 turns several
long-deprecated APIs into errors, most visibly the text APIs that moved from SKPaint to
SKFont. If you pin SkiaSharp packages yourself, move all of them — managed and
native-asset alike — to the same 4.x version, since a managed/native mismatch fails at
runtime rather than at build time.
See also: SkiaSharp.Views.Uno.WinUI, SkiaSharp and HarfBuzz native assets.
GLCanvasElement subclasses compile against Silk.NET.OpenGL 2.23
GLCanvasElement subclasses compile against Silk.NET.OpenGL 2.23 (6.x used 2.16). The OpenGL calls are unchanged, but some enum types passed to them are renamed,
so code using those types needs updating — for example, gl.CullFace takes a TriangleFace instead of a
CullFaceMode:
- gl.CullFace(CullFaceMode.Back);
+ gl.CullFace(TriangleFace.Back);
Manipulation and gestures
Independently of rendering, manipulation recognition was realigned with WinUI.
See also: Cross-platform compatibility flags.
No ManipulationDelta immediately after ManipulationStarted
The distance traveled
before a manipulation is recognized is now reported through ManipulationStarted's
Cumulative and is never replayed as a delta, so a pan no longer visibly jumps by the
recognition threshold the moment it starts. This matches WinUI, where a manipulation can
complete without raising a single ManipulationDelta. Handlers that positioned content by
accumulating ManipulationDelta.Delta alone (the classic GridSplitter pattern) will now
under-track by the recognition distance — read Cumulative instead, which still measures
from the press point and is the value WinUI's own samples use.
Manipulation start thresholds changed
Touch and pen go from 15px to 10px, so panning
starts sooner. Mouse goes from 1px to 4px, matching the Win32 system drag threshold
(SM_CXDRAG), so a small wiggle during a click is no longer reported as a drag. Code that
relied on a near-zero mouse threshold to start a drag should re-test.
A touch or pen hold is no longer a tap
As on WinUI, a hold raises Holding and then
RightTapped, but no Tapped or DoubleTapped, and releasing a hold on a ListView or GridView
item raises no ItemClick and doesn't change the selection. Code that used a long press as a slow
tap should handle Holding or RightTapped instead. To get the earlier behavior back, set
Uno.WinRTFeatureConfiguration.GestureRecognizer.SuppressTapOnHold to false at startup (default
true). That does not bring back the browser's own long-press text selection and callout on the
WebAssembly host page, which 7.0 turns off. See Feature flags.
See also: ListViewBase.ItemClickCommand.
Controls and lists
ContentControl subclasses without a ControlTemplate
Uno used to bypass the
ContentPresenter and parent Content directly when a control had no template at all,
so a custom ContentControl that set its own DefaultStyleKey but shipped no
Template setter still rendered its content. That leniency is gone; as on WinUI, such a
control now renders nothing. Give it a default style with a Template, or leave
DefaultStyleKey alone so it inherits the ContentControl default template.
List selection scrolling was also realigned with WinUI.
See also: UserControl derives from Control.
A new selection is brought into view only while the list has focus
ListView and
GridView used to scroll to every newly selected item. Now, as on WinUI, setting
SelectedItem or SelectedIndex from code on a list that doesn't have focus leaves the
scroll position alone, and a focused list scrolls only as far as needed to show the item.
Clicking or keyboard navigation still brings the item into view, since they focus the list.
In Multiple mode only the first selection is brought into view. Call
ScrollIntoView(item) after selecting if you need the old behavior.
See also: ListViewBase.ItemClickCommand.
SystemBackdrop no longer rewrites your content's backgrounds
Setting Window.SystemBackdrop used to walk the whole visual tree and replace the
Background of every Panel, Border, ContentPresenter, and Control that had an opaque
SolidColorBrush with a transparent one, re-walking whenever new content was loaded. That is
not what WinUI does, and it was observable from app code — a Grid you had explicitly painted
red read back as Transparent afterwards.
7.0 matches WinUI: applying a backdrop drops the window's own root background only, and your content is left exactly as you set it.
If your app relied on the old behavior, the material will now be hidden behind your own opaque backgrounds. Make the surfaces you want the material to show through transparent yourself — typically the root
Pageor panel:<Page Background="Transparent"> <!-- your content --> </Page>This is what WinUI has always required, so the same markup works on Windows.
Backgrounds bound with
{ThemeResource}are no longer clobbered. The old walk wrote its transparent brush with local precedence, which silently stopped those backgrounds from re-resolving on a theme change. They now update correctly.Reading
Backgroundback after setting a backdrop returns your own brush again.
See also: Background is no longer on FrameworkElement.
Dependency property defaults
Dependency properties registered without a default value now follow WinUI.
Custom value types default to null
DependencyProperty.Register with no metadata, or
with a null DefaultValue, used to synthesize the zero value of any value type. It now does
what WinUI does: int, long, float, double, bool, TimeSpan, DateTimeOffset,
Point, Size, Rect, Color, Thickness, CornerRadius and the enums of WinUI's XAML
metadata (Visibility, Orientation, FontStyle, VirtualKey, ...) get their zero value,
GridLength gets 1*, and every other value type (your own structs and enums, DateTime,
Guid, decimal, Duration, Matrix, Vector2, ...) gets null, so a
(MyEnum)GetValue(...) wrapper on such a property throws until a value is set. Pass the
default explicitly, for example new PropertyMetadata(MyEnum.None). Code that already runs
on Windows is unaffected, since it needed the explicit default there. The WinUI documentation
says a value type gets "the default of the type", but the WinAppSDK runtime does that only for
the types above; the table was measured on it. SetValue(null) on such a property is now
stored rather than rejected with an InvalidOperationException, and ClearValue, or a
binding that produces null, leaves it null too. With Debug logging enabled for
Microsoft.UI.Xaml.DependencyProperty, every registration that gets a null default is
listed at startup.
See also: DependencyObject is a class, DependencyPropertyValuePrecedences.
Authentication on WebAssembly
Independently of rendering, Uno.WinUI.MSAL's WithUnoHelpers() changed on WebAssembly.
WithUnoHelpers() signs in through WebAuthenticationBroker on WebAssembly
The WASM-only
flavor of Uno.UI.MSAL was removed, and its helpers now ship in the main assembly.
PublicClientApplicationBuilder.WithUnoHelpers() still sets a browser-compatible
HttpClient factory, and AcquireTokenInteractiveParameterBuilder.WithUnoHelpers() sets a
web UI that uses the WebAuthenticationBroker popup, configured
through WinRTFeatureConfiguration.WebAuthenticationBroker. Both replace a
WithHttpClientFactory(...) or WithCustomWebUi(...) called before them, so call your own
after WithUnoHelpers() — see MSAL: WebAssembly.
On WebAssembly, WebAuthenticationBroker.AuthenticateAsync() changed how it ends.
A timeout returns ErrorHttp instead of UserCancel
When
WinRTFeatureConfiguration.WebAuthenticationBroker.AuthenticationTimeout elapses, the result is
WebAuthenticationStatus.ErrorHttp with ResponseErrorDetail 408, so it can be told apart
from the user closing the popup. Canceling the operation now also closes the popup or
<iframe>, and the <iframe> mode now honors the timeout.
Android drawables
On Android, retargeted drawables now keep their file extension in their resource name.
Retargeted drawables are renamed
Images under Assets that Uno packages as Android
drawables now include the original extension, so Assets/logo.png and Assets/logo.jpg no
longer collide and a same-named logo.svg asset is no longer shadowed by the PNG. Code that
uses the generated ids changes accordingly: Resource.Drawable.Assets_logo becomes
Resource.Drawable.Assets_logo_png. Callers of DrawableHelper.FindResourceId must now pass
the filename with its extension (for example, FindResourceId("logo.png")); FindResourceId("logo")
no longer resolves. XAML and code that load images through ms-appx:/// URIs or relative paths are unaffected.
See also: bundle:// image scheme removed.
Drawables you add under Platforms/Android/Resources/drawable no longer resolve through ms-appx:///
A lookup for ms-appx:///foo.png now searches for the drawable foo_png, not
foo. Move images you load by URI into Assets so Uno packages them, or reference the native
drawable directly (Resource.Drawable.foo).
See also: Relative URIs in XAML.
Related pages
- Removed feature flags if your app set a flag that kept the old behavior.
- WinUI API alignment for changes the compiler does report.
- App startup, activation and platform state for iOS settings and activation.
- Errors, symptoms and code search to go from an error or symptom to its fix.
- Upgrade steps to return to the main path.