unoplatform / uno

Open-source platform for building cross-platform native Mobile, Web, Desktop and Embedded apps quickly. Create rich, C#/XAML, single-codebase apps from any IDE. Hot Reload included! 90m+ NuGet Downloads!!
https://platform.uno
Apache License 2.0
8.92k stars 722 forks source link

`CalendarView` issue when shown for the second time #16123

Open FrozDark opened 7 months ago

FrozDark commented 7 months ago

Current behavior

When the calendar is shown the second time it gets bugged with no interaction possible

Tested on Skia.WPF and Android have the same issue

qtagsag

Expected behavior

No bug

How to reproduce it (as minimally and precisely as possible)

CalendarViewIssue.zip

Workaround

No response

Works on UWP/WinUI

Yes

Environment

Uno.WinUI / Uno.WinUI.WebAssembly / Uno.WinUI.Skia

NuGet package version(s)

Tested on 5.2.0-dev.484 and 5.3.0-dev.12

Affected platforms

Android, iOS, Skia (WPF), Skia (GTK on Linux/macOS/Windows), Skia (Linux Framebuffer)

IDE

No response

IDE version

No response

Relevant plugins

No response

Anything else we need to know?

No response

Youssef1313 commented 7 months ago

It's crashing during measure:

      System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
         at System.Collections.Generic.List`1.get_Item(Int32 index)
         at Microsoft.UI.Xaml.Controls.Primitives.CalendarPanel.ContainersCache.GetOrCreate(Int32 index) in /__w/1/s/src/Uno.UI/UI/Xaml/Controls/CalendarView/Primitives/CalendarPanel.ModernCollectionBasePanel.cs:line 252
         at Microsoft.UI.Xaml.Controls.Primitives.CalendarPanel.base_MeasureOverride(Size availableSize) in /__w/1/s/src/Uno.UI/UI/Xaml/Controls/CalendarView/Primitives/CalendarPanel.ModernCollectionBasePanel.cs:line 590
Youssef1313 commented 7 months ago

NOTE: There is this piece of code:

#if HAS_UNO
        static readonly Dictionary<Type, WeakReference<ContentDialog>> DialogInstances = new();
        public static T GetDialog<T>(XamlRoot xamlRoot) where T : ContentDialog
        {
            T dialog;
            if (!DialogInstances.TryGetValue(typeof(T), out var dialogRef))
            {
                dialog = (T)Activator.CreateInstance(typeof(T), true);

                DialogInstances[typeof(T)] = new(dialog);
            }
            else if (!dialogRef.TryGetTarget(out var target))
            {
                dialog = (T)Activator.CreateInstance(typeof(T), true);
                dialogRef.SetTarget(dialog);
            }
            else
            {
                dialog = (T)target;
            }

            dialog.XamlRoot = xamlRoot;

            return dialog;
        }
#else
        public static T GetDialog<T>(XamlRoot xamlRoot) where T : ContentDialog
        {
            var dialog = (T)Activator.CreateInstance(typeof(T), true);
            dialog.XamlRoot = xamlRoot;

            return dialog;
        }
#endif

Using the "else" path in Uno makes it not crash. Investigating further as using the HAS_UNO path in WinUI doesn't crash in WinUI.

FrozDark commented 7 months ago

NOTE: There is this piece of code:

#if HAS_UNO
        static readonly Dictionary<Type, WeakReference<ContentDialog>> DialogInstances = new();
        public static T GetDialog<T>(XamlRoot xamlRoot) where T : ContentDialog
        {
            T dialog;
            if (!DialogInstances.TryGetValue(typeof(T), out var dialogRef))
            {
                dialog = (T)Activator.CreateInstance(typeof(T), true);

                DialogInstances[typeof(T)] = new(dialog);
            }
            else if (!dialogRef.TryGetTarget(out var target))
            {
                dialog = (T)Activator.CreateInstance(typeof(T), true);
                dialogRef.SetTarget(dialog);
            }
            else
            {
                dialog = (T)target;
            }

            dialog.XamlRoot = xamlRoot;

            return dialog;
        }
#else
        public static T GetDialog<T>(XamlRoot xamlRoot) where T : ContentDialog
        {
            var dialog = (T)Activator.CreateInstance(typeof(T), true);
            dialog.XamlRoot = xamlRoot;

            return dialog;
        }
#endif

Using the "else" path in Uno makes it not crash. Investigating further as using the HAS_UNO path in WinUI doesn't crash in WinUI.

As a workaround

It's been expected to have a new instance of the dialog to be working correctly as it will always show the control for the first time it's initialized

Also worth noting that It's working on stable 5.1.104

Youssef1313 commented 7 months ago

The main root cause is that MaxDate changes between the two shows of the CalendarView. This is breaking the ContainersCache (it doesn't get cleared properly). Working on getting a PR soon-ish.