From 6bf96e5b78d9f0c1728bc7d0ce2eef1eeaa5d866 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 8 Jun 2021 10:49:28 -0400 Subject: [PATCH] fix(calendar): Improve reliability of calendar by safely handling invalid cases --- .../CalendarView_Partial_Interaction.cs | 9 ++++ ...CalendarPanel.ModernCollectionBasePanel.cs | 41 +++++++++++++++---- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarView_Partial_Interaction.cs b/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarView_Partial_Interaction.cs index a5d1afa353f9..60d2f5193344 100644 --- a/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarView_Partial_Interaction.cs +++ b/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarView_Partial_Interaction.cs @@ -351,6 +351,15 @@ internal void OnItemFocused(CalendarViewBaseItem pItem) SetDisplayDateInternal(date); // scroll item into view so we can move focus to it. spItemAsI = pHost.Panel.ContainerFromIndex(index); + + // Uno workaround + if (spItemAsI is null) + { + // The scrolling might occurs async, especially on wasm. + // For safety we prefer to ignore this instead of crashing which might cause exceptions worst than loosing the focus. + return; + } + global::System.Diagnostics.Debug.Assert(spItemAsI is {}); spItem = (CalendarViewBaseItem) spItemAsI; diff --git a/src/Uno.UI/UI/Xaml/Controls/CalendarView/Primitives/CalendarPanel.ModernCollectionBasePanel.cs b/src/Uno.UI/UI/Xaml/Controls/CalendarView/Primitives/CalendarPanel.ModernCollectionBasePanel.cs index c66485af4968..90c2a9c7dc2a 100644 --- a/src/Uno.UI/UI/Xaml/Controls/CalendarView/Primitives/CalendarPanel.ModernCollectionBasePanel.cs +++ b/src/Uno.UI/UI/Xaml/Controls/CalendarView/Primitives/CalendarPanel.ModernCollectionBasePanel.cs @@ -14,6 +14,7 @@ using Uno.Extensions.Specialized; using Uno.Logging; using Uno.UI; +using Uno.UI.Extensions; namespace Windows.UI.Xaml.Controls.Primitives { @@ -48,7 +49,7 @@ private class ContainersCache : IItemContainerMapping, IEnumerable set { _host = value; - _entries.Clear(); + Clear(); } } @@ -71,6 +72,17 @@ private enum GenerationState After } + internal void Clear() + { + _entries.Clear(); + FirstIndex = -1; + LastIndex = int.MinValue; + + _generationStartIndex = -1; + _generationCurrentIndex = -1; + _generationEndIndex = -1; + } + internal void BeginGeneration(int startIndex, int endIndex) { if (_host is null) @@ -86,7 +98,7 @@ internal void BeginGeneration(int startIndex, int endIndex) _generationEndIndex = endIndex; _generationState = GenerationState.Before; - // Note: Start and End indexes are INCLUSIVE + // Note: Fist and Last indexes are INCLUSIVE startIndex = Math.Max(FirstIndex, startIndex); endIndex = Math.Min(LastIndex, endIndex); @@ -607,17 +619,28 @@ private Size base_MeasureOverride(Size availableSize) } finally { - FirstVisibleIndexBase = Math.Max(firstVisibleIndex, startIndex); - LastVisibleIndexBase = Math.Max(FirstVisibleIndexBase, lastVisibleIndex); + try + { + FirstVisibleIndexBase = Math.Max(firstVisibleIndex, startIndex); + LastVisibleIndexBase = Math.Max(FirstVisibleIndexBase, lastVisibleIndex); + + foreach (var unusedEntry in _cache.CompleteGeneration(index - 1)) + { + Children.Remove(unusedEntry.Container); + } - foreach (var unusedEntry in _cache.CompleteGeneration(index - 1)) + global::System.Diagnostics.Debug.Assert(_cache.FirstIndex <= FirstVisibleIndex || FirstVisibleIndex == -1); + global::System.Diagnostics.Debug.Assert(_cache.LastIndex >= LastVisibleIndex || LastVisibleIndex == -1); + global::System.Diagnostics.Debug.Assert(Children.Count == _cache.LastIndex - _cache.FirstIndex + 1 || (_cache.LastIndex == -1 && _cache.LastIndex == -1)); + } + catch { - Children.Remove(unusedEntry.Container); + _cache.Clear(); + Children.Clear(); + + InvalidateMeasure(); } - global::System.Diagnostics.Debug.Assert(_cache.FirstIndex <= FirstVisibleIndex || FirstVisibleIndex == -1); - global::System.Diagnostics.Debug.Assert(_cache.LastIndex >= LastVisibleIndex || LastVisibleIndex == -1); - global::System.Diagnostics.Debug.Assert(Children.Count == _cache.LastIndex - _cache.FirstIndex + 1 || (_cache.LastIndex == -1 && _cache.LastIndex == -1)); // We force the parent ScrollViewer to use the same viewport as us, no matter its own stretching. ViewportHeight = viewport.Height;