Home>
I do not know how to receive parameters at the screen transition destination

I would like to implement screen transitions in the MVVM pattern.
Reference pageAlthough I was able to do the screen transition itself by referring to, I am worried because I do not know how to acquire the parameter at the transition destination.

Created code

Main window

<NavigationWindow x:Class="NavigationTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:NavigationTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" ​​Width="800"></NavigationWindow>
  public partial class MainWindow: NavigationWindow
    {
        public MainWindow()
        {
            InitializeComponent();
            var NextPage = new Page1View();
            NavigationService.Navigate(NextPage);
        }
    }

Page 1

<Page x:Class="NavigationTest.Page1View"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      mc:Ignorable="d"
      d:DesignHeight="450" ​​d:DesignWidth="800"
      xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
      xmlns:b="clr-namespace:Common"
      xmlns:vm="clr-namespace:NavigationTest"
      Title="Page1"><Page.DataContext>    <vm:Page1ViewModel x:Name="Page1ViewModel"/></Page.DataContext><Grid>    <Button Content="Page2">>        <i:Interaction.Behaviors>            <b:NavigateButtonBehaivior NavigatePage="Page2View.xaml" NavigateExtraData="TEST" />        </i:Interaction.Behaviors>    </Button></Grid></Page>

Page 2

<Page x:Class="NavigationTest.Page2View"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      mc:Ignorable="d"
      d:DesignHeight="450" ​​d:DesignWidth="800"
      xmlns:vm="clr-namespace:NavigationTest"
      Title="Page2"><Page.DataContext>    <vm:Page2ViewModel x:Name="Page2ViewModel"/></Page.DataContext><Grid>    <Label Content="{Binding LabelContent}"/></Grid></Page>

Behavior class

 public class NavigateButtonBehaivior: Behavior<ButtonBase>  {
        public static readonly DependencyProperty NavigatePageProperty =
        DependencyProperty.Register("NavigatePage", typeof(Uri), typeof(NavigateButtonBehaivior), new UIPropertyMetadata(null));
        public static readonly DependencyProperty NavigateExtraDataProperty =
            DependencyProperty.Register("NavigateExtraData", typeof(object), typeof(NavigateButtonBehaivior), new UIPropertyMetadata(null));
        ///<summary>      /// Transition destination page
        ///</summary>      public Uri NavigatePage
        {
            get {return (Uri)GetValue(NavigatePageProperty);}
            set {SetValue(NavigatePageProperty, value);}
        }
        ///<summary>      /// Parameter to be passed to the transition destination
        ///</summary>      public object NavigateExtraData
        {
            get {return GetValue(NavigateExtraDataProperty);}
            set {SetValue(NavigateExtraDataProperty, value);}
        }
        ///<summary>      /// when attached
        ///</summary>      protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.Click += this.AssociatedObjectClick;
        }
        ///<summary>      /// when detached
        ///</summary>      protected override void OnDetaching()
        {
            this.AssociatedObject.Click -= this.AssociatedObjectClick;
            base.OnDetaching();
        }
        ///<summary>      /// when clicked
        ///</summary>      private void AssociatedObjectClick(object sender, RoutedEventArgs e)
        {
            if (this.NavigatePage == null)
            {
                return;
            }
            var button = (ButtonBase)sender;
            var navigationService = GetNavigationService(button);
            if (navigationService == null)
            {
                return;
            }
            // Get the package URL of the current page and convert the relative path to an absolute path.
            // * new Uri(((IUriContext)navigationWindow).BaseUri, this.NavigatePage)
            // Since it is a relative path from the navigation window XAML, it is not possible to transition to and from the subdirectory.
            var baseUri = BaseUriHelper.GetBaseUri(button);
            var uri = new Uri(baseUri, this.NavigatePage);
            // Navigate
            navigationService.Navigate(uri, this.NavigateExtraData);
        }
        ///<summary>/// Get navigation service
        ///</summary>      ///<param name="element"></param>      ///<returns></returns>      protected virtual NavigationService GetNavigationService(DependencyObject element)
        {
            var window = Window.GetWindow(element);
            if (window is NavigationWindow navigationWindow)
            {
                // For NavigationWindow
                return navigationWindow.NavigationService;
            }
            var parent = element;
            while ((parent = VisualTreeHelper.GetParent(parent)) != null)
            {
                if (parent is Frame frame)
                {
                    // If i am using a Frame in a plain (non-Navigation) Window
                    return frame.NavigationService;
                }
            }
            return null;
        }
    }
Tried 1

Get the parameter set in NavigationService.LoadCompleted event in the code-behind of page 2.
(I want to implement it in MVVM, so I want to finally implement NavigationService.LoadCompleted in ViewModel, but the method is
I did not understand it, so I wrote it as a test in the code-behind)
The result is an exception.

Code tried
  public partial class Page2View :Page
    {
        public Page2View()
        {
            InitializeComponent();
            // error here
            NavigationService.LoadCompleted += NavigationService_LoadCompleted;
        }
        private void NavigationService_LoadCompleted(object sender, NavigationEventArgs e)
        {
            string str = (string)e.ExtraData;
        }
    }

Error message

An unhandled exception of type'System.Reflection.TargetInvocationException' occurred in PresentationFramework.dll
Additional Information: The target of the call threw an exception.
Tried 2

Get the value set in NavigateExtraDataProperty of NavigateButtonBehaivior class
The result is Null.

  public partial class Page2View :Page
    {
        public Page2View()
        {
            InitializeComponent();
            // result gets null
            var para = new NavigateButtonBehaivior().NavigateExtraData;
        }
    }
Supplement

Since the above implementation method does not hit the point in the first place or it is a coding mistake,
If there is any other good method, I would like to ask for a professor.

  • Answer # 1

    Navigation Overview-WPF | Microsoft Docs
    But be careful, with WPFNavigationServiceSince it is possible to have multiplePage2ViewIn the constructor ofNavigationServiceI think I can't get (I can't decide yet)

    Application.NavigatedOrNavigationWindowofNavigationServiceWould you like to get?

    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Navigation;
    namespace NavigationTest
    {
        public partial class Page2View :Page
        {
            public Page2View()
            {
                InitializeComponent();
                Application.Current.Navigated += Application_Navigated;
                var w = Application.Current.MainWindow as NavigationWindow;
                w.NavigationService.LoadCompleted += NavigationService_LoadCompleted;
            }
            private void Application_Navigated(object sender, NavigationEventArgs e)
            {
                Debug.WriteLine("Application_Navigated");
                Debug.WriteLine(e.ExtraData);
                Application.Current.Navigated -= Application_Navigated;
            }
            private void NavigationService_LoadCompleted(object sender, NavigationEventArgs e)
            {
                Debug.WriteLine("NavigationService_LoadCompleted");
                Debug.WriteLine(e.ExtraData);
                var w = Application.Current.MainWindow as NavigationWindow;
                w.NavigationService.LoadCompleted -= NavigationService_LoadCompleted;
            }
        }
    }

    It was the first time I found out after researching, but there seems to be something like this (I do not understand anything ^ ^;
    Structured Navigation Overview-WPF | Microsoft Docs
    PageFunctionClass (System.Windows.Navigation) | Microsoft Docs

    If you have a strong commitment to MVVM, you may be happier to move to Prism Library etc. (NavigationWindowOrFrameIs an impression that is not used much in WPF)