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.78k stars 706 forks source link

NavigationView not working when using fluent styles #5265

Closed manfromarce closed 3 years ago

manfromarce commented 3 years ago

Current behavior

The NavigationView's panel isn't visible on Android. The back button and toggle button are visible but they do nothing when I press them.

Expected behavior

NavigationView should work normally as in UWP.

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

Download the minimal repro solution and build it with Visual Studio: MinimalSol.zip I'm using Visual Studio's Android Emulator (Google Pixel 2 with Android 9,0). I haven't tried yet if the problem also occurs on iOS.

Environment

Nuget Package:

Nuget Package Version(s): 3.5.1

Affected platform(s):

IDE:

IsmailHassani commented 3 years ago

This issue seems to happen on WPF and WASM head also. I upgraded my project from version 3.4.0 to 3.5.1 when this behavior occurs. If you take the source of the dev branch and update all Uno references to version 3.5.1 you'll notice this change.

IsmailHassani commented 3 years ago

image

WASM head

IsmailHassani commented 3 years ago

image

WPF head

IsmailHassani commented 3 years ago

If i use the WinUI NavigationView Net5.0 branch this same behavior occurs, except that the header is visible.

image

jeromelaban commented 3 years ago

@MartinZikmund does it sound familiar ?

MartinZikmund commented 3 years ago

@MartinZikmund does it sound familiar ?

Unfortunately not, first time I see this problem 🤔

IsmailHassani commented 3 years ago

@MartinZikmund and @jeromelaban I can try to figure out from which version this started. Maybe we have a better starting point.

IsmailHassani commented 3 years ago

@MartinZikmund and @jeromelaban As soon i switch from version 3.5.0-dev.498 to 3.5.1 i get this error.

IsmailHassani commented 3 years ago

Another strange issue when applying the temporary workaround in #5267 is that the menu items in the PaneFooter don't look how they should. Maybe because the style is been ignored?

image

IsmailHassani commented 3 years ago

This is what it should look like (UWP head) But the panefooter will only be shown correctly when i change muxc:NavigationViewItem to NavigationViewItem while Hot Reloading. Not directly from startup.

image

IsmailHassani commented 3 years ago

Hi guys, I tried the workaround from #5325 but was not happy about it because i need this styling for UWP which works fine. So I came up with another workaround which allows conditional loading of this style.

On the App.xaml i removed the line

<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls"/>

Inside the App.xaml.cs i adjusted the OnLaunched method from:

        /// <summary>
        /// Invoked when the application is launched. Override this method to perform application initialization and to display initial content in the associated Window.
        /// </summary>
        /// <param name="args">Event data for the event.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs args)
        {
#if NETFX_CORE || (NET5_0 && WINDOWS)
            switch (AnalyticsInfo.VersionInfo.DeviceFamily)
            {
                case "Windows.Desktop":
                    break;
                case "Windows.IoT":
                    break;
                case "Windows.Xbox":
                    ApplicationView.GetForCurrentView().SetDesiredBoundsMode(ApplicationViewBoundsMode.UseCoreWindow);
                    break;
            }
#endif

            ThemeSelector.SetThemeColor(_serviceProvider.GetRequiredService<IApplicationSettingsService>().Color.ToEnum(ThemeColors.Default));

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (!(Windows.UI.Xaml.Window.Current.Content is Frame rootFrame))
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();

                rootFrame.NavigationFailed += OnNavigationFailed;
                rootFrame.Navigated += OnNavigated;

                if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    //TODO: Load state from previously suspended application
                }

                // Register a handler for BackRequested events and set the
                // visibility of the Back button
                SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;

                SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
                    rootFrame.CanGoBack ?
                    AppViewBackButtonVisibility.Visible :
                    AppViewBackButtonVisibility.Collapsed;

                // Place the frame in the current Window
                Windows.UI.Xaml.Window.Current.Content = rootFrame;
            }

            if (!args.PrelaunchActivated)
            {
                if (rootFrame.Content is null)
                {
                    // When the navigation stack isn't restored navigate to the first page,
                    // configuring the new page by passing required information as a navigation
                    // parameter

                    var view = _serviceProvider.GetRequiredService<IShellView>();
                    rootFrame.Navigate(view.GetType(), args.Arguments);
                }

                // Ensure the current window is active
                Windows.UI.Xaml.Window.Current.Activate();
            }
        }

