ItemsRepeater: setting ItemsSource after an unload/reload throws NullReferenceException (OnLoaded revoker reads the field)
Current behavior
After an ItemsRepeater has been unloaded and loaded again, setting ItemsSource = null (or to any other value) throws NullReferenceException from inside the setter.
Cause: the Uno-specific re-subscription in ItemsRepeater.OnLoaded stores a revoker lambda that reads the field m_itemsSourceView when it is disposed:
if (_dataSourceSubscriptionsRevoker.Disposable is null && m_itemsSourceView is not null)
{
m_itemsSourceView.CollectionChanged += OnItemsSourceViewChanged;
_dataSourceSubscriptionsRevoker.Disposable = Disposable.Create(() =>
{
m_itemsSourceView.CollectionChanged -= OnItemsSourceViewChanged; // reads the field at dispose time
});
}OnDataSourcePropertyChanged assigns m_itemsSourceView = newValue first and only then disposes the previous revoker, so on an ItemsSource = null the lambda runs against a null field and throws. In the compiled 6.6.176 Skia runtime this is ItemsRepeater.<OnLoaded>b__NN_0, whose IL is ldarg.0; ldfld m_itemsSourceView; ...; callvirt ItemsSourceView::remove_CollectionChanged (same source at tag 6.6.184 and on master).
Because the throw happens before -= runs, the previous ItemsSourceView also stays subscribed to OnItemsSourceViewChanged.
The revoker that OnDataSourcePropertyChanged itself installs captures the local newValue instead, and does not have this problem.
Visible symptom: NavigationView.UpdatePaneDisplayMode re-hosts the pane (unloading/reloading the footer ItemsRepeater, which arms the lambda) and then calls UpdateFooterRepeaterItemsSource, whose first step clears the footer repeater's ItemsSource. The exception aborts that method before the footer is repopulated, so the NavigationView footer (e.g. Settings and any FooterMenuItems) renders empty after a pane display-mode change, e.g. when the window is resized across an adaptive breakpoint.
Expected behavior
Changing ItemsSource after an unload/reload cycle works as it does before one, and the previous items source is unsubscribed.
How to reproduce it (as minimally and precisely as possible)
var repeater = new ItemsRepeater { ItemsSource = new ObservableCollection<string> { "a", "b" } };
var root = new Border { Child = repeater };
// put root in the window and wait for it to load
root.Child = new TextBlock(); // unload the repeater
root.Child = repeater; // load it again
repeater.ItemsSource = null; // NullReferenceExceptionWorkaround ️
After a NavigationView pane display-mode change, set FooterMenuItemsSource to the control's own FooterMenuItems and back to null; that routes to UpdateFooterRepeaterItemsSource a second time without re-hosting the pane, and the second pass completes because the repeater's ItemsSource is already null.
Renderer
- Skia
- Native
Affected platforms ️
All platforms (the code is under #if HAS_UNO; observed on Desktop (Windows) Skia)
Uno.Sdk version (and other relevant versions)
Uno.Sdk 6.6.33 / Uno.WinUI 6.6.176; unchanged on master.
Anything else we need to know?
A PR with the fix and a runtime test follows.
Source: unoplatform/uno