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

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

新規登録して質問してみよう
ただいま回答率
85.40%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

1回答

334閲覧

WPF Prism 子xamlに親xamlのDataContextが渡せない(nullとなる)

shrgnoko24

総合スコア1

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

2グッド

0クリップ

投稿2024/07/21 07:36

編集2024/07/21 13:45

実現したいこと

Main.xaml内で読み込んでいるChildArea.xamlのViewModel内のLoadedコマンド
パラメータにDataContext(MainViewModel)を設定したいです。

Main.xaml

1<UserControl x:Name="MainView" x:Class="Sample.Views.Main" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:i="http://schemas.microsoft.com/xaml/behaviors" 5 xmlns:TestArea="clr-namespace:Sample.Views.Main.TestArea" 6 xmlns:prism="http://prismlibrary.com/" 7 prism:ViewModelLocator.AutoWireViewModel="True"> 8 9  … 10 <Grid> 11  <GroupBox Margin="5"> 12   <StackPanel x:Name="Child" Margin="10,10,0,0" > 13    <TestArea:ChildArea /> 14   </StackPanel> 15  </GroupBox>  16 </Grid> 17 18 … 19</UserControl>

ChildArea.xaml

1<UserControl x:Name="ChildAreaView" 2 x:Class="Sample.Views.Main.TestArea.ChildArea" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:i="http://schemas.microsoft.com/xaml/behaviors" 6 xmlns:prism="http://prismlibrary.com/" 7 prism:ViewModelLocator.AutoWireViewModel="True"> 8 9  <i:Interaction.Triggers> 10 <i:EventTrigger EventName="Loaded"> 11 <i:InvokeCommandAction Command="{Binding LoadedCommand}" 12 CommandParameter="{Binding DataContext, ElementName=MainView}"/> 13 </i:EventTrigger> 14 </i:Interaction.Triggers> 15 16 … 17</UserControl>

ChildViewModel.cs

