user19227467
user19227467

Reputation: 3

WPF How do I allow my ControlTemplate to use an existing NavigationService?

I have a ControlTemplate for a Menu which I want to be used for all my Pages. The function calls of the ControlTemplate requires the usage of the NavigationService belonging my NavigationWindow. However, I can not place the ControlTemplate in the NavigationWindow xaml file, because it is not accessible to the Pages. How would I allow my ControlTemplate to access the NavigationService while being available to use in my Pages?

My NavigationWindow is located at MainWindow.xaml

App.xaml.cs

namespace WPFApplication
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private void Menu_ModeAdvanced_Click(object sender, RoutedEventArgs e)
        {
            // Where I want to access the Navigation Service
        }
    }
}

App.xaml

<Application x:Class="WPFApplication.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WPFApplication"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <!-- Page Design -->
            <Style TargetType="Page">
                <Setter Property="Foreground" Value="#FBF5F3" />
                <Setter Property="Background" Value="#363537" />
            </Style>
            
            <!-- Label Design-->
            <Style TargetType="Label">
                <Setter Property="Foreground" Value="#FBF5F3" />
            </Style>
        </ResourceDictionary>

        <!-- Menu Template-->
        <ControlTemplate x:Key="StandardMenu" TargetType="Menu">
            <Menu Height="20">
                <MenuItem Header="File" AllowDrop="True">
                    <MenuItem Header="Mode" AllowDrop="True">
                        <MenuItem Header="Advanced"/>
                    </MenuItem>
                </MenuItem>
            </Menu>
        </ControlTemplate>
    </Application.Resources>
</Application>

I have tried putting the ControlTemplate and related code into a ResourceDictionary located in the NavigationWindow's class. However, I could not use it as a static resource in my pages.

Upvotes: -1

Views: 54

Answers (1)

BionicCode
BionicCode

Reputation: 28948

Simply get the parent Window of the MenuItem. If it is the NavigationWindow you can use its API to execute navigation related tasks or use its associated NavigationWindow.NavigationService:

private void Menu_ModeAdvanced_Click(object sender, RoutedEventArgs e)
{
  FrameworkElement element = sender as FramwworkElement;
  NavigationWindow navigationWindow = null;
  while (element.Parent is not null)
  {
    element = element.Parent as FrameworkElement;
    if (element is NavigationWindow window)
    {
      navigationWindow = window;
      break;
    }
  }

  // Navigate etc. using the NavigationWindow API
  navigationWindow?.Navigate(new Uri());
 
  // Navigate etc. using the NavigationService
  navigationWindow?.NavigationService.Navigate(new Uri());
}

But I recommend defining routed commands in your NavigationWindow.
It's much cleaner from a design perspective as the navigation details are kept private to the navigation host:

MainWindow.xaml.cs

partial class MainWindow : NavigationWindow
{
  public static RoutedCommand NavigateToPageCommand { get; }
    = new RoutedCommand(nameof(MainWindow.NavigateToPageCommand, typeof(MainWindow)); 

  private Dictionary<PageId, Uri> PageUriStore { get; }

  public MainWindow()
  {
    InitializeComponent();

    var navigateCommandBinding = new CommandBinding(
      MainWindow.NavigateToPageCommand,
      ExecutedNavigateToPageCommand,
      CanexecutedNavigateToPageCommand);
    this.CommandBindings.Add(navigateCommandBinding);

    this.PageUriStore = new Dictionary<PageId, Uri>
    {
      { PageId.SettingsPage, new Uri(...) },
    };
  }

  private void CanexecutedNavigateToPageCommand(object sender, ExecutedRoutedEventArgs e)
    => e.CanExecute = e.Parameter is PageId;

  private void CanexecutedNavigateToPageCommand(object sender, ExecutedRoutedEventArgs e)
  {
    var pageId = (PageId)e.Parameter;
    if (this.PageUriStore.TryGetValue(pageId, out Uri destinationUri))
    {
      this.Navigate(ddestinationUri);
    }
  }
}

App.xaml

<MenuItem Header="Settings"
          Command="{x:Static MainWindow.NavigateToPageCommand}"
          CommandParameter="{x:Static PageId.SettingsPage}" />

PageId.cs

enum PageId
{
  Default = 0,
  OverviewPage,
  SettingsPage
}

Upvotes: 0

Related Questions