LennyL
LennyL

Reputation: 333

C# WinUI 3 Desktop How do I expose an interface to the App instance?

Attempting to implement NavigationView navigation via code-behind using this solution I found: https://xamlbrewer.wordpress.com/2021/07/06/navigating-in-a-winui-3-desktop-application/

I get this compile error:

enter image description here

The error is saying I should 'cast' m_window; The example did not cast. Why do I need to cast?

I created an interface: (INavigation.cs)

using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MetricReporting.Interfaces
{
    interface INavigation
    {
        NavigationViewItem GetCurrentNavigationViewItem();

        List<NavigationViewItem> GetNavigationViewItems();

        List<NavigationViewItem> GetNavigationViewItems(Type type);

        List<NavigationViewItem> GetNavigationViewItems(Type type, string title);

        void SetCurrentNavigationViewItem(NavigationViewItem item);
    }
}

I created a partial class of my main window (MainWindow.xaml.Navigation.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MetricReporting
{
    public partial class MainWindow : INavigation
    {
        public NavigationViewItem GetCurrentNavigationViewItem()
        {
            return NavigationView.SelectedItem as NavigationViewItem;
        }

        public List<NavigationViewItem> GetNavigationViewItems()
        {
            var result = new List<NavigationViewItem>();
            var items = NavigationView.MenuItems.Select(i => (NavigationViewItem)i).ToList();
            items.AddRange(NavigationView.FooterMenuItems.Select(i => (NavigationViewItem)i));
            result.AddRange(items);
 
            foreach (NavigationViewItem mainItem in items)
            {
                result.AddRange(mainItem.MenuItems.Select(i => (NavigationViewItem)i));
            }
 
            return result;
        }
 
        public List<NavigationViewItem> GetNavigationViewItems(Type type)
        {
            return GetNavigationViewItems().Where(i => i.Tag.ToString() == type.FullName).ToList();
        }
 
        public List<NavigationViewItem> GetNavigationViewItems(Type type, string title)
        {
            return GetNavigationViewItems(type).Where(ni => ni.Content.ToString() == title).ToList();
        }

        public void SetCurrentNavigationViewItem(NavigationViewItem item)
        {
            if (item == null)
            {
                return;
            }

            if (item.Tag == null)
            {
                return;
            }

            MasterContentFrame.Navigate(
            Type.GetType(item.Tag.ToString()),
            item.Content);
            NavigationView.Header = item.Content;
            NavigationView.SelectedItem = item;
        }
    }
}

I am adding this line to my App.xaml.cs file:

public INavigation Navigation => m_window;

It models the line from the article:

private Shell shell;
 
public INavigation Navigation => shell;
         
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    shell = new Shell();
    shell.Activate();
}

I should be able to use the navigation service like this:

All parts of the code base can now easily access the lightweight Navigation Service:

(Application.Current as App).Navigation

For clarity, here is my App.xaml.cs

using MetricReporting.Interfaces;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Navigation;
    
namespace MetricReporting
{
    public partial class App : Application
    {
        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used such as when the application is launched to open a specific file.
        /// </summary>
        /// <param name="args">Details about the launch request and process.</param>
        /// 

        protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
        {
            m_window = new MainWindow();
            m_window.Activate();
        }

        private Window m_window;

        public INavigation Navigation => m_window;   //Compiler does not like this
        //public INavigation Navigation => m_window;   Compiler does not like this
    }
}

Upvotes: 0

Views: 768

Answers (3)

milos_lazovic
milos_lazovic

Reputation: 1

I ran the same code, and the error is that the wrong interface is implicitly inherited.

In the following line, interface INavigation is from namespace: namespace Microsoft.EntityFrameworkCore.Metadata.

public INavigation Navigation => m_window;

Just specify directly which interface are you using, for example if you want to use your implementation:

public MetricReporting.Services.Interfaces.INavigation Navigation => m_window;

or change the name of your interface, e.g. IMainWindowsNavigation.

Upvotes: 0

mm8
mm8

Reputation: 169340

You need to cast m_window to an INavigation for your code to compile:

public INavigation Navigation => m_window as INavigation;

Or define the m_window field as an INavigation directly as suggested by @rfmodulator (+1):

private MainWindow m_window;

Upvotes: 0

rfmodulator
rfmodulator

Reputation: 3738

m_window is a Window...so it could be any Window and not necessarily your MainWindow, or anything that implements INavigation. That's all of the information that build has.

You could fix it with:

private MainWindow m_window;

Upvotes: 2

Related Questions