1namespace Sample.ViewModels.Main.TestArea 2{ 3 /// <summary> 4 /// 子xamlのViewModel 5 /// </summary> 6 public class ChildAreaViewModel : BindableBase 7 { 8    /// コンストラクタ 9 public ChildAreaViewModel () 10 { 11 base.LoadedCommand.Subscribe(x => Loaded(x)); 12 } 13 14 private MainViewModel _mainViewModel; 15 16 public void Loaded(object mainViewModel) 17 { 18 _mainViewModel= mainViewModel as MainViewModel ; 19 20    ‥‥ 以下省略 21 } 22}

発生している問題・分からないこと

元々画面遷移で開いていたMain.xamlをダイアログで表示させるために
MainViewModelにIDialogAwareを継承し、ダイアログでも開けるようにしました。
ですが、Main.xaml内で読み込んでいるChildArea.xamlのViewModelでエラーが発生するようになりました。

エラーの内容としては、画面遷移でMain.xamlを開いていた時には問題なく設定されていた
ChildAreaViewModel内のLoadedコマンド引数「mainViewModel」が
ダイアログで開いた場合、nullになってしまう・・・というものです。

エラーメッセージ

error

1mainViewModelを使用する処理を組み込んでいるため、 2MainViewModelを使用しようとすると以下エラーが発生します。(当たり前ですが) 3 4System.NullReferenceException: 'Object reference not set to an instance of an object.'

該当のソースコード

特になし

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

コードビハインドでDataContextを設定するようにしたり
Main.xaml内で、DataContextを明示して設定するようにしてみましたが
そうすると、ChildViewModelのLoadedコマンドが呼ばれなくなりました…。

補足

WPF Prismにまだ完全に慣れていないため、どこに原因があるかハッキリ掴めないでいます…。
何か見落としやアドバイスがありましたらお願いしたいです。

Prism.Unity:8.1.97
Prism.Wpf:8.1.97
ReactiveProperty:9.0.0

TN8001😄を押しています
TN8001👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Main.xaml内で読み込んでいるChildArea.xamlのViewModel内のLoadedコマンド
パラメータにDataContext(MainViewModel)を設定したいです。

実際にやりたいことはパラメータに渡したいのではなく、MainViewModelのインスタンスが欲しいだけですよね?

単にこれでいいのではないでしょうか。

cs

1public class ChildAreaViewModel : BindableBase 2{ 3 private MainViewModel _mainViewModel; 4 5 public ChildAreaViewModel(MainViewModel mainViewModel) 6 { 7 _mainViewModel = mainViewModel; 8 }

コードビハインドでDataContextを設定するようにしたり
Main.xaml内で、DataContextを明示して設定するようにしてみましたが
そうすると、ChildViewModelのLoadedコマンドが呼ばれなくなりました…。

DataContextはひとつしか持てないので、AutoWireViewModelしているViewに違うDataContextを設定したら当然動かなくなりますよね。


MainViewModelRegisterSingletonすれば上記で十分です。

シングルトンにしたくない場合は、TimerTriggerに変更したら取れました(ぶっちゃけクソっぽいが^^;

xml

1 <i:TimerTrigger EventName="Loaded" MillisecondsPerTick="100" TotalTicks="1"> 2 <i:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding DataContext, ElementName=MainView}" /> 3 </i:TimerTrigger>

100の根拠はありません(手元では1でも0でも!! 取れました)
TimerTrigger クラス (Microsoft.Expression.Interactivity.Core) | Microsoft Learn

xml:MainWindow.xaml

1<Window 2 x:Class="Q07l0vpm2ryc05t.Views.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:prism="http://prismlibrary.com/" 6 Width="525" 7 Height="350" 8 prism:ViewModelLocator.AutoWireViewModel="True"> 9 <DockPanel> 10 <StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> 11 <Button Command="{Binding NavigateCommand}" CommandParameter="ViewA">Navigate ViewA</Button> 12 <Button Command="{Binding NavigateCommand}" CommandParameter="Main">Navigate Main</Button> 13 <Button Command="{Binding ShowDialogCommand}">Show MainDialog</Button> 14 </StackPanel> 15 <ContentControl prism:RegionManager.RegionName="ContentRegion" /> 16 </DockPanel> 17</Window>

cs:MainWindowViewModel.cs

1using Prism.Mvvm; 2using Prism.Regions; 3using Prism.Services.Dialogs; 4using Q07l0vpm2ryc05t.Views; 5using Reactive.Bindings; 6 7namespace Q07l0vpm2ryc05t.ViewModels; 8 9public class MainWindowViewModel : BindableBase 10{ 11 public ReactiveCommand<string> NavigateCommand { get; } 12 public ReactiveCommand ShowDialogCommand { get; } 13 14 public MainWindowViewModel(IRegionManager regionManager, IDialogService dialogService) 15 { 16 NavigateCommand = new ReactiveCommand<string>() 17 .WithSubscribe(x => regionManager.RequestNavigate("ContentRegion", x)); 18 19 ShowDialogCommand = new ReactiveCommand() 20 .WithSubscribe(() => dialogService.ShowDialog(nameof(Main))); 21 } 22}

xml:Main.xaml

1<UserControl 2 x:Class="Q07l0vpm2ryc05t.Views.Main" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:TestArea="clr-namespace:Q07l0vpm2ryc05t.Views" 6 xmlns:prism="http://prismlibrary.com/" 7 x:Name="MainView" 8 prism:ViewModelLocator.AutoWireViewModel="True"> 9 <StackPanel> 10 <TextBlock Text="Main" /> 11 <TextBlock Text="{Binding HashCode}" /> 12 <GroupBox Margin="5"> 13 <TestArea:ChildArea /> 14 </GroupBox> 15 </StackPanel> 16</UserControl>

cs:MainViewModel.cs

1using System; 2using Prism.Mvvm; 3using Prism.Services.Dialogs; 4 5namespace Q07l0vpm2ryc05t.ViewModels; 6 7public class MainViewModel : BindableBase, IDialogAware 8{ 9 public string HashCode => $"MainViewModel.HashCode:{GetHashCode()}"; 10 11 public string Title => "MainDialog"; 12 public event Action<IDialogResult> RequestClose; 13 14 public MainViewModel() { } 15 16 public bool CanCloseDialog() => true; 17 public void OnDialogClosed() { } 18 public void OnDialogOpened(IDialogParameters parameters) { } 19}

xml:ChildArea.xaml

1<UserControl 2 x:Class="Q07l0vpm2ryc05t.Views.ChildArea" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:i="http://schemas.microsoft.com/xaml/behaviors" 6 xmlns:prism="http://prismlibrary.com/" 7 x:Name="ChildAreaView" 8 prism:ViewModelLocator.AutoWireViewModel="True"> 9 <i:Interaction.Triggers> 10 11 <!--<i:EventTrigger EventName="Loaded"> 12 <i:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding DataContext, ElementName=MainView}" /> 13 </i:EventTrigger>--> 14 15 <i:TimerTrigger 16 EventName="Loaded" 17 MillisecondsPerTick="0" 18 TotalTicks="1"> 19 <i:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding DataContext, ElementName=MainView}" /> 20 </i:TimerTrigger> 21 22 </i:Interaction.Triggers> 23 24 <StackPanel> 25 <TextBlock Text="ChildArea" /> 26 <TextBlock Text="{Binding HashCode}" /> 27 </StackPanel> 28</UserControl>

cs:ChildAreaViewModel.cs

1using Prism.Mvvm; 2using Reactive.Bindings; 3 4namespace Q07l0vpm2ryc05t.ViewModels; 5 6public class ChildAreaViewModel : BindableBase 7{ 8 public string HashCode => $"MainViewModel.HashCode:{_mainViewModel?.GetHashCode()}"; 9 public ReactiveCommand<object> LoadedCommand { get; } 10 11 private MainViewModel _mainViewModel; 12 13 public ChildAreaViewModel(MainViewModel mainViewModel) 14 { 15 // RegisterSingletonでいいならこれだけ 16 //_mainViewModel = mainViewModel; 17 18 LoadedCommand = new ReactiveCommand<object>().WithSubscribe(Loaded); 19 } 20 21 private void Loaded(object mainViewModel) 22 { 23 _mainViewModel = mainViewModel as MainViewModel; 24 RaisePropertyChanged(nameof(HashCode)); 25 } 26}

cs:App.xaml.cs

1using System.Windows; 2using Prism.Ioc; 3using Q07l0vpm2ryc05t.ViewModels; 4using Q07l0vpm2ryc05t.Views; 5 6namespace Q07l0vpm2ryc05t; 7 8public partial class App 9{ 10 protected override Window CreateShell() => Container.Resolve<MainWindow>(); 11 12 protected override void RegisterTypes(IContainerRegistry containerRegistry) 13 { 14 //containerRegistry.RegisterSingleton<MainViewModel>(); 15 16 containerRegistry.RegisterForNavigation<ViewA>(); 17 containerRegistry.RegisterForNavigation<Main>(); 18 19 containerRegistry.RegisterDialog<Main>(); 20 } 21}

アプリ動画

投稿2024/07/21 09:42

編集2024/07/22 09:41
TN8001

総合スコア9640

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shrgnoko24

2024/07/21 10:30

回答ありがとうございます。 おっしゃる通りで、MainViewModelのインスタンスを取得したいです。 ただ、言葉足らずで大変申し訳ございませんが、 MainViewModelでも初期処理(Loadedコマンド)を定義しており その初期処理で取得したデータを保持した状態のMainViewModelのインスタンスを ChildAreaViewModelで取得したいです。 ご教授いただいた方法を試してみましたが、インスタンスは取得できましたが 初期処理で取得した情報が設定されておりませんでした。 (初期処理前のインスタンスが渡っているのか、別のインスタンスが渡っている…?) >DataContextはひとつしか持てないので、AutoWireViewModelしているViewに違うDataContextを設定したら当然動かなくなりますよね。 そうですよね…解決策が見えず、色々試してみたことを記載しておりました。 当たり前のことで申し訳ございません。
TN8001

2024/07/21 10:42

> 初期処理で取得した情報が設定されておりませんでした。 では、 public void Loaded(object mainViewModel) を残したままLoadedの中で確認したら設定されているでしょうか? > (初期処理前のインスタンスが渡っているのか、別のインスタンスが渡っている…?) MainViewModelはクラス(参照型)ですから、渡ってきた時点では「初期処理前」であっても実際に使うとき(何かの設定値を取得したりするとき)には値が入っていませんか? インスタンスが別々(GetHashCodeでもして確認してください)ということだと、MainViewModelのDI登録をカスタマイズするかでしょうか(AutoWireの細かい仕様は私もわかってません^^;
shrgnoko24

2024/07/21 11:38

>public void Loaded(object mainViewModel) を残したままLoadedの中で確認したら設定されているでしょうか? >MainViewModelはクラス(参照型)ですから、渡ってきた時点では「初期処理前」であっても実際に使うとき(何かの設定値を取得したりするとき)には値が入っていませんか? こちらは引数のmainViewModelはnull、 コンストラクタで取得したmainViewModelも中身の値は設定されておりませんでした。 実際に処理をする時点でも値は入ってなかったです。 ハッシュ値が異なっていたため、異なるインスタンスを参照しているようです…。 難しい…。
shrgnoko24

2024/07/21 11:44

> public void Loaded(object mainViewModel) ちなみにやりたいことが、うまくいく画面遷移の方では、 LoadedのMainViewModelと初期処理のMainViewModelのハッシュ値は同一だったので 同じインスタンスを参照しているようです。 ダイアログで表示することで、うまくいかなくなるんですね…
TN8001

2024/07/21 13:06

> ダイアログで表示することで、うまくいかなくなるんですね… MainViewはメインのWindowと、それとは別にIDialogAwareで開くダイアログの両方に置かれているということですね? で、AutoWireしたMainViewModelが2つできてるっぽいと。 登録済みのがあったら再利用していると思いましたが違うんですかね? [【WPF Prism】 ViewModelLocatorがViewにViewModelを紐づけるまで #C# - Qiita](https://qiita.com/t13801206/items/7bacfb603cb314b61627) こちらはどうでしょうか? [PrismのDialogServiceでDIしたViewModelが毎回初期化される - Life is Really Short, Have Your Life!!](https://aroundthedistance.hatenadiary.jp/entry/2020/09/05/094206) それでもダメな場合はこちらでも試してみたいので、Prismやコンテナのバージョン等を質問に追記してください^^ --- Prismは(特に.NET Foundationを離れて以降)ドキュメントの更新やイシュー対応等が滞っていますよねぇ。
shrgnoko24

2024/07/21 13:46

> MainViewはメインのWindowと、それとは別にIDialogAwareで開くダイアログの両方に置かれているということですね? で、AutoWireしたMainViewModelが2つできてるっぽいと。 両方に置かれている・・・?という表現が正しいか分かりませんが、 MainViewを開く方法は、以下の2パターンがあります。  ①RegionManager.RequestNavigateメソッドで画面遷移する  ②IDialogAwareを使ってダイアログで開く MainViewModelをシングルトン設定にしてもうまくいきませんでした・・・。 質問の「補足」にバージョン情報を追記しました。
shrgnoko24

2024/07/21 14:17

> こちらはどうでしょうか? [PrismのDialogServiceでDIしたViewModelが毎回初期化される - Life is Really Short, Have Your Life!!](https://aroundthedistance.hatenadiary.jp/entry/2020/09/05/094206) > MainViewModelをシングルトン設定にしてもうまくいきませんでした・・・。 こちら確認ミスだったかもしれません・・・。 同じインスタンスを参照するようになったので、もう少し先の処理まで想定通りかどうかを確認してみます。 申し訳ございません。
TN8001

2024/07/21 15:19

> 同じインスタンスを参照するようになったので、もう少し先の処理まで想定通りかどうかを確認してみます。 あ、はい。 こちらは急がないのでごゆっくりどうぞ^^
TN8001

2024/07/21 15:23

(ダイアログの)キャンセル対応等で「シングルトンだと都合が悪い」ことはあるかもしれませんね。。。
TN8001

2024/07/22 09:44

変更を最小限にしたいなら、TimerTriggerでいけることを確認しました(ぶっちゃけクソっぽいが^^; 回答コードを更新しています。
shrgnoko24

2024/08/05 07:24

全く回答できておらずで申し訳ございません・・・。 MainViewModelをシングルトン設定にすると、後続に影響があることがわかったので TimerTriggerを使用し、繰り返しを許容される最小限に設定することにしました。 様々な提案ありがとうございました。
shrgnoko24

2024/08/30 12:22

回答ありがとうございます。 試したところ問題が解決しました! ベストアンサーに選ばせていただきました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.40%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問