Description
Describe the bug
As the title says, I've found a way to reproduce an annoying issue in the FrameworkElement.Loaded
and FrameworkElement.Unloaded
events, and the FrameworkElement.IsLoaded
property. Basically, the two events will fire in a completely incorrect order or will not fire at all, and consequently the IsLoaded
property will report a value that does not reflect the state of a given element.
In short:
- An element loaded in the tree has
Unloaded
raised last - An element loaded in the tree reports
IsLoaded
beingfalse
(!)
This is especially an issue because devs are relying on those events to track the lifetime of visual items, eg. to subscribe/unsubscribe to external events, and whatnot. At the moment I've had to come up with specific workarounds in my app Legere to bypass this problem, but it's really not an ideal solution, plus this could be not doable at all in other scenarios where this issue is present.
Steps to reproduce the bug
Steps to reproduce the behavior:
- Download this repro: LoadedGlitchRepro.zip
- Build and start the app with the attached debugger
- Click on any of the command bar buttons
Expected behavior
You should see the output window display the Loaded
event last for the second button you clicked. Instead you'll see Unloaded
being raised last for the control that you can clearly see being loaded.
Additional steps
- Click on some of those buttons at random and just observe the output log. See the screenshot below for an example of the output window. You can see the
Loaded
andUnloaded
events are fired in a completely unreliable and incorrect manner.
Screenshots
Version Info
- Windows 10 Pro 18362.592
- Tried targeting both the SDK 17763 and the SDK 18632
NuGet package version:
- Microsoft.UI.Xaml 2.1.190606001 (though this shouldn't be relevant here)
Windows 10 version | Saw the problem? |
---|---|
Insider Build (xxxxx) | |
November 2019 Update (18363) | |
May 2019 Update (18362) | Yes |
October 2018 Update (17763) | |
April 2018 Update (17134) | |
Fall Creators Update (16299) | |
Creators Update (15063) |
Device form factor | Saw the problem? |
---|---|
Desktop | Yes |
Mobile | |
Xbox | |
Surface Hub | |
IoT |
Additional context
The reason why I have a setup like this is that in Legere I have a series of post views that I reuse, to avoid the overhead of creating a completely new instance every time a post of a given type is loaded. They're initially all stored inside an invisible and collapsed Grid
, and lazily loaded. When one view is needed, I load it with FindName(string)
, then place it in a host Border
the user can interact with. When the user opens a post of another type, I remove that view and place it back in the original host (so that FindName
will work again, as it needs the element to be in the visual tree for it to find it), then repeat the procedure to find and swap in the new view to use.