and changed it with this:

        /// <summary>
        /// Invoked when the application is launched. Override this method to perform application initialization and to display initial content in the associated Window.
        /// </summary>
        /// <param name="args">Event data for the event.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs args)
        {
#if NETFX_CORE || (NET5_0 && WINDOWS)
            switch (AnalyticsInfo.VersionInfo.DeviceFamily)
            {
                case "Windows.Desktop":
                    break;
                case "Windows.IoT":
                    break;
                case "Windows.Xbox":
                    ApplicationView.GetForCurrentView().SetDesiredBoundsMode(ApplicationViewBoundsMode.UseCoreWindow);
                    break;
            }
#endif

            ThemeSelector.SetThemeColor(_serviceProvider.GetRequiredService<IApplicationSettingsService>().Color.ToEnum(ThemeColors.Default));

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (!(Windows.UI.Xaml.Window.Current.Content is Frame rootFrame))
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();

                rootFrame.NavigationFailed += OnNavigationFailed;
                rootFrame.Navigated += OnNavigated;

                if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    //TODO: Load state from previously suspended application
                }

                // Add custom resourcedictionaries from code.
                var dictionary = this.Resources?.MergedDictionaries;

                if(dictionary is not null)
                {
                    foreach (var item in GetAdditionalResourceDictionaries())
                    {
                        if(!dictionary.Any(t => t.Source == item.Source))
                            this.Resources.MergedDictionaries.Add(item);
                    }
                }

                // Register a handler for BackRequested events and set the
                // visibility of the Back button
                SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;

                SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
                    rootFrame.CanGoBack ?
                    AppViewBackButtonVisibility.Visible :
                    AppViewBackButtonVisibility.Collapsed;

                // Place the frame in the current Window
                Windows.UI.Xaml.Window.Current.Content = rootFrame;
            }

            if (!args.PrelaunchActivated)
            {
                if (rootFrame.Content is null)
                {
                    // When the navigation stack isn't restored navigate to the first page,
                    // configuring the new page by passing required information as a navigation
                    // parameter

                    var view = _serviceProvider.GetRequiredService<IShellView>();
                    rootFrame.Navigate(view.GetType(), args.Arguments);
                }

                // Ensure the current window is active
                Windows.UI.Xaml.Window.Current.Activate();
            }
        }

#if NETFX_CORE
        private IList<ResourceDictionary> GetAdditionalResourceDictionaries() =>
            new List<ResourceDictionary>()
            {
                new Microsoft.UI.Xaml.Controls.XamlControlsResources()
            };
#else
        private IList<ResourceDictionary> GetAdditionalResourceDictionaries() =>
            new List<ResourceDictionary>();
#endif

This extra piece of code will add the WinUI style dictionary if it's not already added. But only for UWP. An extra benefit you get, you're now able to add additional resource dictionaries from code. I preffered not to have to use this kind of hack, but it suits my purpose and solved my issues. All the heads will display your NavigationView correctly.

IsmailHassani commented 3 years ago

Another note: As you probably noted on the screenshots, the footer buttons are a little bit off. I don't know what has changed in this control, but on a sudden, the UI gets distorted. I managed to fix this issue by creating an extra list of navigationitems and bound them to the FooterMenuItemsSource which displays them correctly.

jeromelaban commented 3 years ago

This issue is caused by the SplitView style in Fluent themes being incorrect. To work around this issue, copy this style in your App.xaml resources:

https://github.com/unoplatform/uno/blob/0350e1330fcd5228cac3faddae5f427f41ddc1a4/src/Uno.UI/UI/Xaml/Style/Generic/Generic.xaml#L2021

using the xamarin namespace: https://github.com/unoplatform/uno/blob/0350e1330fcd5228cac3faddae5f427f41ddc1a4/src/Uno.UI/UI/Xaml/Style/Generic/Generic.xaml#L11

Will be fixed by https://github.com/unoplatform/uno/pull/5367