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

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

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

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

WPF

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

Q&A

解決済

1回答

4546閲覧

[WPF]親コントロールのDataContextをCommandParameterにしたい

ttact

総合スコア171

C#

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

WPF

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

0グッド

0クリップ

投稿2022/02/25 00:33

課題の背景

WPFで、ボタンの派生クラスを作っています。
そのボタンのDataContextには、自身のViewModelが設定されています。

そのボタンのCommandにバインドしたコマンドの引数に、そのボタンの親コントロールのDataContextを渡したいです。

問題解決の方針として、できればコードビハインドでの実装を避けたいです。

課題

以下のようにしてCommandParameterにバインドを行ってみたのですが、コマンドのExecute()の引数にはnullが渡されてしまいます。

xml

1<Button x:Class="Sample.MyButton" 2 (xmlnsは略) 3 Command={Binding MyCommand} 4 CommandParameter="{Binding Parent.DataContext, 5 RelativeSource={RelativeSource Mode=Self}}" 6 />

コードビハインドを使わずにParent.DataContextを得る良い方法はないでしょうか?

やってみたこと

Microsoft.Xaml.Behaviors.Wpfを使ってLoadedイベントにて取得する際は、nullではありません。

xml

1(略) 2 xmlns:bh="http://schemas.microsoft.com/xaml/behaviors" 3(略) 4 <bh:Interaction.Triggers> 5 <bh:EventTrigger EventName="Loaded"> 6 <bh:InvokeCommandAction 7 Command="{Binding SettingParentViewModelCommand}" 8 CommandParameter="{Binding Parent.DataContext, 9 RelativeSource={RelativeSource 10 Mode=FindAncestor, 11 AncestorType=local:MyButton, 12 AncestorLevel=1}}" 13 /> 14 </bh:EventTrigger> 15</bh:Interaction.Triggers>

これなら一応実現はできているのですが、下記の理由から微妙だなぁと思っています:

  • 本来実行したいコマンドとは別のコマンドでViewModelのフィールドにキャッシュすることになるので、コードの意図が読んでいて若干わかりづらい
  • コマンド実行しないときでも必ず実行される点が嬉しくない

DataContextは依存関係プロパティではなくて、かつCommandParameterのバインディングが行われるタイミングがコマンド起動時ではなくDataContextChangedの時なのでしょうかね。。。

環境

Visual Studio Professional 2022 ver.17.0.6
.NET 6.0.2
Microsoft.Xaml.Behaviors.Wpf 1.1.39

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

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

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

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

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

hihijiji

2022/02/25 02:15

どうやって親ViewのDataContextにViewModelのインスタンスを渡しているのかの記載がないと誰も答えられないですよ。
hihijiji

2022/02/25 02:19

読み飛ばしていた。 > 自身のViewModelが設定されています 原則的にViewModelは作らず親のViewのViewModelにバインドします。
ttact

2022/02/25 02:29

> hihijiji様 それは何故なのでしょうか。親ViewのDataContextの設定タイミングなどで何か影響があるのでしょうか。 質問に対するお返事としては...ViewModelファーストのMVVMライブラリを使っていまして、ざっくりとは以下のような感じになってます。 void 画面遷移メソッド(ViewModel vm, ContentControl cc) { var view = ViewFactory(vm.GetType()); view.DataContext = vm; cc.Content = view; } 今回のボタンは、上記のviewの配下に含まれる形になっております。 質問に記載の通りLoadedイベントでは取得できています。ボタン押下でコマンドが発動するタイミングはLoadedよりは間違いなく後ですので、コマンド実行時には目的のViewModelはボタン.Parent.DataContextに確実に格納されているはずです。
ttact

2022/02/25 02:30

> hihijiji様 私のコメントも入れ違ってしまいました。すみません。
hihijiji

2022/02/25 04:16

普通にRelativeSourceで親のViewを探してDataContextを指定すればいいだけです。 ButtonのViewModelは不要です。
ttact

2022/02/25 04:28

> hihijiji様 ButtonのViewModelは別の機能のために必要でして、コードビハインドで実装するとその機能がユニットテストできないために独立化しています。 > 普通にRelativeSourceで親のViewを探してDataContextを指定すればいいだけです。 これはFindAncenstorでやったら可能でした。ありがとうございます!別途、回答化します。
guest

回答1

0

自己解決

hihijiji様のヒントから、RelativeSourceで1つ上の要素を単純に取ってくればいいんじゃ?と気づいたので、以下のようにしました。

xml

1<Button x:Class="Sample.MyButton" 2 (xmlnsは略) 3 Command={Binding MyCommand} 4 CommandParameter="{Binding DataContext, 5 RelativeSource={RelativeSource 6 Mode=FindAncestor, 7 AncestorType=FrameworkElement, 8 AncestorLevel=1}}" 9 />

うーん、RelativeSourceのルールがイマイチよくわかりませんが、今回はなんとかクリアできたので良しとします。

投稿2022/02/25 04:31

ttact

総合スコア171

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問