[Regression][Skia] Image inside an open ToolTip stays blank when its source loads after the ToolTip opened
Current behavior
Regression. On Skia, an Image whose BitmapImage source is assigned (or finishes loading) after the ToolTip that contains it has opened stays blank for the whole time the tooltip is shown. The image only appears the next time the tooltip opens, once the bitmap is already loaded.
This worked in earlier 6.7 dev builds; it broke for us when moving from Uno.Sdk 6.7.0-dev.165 to 6.7.22. The tooltip/image code in our app has not changed since well before the last known-good build (the only nearby change was restyling the button that owns the tooltip, which does not touch the image).
We use the pattern to lazily load preview images for tooltips on tiles in a virtualized ItemsRepeater (thousands of items, so the previews can't be fetched eagerly):
var image = new Image { Width = 180, Height = 120, Stretch = Stretch.Uniform };
var tooltip = new ToolTip { Content = image };
tooltip.Opened += (_, _) => image.Source ??= new BitmapImage(new Uri(url));
ToolTipService.SetToolTip(tile, tooltip);First hover: tooltip opens, blank 180×120 area. Move the pointer away and back: the tooltip opens with the image.
From reading Image.cs (Skia): the decoded data is stored in _pendingImageData and only applied during MeasureOverride → TryProcessPendingSource(). The InvalidateMeasure() the image issues when the data arrives does not lead to a measure pass while it is inside the tooltip's popup, so the pending data is never applied until the popup is opened again (which measures the image fresh with the data already present).
None of the following make the image appear on the first open once the data has arrived, which suggests the popup is not running layout for its content at all after it has opened:
image.InvalidateMeasure()/InvalidateArrange()/tooltip.InvalidateMeasure()fromBitmapImage.ImageOpened, dispatched atNormalandLowpriority (Low so it lands after the image's own subscription has received the data).- Re-presenting the content (
tooltip.Content = null; tooltip.Content = image;) after the data arrived. - Assigning the source from
image.Loadedinstead ofToolTip.Opened. - Repeatedly calling
image.InvalidateMeasure(); image.UpdateLayout();on a 50 ms timer for 2 s afterBitmapImage.ImageOpened—Image.ImageOpened(raised fromTryProcessPendingSource) never fires during that time.
Expected behavior
An Image inside an open ToolTip should render its bitmap as soon as it has loaded, as it does when the same Image is anywhere else in the visual tree (and as it did before 6.7.22).
How to reproduce it (as minimally and precisely as possible)
dotnet new unoapp(Skia renderer), add aButtontoMainPage.- In code-behind:
public MainPage()
{
this.InitializeComponent();
var image = new Image { Width = 180, Height = 120, Stretch = Stretch.Uniform };
var tooltip = new ToolTip { Content = image };
// Any http(s) image works; use something that takes a moment to download to make the timing obvious.
tooltip.Opened += (_, _) => image.Source ??= new BitmapImage(new Uri("https://picsum.photos/360/240"));
ToolTipService.SetToolTip(MyButton, tooltip);
}- Run on Desktop (Windows), hover the button and wait for the tooltip.
- Observe: the tooltip opens but the image area is blank. Move the pointer off the button and back: the tooltip now shows the image.
Same result if step 2 uses image.Loaded += (_, _) => image.Source ??= ...; instead of tooltip.Opened.
Workaround ️
Don't attach the ToolTip until the bitmap is loaded, so its Image is measured for the first time with the data already available:
var bitmap = new BitmapImage(new Uri(url));
var loader = new Image { Source = bitmap }; // never enters the tree; just drives the load
bitmap.ImageOpened += (_, _) =>
{
ToolTipService.SetToolTip(tile, new ToolTip { Content = new Image { Source = bitmap, Width = 180, Height = 120 } });
GC.KeepAlive(loader);
};Note that ToolTipService only starts its show timer from PointerEntered on an element that already has a tooltip, so a tooltip attached mid-hover must be opened manually (IsOpen = true after FeatureConfiguration.ToolTip.ShowDelay elapses) or it doesn't appear until the next hover.
Renderer
- Skia
- Native
Affected platforms ️
All
Uno.Sdk version (and other relevant versions)
- Broken:
Uno.Sdk 6.7.22 - Last known good:
Uno.Sdk 6.7.0-dev.165 - .NET SDK 10.0.401,
net10.0-desktophead
IDE version
dotnet CLI (SDK 10.0.401); not IDE related
Anything else we need to know?
Relevant code: Image.cs (Skia) — InitializeImageSource stores _pendingImageData and calls InvalidateMeasure(); TryProcessPendingSource() runs only from MeasureOverride. The ToolTip popup does not seem to get a layout pass for that invalidation once it is open.
Source: unoplatform/uno