-
Notifications
You must be signed in to change notification settings - Fork 837
Description
Current behavior
When using BrowserHtmlElement as Content of a ContentControl (or derived class) on WASM with Skia renderer, the native HTML element is not displayed. The element remains in the hidden uno-native-element-store div and is never moved to the visible uno-native-element-host.
Expected behavior
The BrowserHtmlElement should be automatically attached to the native element host and displayed correctly, similar to how it works described in the documentation.
How to reproduce?
- Create a
ContentControl(or derived class like custom control) - Create a
BrowserHtmlElementand set it asContent - The element is created but not visible
public partial class MyControl : ContentControl
{
public void SetupNativeElement()
{
var htmlElement = BrowserHtmlElement.CreateHtmlElement("div");
this.Content = htmlElement;
// Element is now in uno-native-element-store (hidden)
// but never moved to uno-native-element-host (visible)
}
}Root Cause Analysis
After investigating the Uno source code, I found the following issues:
1. ContentPresenter.skia.cs - TryRegisterNativeElement
In ContentPresenter.skia.cs, the TryRegisterNativeElement method checks IsInLiveTree before calling AttachNativeElement():
if (IsInLiveTree)
{
//If in visual tree, attach immediately. If not, don't attach since Enter will attach later.
AttachNativeElement();
}The comment says "Enter will attach later", but this doesn't happen reliably when using ContentControl.
2. Missing AttachNativeElement call
The AttachNativeElement() is supposed to be called either:
- Immediately if
IsInLiveTreeis true when content is set - Later in
EnterImplwhenIsNativeHostis true
However, when setting Content on a ContentControl, the content flows to the internal ContentPresenter via template binding. The timing seems to cause IsNativeHost to not be set correctly by the time EnterImpl runs.
3. DOM State
Looking at the browser DOM:
- Element is created in
uno-native-element-store(hasdisplay: none) uno-native-element-hostis never created (created lazily on first attach)- The JavaScript
attachNativeElement()function that moves elements from store to host is never called
Workaround
The following workaround manually calls the JavaScript functions:
private void AttachNativeElementManually()
{
var position = this.TransformToVisual(null).TransformPoint(new Point(0, 0));
var x = position.X.ToString(CultureInfo.InvariantCulture);
var y = position.Y.ToString(CultureInfo.InvariantCulture);
var width = ActualWidth.ToString(CultureInfo.InvariantCulture);
var height = ActualHeight.ToString(CultureInfo.InvariantCulture);
var javascript = $@"
var elementId = element.id;
var api = globalThis.Uno.UI.NativeElementHosting.BrowserHtmlElement;
// 1. Attach element to host
api.attachNativeElement(elementId);
// 2. Position the element
api.arrangeNativeElement(elementId, {x}, {y}, {width}, {height});
// 3. Set clip-path for this area
var path = 'M {x} {y} L ' + ({x}+{width}) + ' {y} L ' + ({x}+{width}) + ' ' + ({y}+{height}) + ' L {x} ' + ({y}+{height}) + ' Z';
api.setSvgClipPathForNativeElementHost(path, 'nonzero');
";
_htmlElement.ExecuteJavascript(javascript);
}Environment
- Uno.Sdk: 5.5 / 5.6 (tested with both)
- .NET: 9.0 / 10.0
- Platform: WebAssembly with Skia renderer
- Browser: Chrome, Safari (all browsers affected)
Additional Context
This issue specifically affects WASM with Skia renderer. The ContentPresenter.wasm.cs (for native WASM renderer) has different, simpler logic that might work correctly, but ContentPresenter.skia.cs (used for Skia-based WASM) has the timing issue.
The native element hosting extension (BrowserNativeElementHostingExtension) and JavaScript interop (Uno.Runtime.Wasm.js) work correctly when called directly - the issue is that ContentPresenter doesn't call them.