質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.10%

[WPF][MVVM]画面遷移時のパラメータ取得方法について

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 155

WTB_21

score 2

画面遷移先でパラメータの受け方がわからない

MVVMパターンでの画面遷移を実装したいと考えています。
参考ページを参考にして画面遷移自体は出来たのですが、遷移先でパラメータの取得方法がわからずに悩んでいます。

作成したコード

メインウインドウ

<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);

        }
    }

ページ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>

ページ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>

ビヘイビアクラス

   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>
        /// 遷移先のページ
        /// </summary>
        public Uri NavigatePage
        {
            get { return (Uri)GetValue(NavigatePageProperty); }

            set { SetValue(NavigatePageProperty, value); }
        }

        /// <summary>
        /// 遷移先に渡すパラメータ
        /// </summary>
        public object NavigateExtraData
        {
            get { return GetValue(NavigateExtraDataProperty); }

            set { SetValue(NavigateExtraDataProperty, value); }
        }

        /// <summary>
        /// アタッチされたとき
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();

            this.AssociatedObject.Click += this.AssociatedObjectClick;
        }

        /// <summary>
        /// デタッチされたとき
        /// </summary>
        protected override void OnDetaching()
        {
            this.AssociatedObject.Click -= this.AssociatedObjectClick;

            base.OnDetaching();
        }

        /// <summary>
        /// クリックされたとき
        /// </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;
            }

            // 現ページのパッケージURLを取得して相対パスを絶対パスに変換する。
            // ※new Uri(((IUriContext)navigationWindow).BaseUri, this.NavigatePage) だと
            //  ナビゲーションウィンドウXAMLからの相対パスになるので、サブディレクトリとの間で遷移できない。
            var baseUri = BaseUriHelper.GetBaseUri(button);
            var uri = new Uri(baseUri, this.NavigatePage);

            // ナビゲート
            navigationService.Navigate(uri, this.NavigateExtraData);
        }

        /// <summary>
        /// ナビゲーションサービス取得
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        protected virtual NavigationService GetNavigationService(DependencyObject element)
        {
            var window = Window.GetWindow(element);
            if (window is NavigationWindow navigationWindow)
            {
                // NavigationWindow の場合
                return navigationWindow.NavigationService;
            }

            var parent = element;
            while ((parent = VisualTreeHelper.GetParent(parent)) != null)
            {
                if (parent is Frame frame)
                {
                    // プレーンな(非 Navigation)Window で Frame を使用している場合
                    return frame.NavigationService;
                }
            }

            return null;
        }
    }

試したこと1

ページ2のコードビハインドにNavigationService.LoadCompletedイベントで設定したパラメータを取得。
(MVVMで実装したいため、最終的にはViewModelでNavigationService.LoadCompletedの実装を行いたいが方法が、
わからなかったためテストとしてコードビハインドに記載)
結果は例外が発生。

試したコード

    public partial class Page2View : Page
    {
        public Page2View()
        {
            InitializeComponent();

            // ここでエラーが発生
            NavigationService.LoadCompleted += NavigationService_LoadCompleted;

        }

        private void NavigationService_LoadCompleted(object sender, NavigationEventArgs e)
        {
            string str = (string)e.ExtraData;
        }
    }

エラーメッセージ

'System.Reflection.TargetInvocationException' のハンドルされていない例外が PresentationFramework.dll で発生しました
追加情報: 呼び出しのターゲットが例外をスローしました。

試したこと2

NavigateButtonBehaiviorクラスのNavigateExtraDataPropertyに設定した値を取得
結果はNullが取得された。

    public partial class Page2View : Page
    {
        public Page2View()
        {
            InitializeComponent();

            // 結果はnullを取得
            var para = new NavigateButtonBehaivior().NavigateExtraData;

        }
    }

補足

上記の実装方法がそもそも的を射ていないのか、コーディングミスなのかの判断もついていないため、
他に良い方法があるなど御座いましたらご教授いただきたいです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

ナビゲーションの概要 - WPF | Microsoft Docs
でも注意がありますが、WPFだとNavigationServiceを複数持つことが可能なため、Page2ViewのコンストラクタではNavigationServiceを取得できないのだと思います(まだ決定できない)

Application.Navigatedか、NavigationWindowNavigationServiceを取得するかでしょうか?

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;
        }
    }
}

調べていて初めて知ったのですが、こういったものもあるようです(なにもわかっていません^^;
構造化ナビゲーションの概要 - WPF | Microsoft Docs
PageFunction<T> クラス (System.Windows.Navigation) | Microsoft Docs

MVVMに強いこだわりがあるなら、Prism Libraryなどに移行したほうが幸せかもしれません(NavigationWindowFrame はWPFではあまり使われていない印象です)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/06/30 22:16 編集

    ご教授頂いた方法で解決することが出来ました。ありがとうございます。

    別環境でご教授頂いた方法を試す際に気づいたのですが、NavigateButtonBehaivior クラスので使用している「Behavior<T>」は名前空間「System.Windows.Interactivity」の廃止により「Microsoft.Xaml.Behaviors」に変更されているのですね。

    using部分の記載を省いたせいでご回答くださる際にお手を煩わせていたら申し訳ございません。

    Nugetより「Microsoft.Xaml.Behaviors.Wpf」をインストールすることで解決することが出来ました。

    構造化ナビゲーションとPageFunction<T> クラスについて初見でしたので勉強したいと思います。
    Prism Libraryへの移行も検討したいと思います。ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる