-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WinUI: TabbedPage pages provided by a DataTemplate crash when swapping to a different tab. #14572
Comments
Clues: |
I made a workaround. To help manage expectations please understand it's hot off the press, but I've hammered the using System.Collections;
using System.Collections.Specialized;
using System.Runtime.CompilerServices;
namespace FunctionZero.Maui.MvvmZero.Workaround
{
/// <summary>
/// An alternative for TabbedPage for people that run into this bug: https://github.com/dotnet/maui/issues/14572
/// </summary>
public class AdaptedTabbedPage : TabbedPage
{
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
// If the template changes, use it to rebuild the pages.
if (propertyName == nameof(ItemTemplate))
RebuildPages();
}
private void RebuildPages()
{
Children.Clear();
if (ItemsSource != null)
foreach (var item in ItemsSource)
{
var page = GetPageForItem(item);
if (page != null)
Children.Add(page);
}
}
/// <summary>
/// For a given item (i.e. a ViewModel) use the ItemTemplate to get the Page,
/// and set the BindingContext.
/// </summary>
/// <param name="item">the item to give to the ItemTemplate</param>
/// <returns>A page associated with the item.</returns>
private Page GetPageForItem(object item)
{
Page retval = null;
if (ItemTemplate != null)
{
if (this.ItemTemplate is DataTemplateSelector selector)
retval = (Page)selector.SelectTemplate(item, this).CreateContent();
else
retval = (Page)this.ItemTemplate?.CreateContent();
retval.BindingContext = item;
}
return retval;
}
/// <summary>
/// ATTENTION: Hiding base implementation!
/// </summary>
public static new readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(AdaptedTabbedPage), null, BindingMode.OneWay, null, ItemsSourcePropertyChanged);
/// <summary>
/// ATTENTION: Hiding base implementation!
/// </summary>
public new IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
/// <summary>
/// If the ItemsSource changes ...
/// 1. Remove any existing Pages.
/// 2. Detach from the previous ItemsSource if it was INotifyCollectionChanged.
/// 2. Regenerate new Pages for any new items.
/// 3. Attach to the previous ItemsSource if it was INotifyCollectionChanged.
/// </summary>
private static void ItemsSourcePropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
{
var self = (AdaptedTabbedPage)bindable;
self.Children.Clear();
if (oldvalue is INotifyCollectionChanged oldCollection)
oldCollection.CollectionChanged -= self.ItemsSourceCollectionChanged;
if (newvalue is IEnumerable newCollection)
{
self.RebuildPages();
if (newvalue is INotifyCollectionChanged newObservable)
newObservable.CollectionChanged += self.ItemsSourceCollectionChanged;
}
}
private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
var insertIndex = e.NewStartingIndex;
foreach (var newItem in e.NewItems)
{
var page = GetPageForItem(newItem);
this.Children.Insert(insertIndex++, page);
}
break;
case NotifyCollectionChangedAction.Remove:
for (int c = 0; c < e.OldItems.Count; c++)
{
this.Children.RemoveAt(e.OldStartingIndex);
}
break;
case NotifyCollectionChangedAction.Replace:
throw new NotImplementedException("Handle if necessary!");
case NotifyCollectionChangedAction.Move:
throw new NotImplementedException("Handle if necessary!");
case NotifyCollectionChangedAction.Reset:
this.Children.Clear();
break;
}
}
}
} CAUTION |
Stacktrace:
|
It seems referencing this bug in a commit message in one of my public libraries has closed this bug. That wasn’t my intention so I’ve reopened it. |
Could not get past this issue with using datatemplate and above workaround is not suitable for my purposes. Alternate workaround, which did seem to work OK on all platforms was to get rid of the datatemplate all together and just dynamically add the contents of of what was previously ItemsSource to the TabbedPage children in the constructor (retaining the same view model). Not an ideal solution I know, but works for now. |
I've enhanced the above workaround to support setting public class AdaptedTabbedPage : TabbedPage
{
public AdaptedTabbedPage()
{
this.PropertyChanged += AdaptedTabbedPage_PropertyChanged;
}
private void AdaptedTabbedPage_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(SelectedItem))
{
if (UseExperimentalSelectedItem)
if (Children != null)
foreach (var page in Children)
if (page.BindingContext == SelectedItem)
{
// Works fine.
CurrentPage = page;
break;
}
}
else if (e.PropertyName == nameof(CurrentPage))
{
// This causes a crash on WinUI. Android and iOS are fine.
//if (UseExperimentalSelectedItem)
// SelectedItem = CurrentPage?.BindingContext;
}
} |
Verified this on Visual Studio Enterprise 17.7.0 Preview 2.0. Repro on Windows 11 with below Project: |
This crash is still happening:
Exception Message: |
yet another blocker for our xamarin forms app conversion. seems like most of our UI on Windows is broken and doesn't display or just crashes due to issues like this one. |
Hi @MitchBomcanhao package here. If you don't want a dependency on the library you can just lift AdaptedTabbedPage.cs |
How come nobody is assigned to this bug? It still happens and I've wasted days on trying to find a problem in my code until I realzed it's (another) issue with MAUI. Please stop shipping cool new features until you've fixed the bugs in those already released! |
is this ever getting looked at? quite the horrible bug |
Description
When a TabbedPage is used as the root application page and the child pages are provided using ItemsSource, the app crashes when a tab other than the current tab is pressed.
Steps to Reproduce
Link to public reproduction project repository
https://github.com/Keflon/TabbedPageTemplateBug
Version with bug
7.0 (current)
Last version that worked well
Unknown/Other
Affected platforms
Windows
Affected platform versions
Latest
Did you find any workaround?
No. If anyone finds one please share here.
Relevant log output
The text was updated successfully, but these errors were encountered: