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

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

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

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

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WPF

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

Q&A

解決済

1回答

2065閲覧

MVVMで実装しているUserControlでViewの依存関係プロパティとVMのプロパティを連動させる方法

tuyudaku

総合スコア75

C#

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

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WPF

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

1グッド

3クリップ

投稿2022/01/18 09:11

UserControlをMVVMで実装しています。
UserControlにいくつかの依存関係プロパティを定義しているのですが、
その値にVMで処理した結果を使用したい場合があります。

例えばUserControl内にボタンを配置して、それが押されると「Push!」という文字列が依存関係プロパティにセットされるというようなものを作りたいとします。

その場合、私が現在行っている実装方法は
・VMにPropertyChangedEventHandlerのイベントを発火するstringプロパティを定義。
・ボタンにはVMで定義したコマンドをバインド。
・コマンド内でstringプロパティに"Push!"を代入。
・VMからプロパティ変更通知が発火。
・Viewでプロパティ変更通知を受信して、VMのstringプロパティを参照してViewの依存関係プロパティへ代入。

というような手順で行っています。
これで望み通りの動作はしてくれていますが...
まぁ、あまり美しくはないかな...と思っています。

このような実装をしたい場合、他にスマートな実装方法はありますでしょうか?
UserControlをMVVMで実装する必要性は?とかの意見はあると思いますが、
とりあえずMVVMで実装する前提で回答を頂けるとありがたいです。
よろしくお願いいたします。

TN8001👍を押しています

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

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

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

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

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

TN8001

2022/01/18 11:35

MVVM教の方のようですので回答はしません(話が合わないので)が、下記どちらなのかは気になります。 * (Windowの一部を切り出した)部分ビュー的なもの  DependencyPropertyは必要ありません。  親VMが欲しければそのままDataContextに流れてきます。 * 独自コントロールのようなもの  無理にMVVMする必要はありません。  より汎用性を上げるためカスタムコントロールにすることを検討します。 [.net - Wpf UserControl and MVVM - Stack Overflow](https://stackoverflow.com/questions/3333934/wpf-usercontrol-and-mvvm [WPFアプリのMVVM構造の設計時に留意すべきこと - cactuaroid blog](https://cactuaroid.hatenablog.com/entry/2018/08/13/232738 [WPF MVVMスタイルでのUserControlに対するデータの入出力について](https://teratail.com/questions/292544
tuyudaku

2022/01/19 00:20

>MVVM教の方のようですので MVVMを勉強中、かつそういう方針で実装しなければならない という感じです。 といってもそこまで強制ではないですが。 >(Windowの一部を切り出した)部分ビュー的なもの 共通して使用するパーツをユーザーコントロール化したもの というイメージです。 > 独自コントロールのようなもの どちらかといえばこちらに近いかな? でもただのコントロール群ではあるのでやはり部分ビューなのでしょうか? 言ってしまうと、動画再生コントロールという感じです。 再生部、操作部などをまとめたものです。 ここの作成に関しては何度かTN8001さんにお世話になっております...w >無理にMVVMする必要はありません。 最初はそう思っていたのですが、 MVVMで作れそうなところはなるべくそうしよう という空気というか圧がありまして... MVVM勉強中だし頑張ってみるか!という感じです。 >より汎用性を上げるためカスタムコントロールにすることを検討します。 ネットで検索した感じの意見だと、 ユーザーコントロールをMVVMで作る必要はない カスタムコントロールをMVVMで作るなら分かる というような感じでしたが、TN8001さんもそのような意見でしょうか?
TN8001

2022/01/19 03:45

> 言ってしまうと、動画再生コントロールという感じです。 > 再生部、操作部などをまとめたものです。 1回しか使わないなら部分ビューと見ていい気はします。 ユーザーコントロールに切り出さずにWindowにべたに書いてきれいに書ける(コード量は多くなるがまどろっこしいところはない)なら、(切り出す範囲を間違わなければ)ユーザーコントロール化もきれいにできるはずです。 複数回使うなら事実上(見た目のカスタマイズ性を捨てた)カスタムコントロールと見れるので、その場合はButtonやListBoxのようにあなた以外にもだれでも使える(ライブラリを作る)つもりで書かないとおかしなことになります(この辺頭の切り替えが必要です) > ユーザーコントロールをMVVMで作る必要はない > カスタムコントロールをMVVMで作るなら分かる > というような感じでしたが、TN8001さんもそのような意見でしょうか? 逆ですね。 * ユーザーコントロールを 1. の方(部分ビュー的なもの)と考えるならMVVMで作るのも分かる * カスタムコントロールをMVVMで作る必要はない と考えています。 普通カスタムコントロールはBindingを使用しません(TemplateBindingやRelativeSourceによる自身のDependencyPropertyへのバインドはあります) 例えばButtonViewModelなんてものはありませんよね? もちろん外に見えないだけで内部ではバインドしているカスタムコントロールもあるとは思いますが、それは一般的に言うMVVM(コードビハインドを書かない)とはだいぶ実態が違うと思います(バリバリコードビハインド書いているんので)
TN8001

2022/01/24 11:12

tuyudakuさん だんまりになってしまいましたが、やっぱり話が合わないということでしょうか? [WPF MVVMスタイルでのUserControlに対するデータの入出力について](https://teratail.com/questions/292544 [MVVM構造のUserControlに依存関係プロパティを生やして親画面のVMにバインドしたい](https://teratail.com/questions/362091
guest

回答1

0

ベストアンサー

回答しないといいましたが、コメントだけでは伝えられそうにないので回答します^^;
文字数制限があるので可能な限りシンプルに、NumericUpDownを題材にさせていただきました(超雑なのは目をつぶってください^^;

6パターンあります。

  • ベタ置き
  • 部分ビュー的
  • カスタムコントロール的
  • オリジナル
  • 継承
  • 添付プロパティ

イメージとしてはベタ置きで書いていたがゴチャついてきたので、部分ビュー的に切り出した。
その後複数個使いたくなったので、カスタムコントロール的に作り替えたといった感じです。
もちろんNumericUpDownならカスタムコントロールにするのがベストですが、使いまわす気がないなら手前で留めておくのもありです。

MVVMで実装する前提で回答を頂けるとありがたいです。

とのことですので後半3つは無理やり作りましたが、個人的には意味が分かんないですね。
部分ビュー的・カスタムコントロール的の面倒なところをわざわざ合わせたというか無駄にまどろっこしいというか、「それ何の意味があるの?」感が伝わりますかね?^^;

MainWindow

xml

1<Window 2 x:Class="Q2x9yv8s160mb6u.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="clr-namespace:Q2x9yv8s160mb6u" 6 Width="800" 7 Height="450"> 8 <Window.DataContext> 9 <local:MainViewModel /> 10 </Window.DataContext> 11 <UniformGrid Columns="3"> 12 <GroupBox Header="ベタ置き"> 13 <StackPanel> 14 <TextBlock Text="{Binding Value}" /> 15 <RepeatButton Command="{Binding IncrementCommand}" Content="Up" /> 16 <RepeatButton Command="{Binding DecrementCommand}" Content="Down" /> 17 <TextBlock Text="{Binding Value, StringFormat=今の値は {0} です}" /> 18 </StackPanel> 19 </GroupBox> 20 21 <GroupBox Header="部分ビュー的"> 22 <StackPanel> 23 <local:UserControl1 DataContext="{Binding UserControl1ViewModel}" /> 24 <TextBlock Text="{Binding UserControl1ViewModel.Value, StringFormat=今の値は {0} です}" /> 25 </StackPanel> 26 </GroupBox> 27 28 <GroupBox Header="カスタムコントロール的"> 29 <StackPanel> 30 <local:UserControl2 x:Name="userControl2" /> 31 <TextBlock Text="{Binding Value, ElementName=userControl2, StringFormat=今の値は {0} です}" /> 32 <!-- もちろんこうなっていてもいい(以下3・4・5も同様) --> 33 <!--<local:UserControl2 Value="{Binding Hoge}" /> 34 <TextBlock Text="{Binding Hoge, StringFormat=今の値は {0} です}" />--> 35 </StackPanel> 36 </GroupBox> 37 38 <GroupBox Header="オリジナル"> 39 <StackPanel> 40 <local:UserControl3 x:Name="userControl3" /> 41 <TextBlock Text="{Binding Value, ElementName=userControl3, StringFormat=今の値は {0} です}" /> 42 </StackPanel> 43 </GroupBox> 44 45 <GroupBox Header="継承"> 46 <StackPanel> 47 <local:UserControl4 x:Name="userControl4" /> 48 <TextBlock Text="{Binding Value, ElementName=userControl4, StringFormat=今の値は {0} です}" /> 49 </StackPanel> 50 </GroupBox> 51 52 <GroupBox Header="添付プロパティ"> 53 <StackPanel> 54 <local:UserControl5 x:Name="userControl5" /> 55 <TextBlock Text="{Binding (local:UserControl5.Value), ElementName=userControl5, StringFormat=今の値は {0} です}" /> 56 </StackPanel> 57 </GroupBox> 58 </UniformGrid> 59</Window>

cs

1using System.Windows; 2using CommunityToolkit.Mvvm.ComponentModel; 3using CommunityToolkit.Mvvm.Input; 4 5namespace Q2x9yv8s160mb6u 6{ 7 public class UserControlViewModel : ObservableObject 8 { 9 public int Value { get => _Value; set => SetProperty(ref _Value, value); } 10 private int _Value; 11 12 public RelayCommand IncrementCommand { get; } 13 public RelayCommand DecrementCommand { get; } 14 15 public UserControlViewModel() 16 { 17 IncrementCommand = new(() => Value++); 18 DecrementCommand = new(() => Value--); 19 } 20 } 21 22 public class MainViewModel : UserControlViewModel // 同じものを書くのが無駄なので継承で済ませた^^; 23 { 24 public UserControlViewModel UserControl1ViewModel { get; } = new(); 25 } 26 27 public partial class MainWindow : Window 28 { 29 public MainWindow() => InitializeComponent(); 30 } 31}

UserControl1(部分ビュー的)

xml

1<UserControl 2 x:Class="Q2x9yv8s160mb6u.UserControl1" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 5 <StackPanel> 6 <TextBlock Text="{Binding Value}" /> 7 <RepeatButton Command="{Binding IncrementCommand}" Content="Up" /> 8 <RepeatButton Command="{Binding DecrementCommand}" Content="Down" /> 9 </StackPanel> 10</UserControl>

cs

1using System.Windows.Controls; 2 3namespace Q2x9yv8s160mb6u 4{ 5 public partial class UserControl1 : UserControl 6 { 7 public UserControl1() => InitializeComponent(); 8 } 9}

UserControl2(カスタムコントロール的)

xml

1<UserControl 2 x:Class="Q2x9yv8s160mb6u.UserControl2" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="clr-namespace:Q2x9yv8s160mb6u"> 6 <StackPanel> 7 <TextBlock Text="{Binding Value, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UserControl2}}}" /> 8 <RepeatButton Click="UpButton_Click" Content="Up" /> 9 <RepeatButton Click="DownButton_Click" Content="Down" /> 10 </StackPanel> 11</UserControl>

cs

1using System.Windows; 2using System.Windows.Controls; 3 4namespace Q2x9yv8s160mb6u 5{ 6 public partial class UserControl2 : UserControl 7 { 8 public int Value { get => (int)GetValue(ValueProperty); set => SetValue(ValueProperty, value); } 9 public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(int), typeof(UserControl2), new PropertyMetadata(0)); 10 11 public UserControl2() => InitializeComponent(); 12 13 private void UpButton_Click(object sender, RoutedEventArgs e) => Value++; 14 private void DownButton_Click(object sender, RoutedEventArgs e) => Value--; 15 } 16}

UserControl3(オリジナル)

xml

1<UserControl 2 x:Class="Q2x9yv8s160mb6u.UserControl3" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 5 <StackPanel> 6 <TextBlock Text="{Binding Value}" /> 7 <RepeatButton Command="{Binding IncrementCommand}" Content="Up" /> 8 <RepeatButton Command="{Binding DecrementCommand}" Content="Down" /> 9 </StackPanel> 10</UserControl>

cs

1using System.Windows; 2using System.Windows.Controls; 3using System.Windows.Data; 4 5namespace Q2x9yv8s160mb6u 6{ 7 public partial class UserControl3 : UserControl 8 { 9 public int Value { get => (int)GetValue(ValueProperty); set => SetValue(ValueProperty, value); } 10 public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(int), typeof(UserControl3), new PropertyMetadata(0)); 11 12 public UserControl3() 13 { 14 InitializeComponent(); 15 var vm = new UserControlViewModel(); 16 //vm.PropertyChanged += (s, e) => Value = vm.Value; 17 // コードでバインドする? 18 SetBinding(ValueProperty, new Binding(nameof(vm.Value))); 19 DataContext = vm; 20 } 21 } 22}

UserControl4(継承)

xml

1<local:UserControl4Base 2 x:Class="Q2x9yv8s160mb6u.UserControl4" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="clr-namespace:Q2x9yv8s160mb6u" 6 Value="{Binding Value}"> 7 <StackPanel> 8 <TextBlock Text="{Binding Value}" /> 9 <RepeatButton Command="{Binding IncrementCommand}" Content="Up" /> 10 <RepeatButton Command="{Binding DecrementCommand}" Content="Down" /> 11 </StackPanel> 12</local:UserControl4Base>

cs

1using System.Windows; 2using System.Windows.Controls; 3 4namespace Q2x9yv8s160mb6u 5{ 6 // <local:UserControl4Base x:Class="Q2x9yv8s160mb6u.UserControl4" Value="{Binding Value}"> 7 // こう書けるようにするため、仕方なく間にクラスを挟む 8 public class UserControl4Base : UserControl 9 { 10 public int Value { get => (int)GetValue(ValueProperty); set => SetValue(ValueProperty, value); } 11 public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(int), typeof(UserControl4Base), new PropertyMetadata(0)); 12 } 13 14 public partial class UserControl4 : UserControl4Base 15 { 16 public UserControl4() 17 { 18 InitializeComponent(); 19 DataContext = new UserControlViewModel(); 20 } 21 } 22}

UserControl5(添付プロパティ)

xml

1<UserControl 2 x:Class="Q2x9yv8s160mb6u.UserControl5" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="clr-namespace:Q2x9yv8s160mb6u" 6 local:UserControl5.Value="{Binding Value}"> 7 <StackPanel> 8 <TextBlock Text="{Binding Value}" /> 9 <RepeatButton Command="{Binding IncrementCommand}" Content="Up" /> 10 <RepeatButton Command="{Binding DecrementCommand}" Content="Down" /> 11 </StackPanel> 12</UserControl>

cs

1using System.Windows; 2using System.Windows.Controls; 3 4namespace Q2x9yv8s160mb6u 5{ 6 public partial class UserControl5 : UserControl 7 { 8 public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached("Value", typeof(int), typeof(UserControl5), new PropertyMetadata(0)); 9 public static int GetValue(DependencyObject obj) => (int)obj.GetValue(ValueProperty); 10 public static void SetValue(DependencyObject obj, int value) => obj.SetValue(ValueProperty, value); 11 12 public UserControl5() 13 { 14 InitializeComponent(); 15 DataContext = new UserControlViewModel(); 16 } 17 } 18}

アプリ画像

投稿2022/01/19 09:01

編集2023/07/29 14:31
TN8001

総合スコア9317

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

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

tuyudaku

2022/01/25 05:17

返事が遅くなり申し訳ありません。 回答ありがとうございました。 コードもついててとても分かりやすくて助かりました! 個人的には親から子のDataContexをBindingする方法に目からうろこです。 確かにその方法があるなと思いました。 この方法は一般的というか、まぁよくやる手法という感じでしょうか? それともこういうやり方もなくはないよねという感じでしょうか?
TN8001

2022/01/25 05:23

> 個人的には親から子のDataContexをBindingする方法に目からうろこです。 > この方法は一般的というか、まぁよくやる手法という感じでしょうか? > それともこういうやり方もなくはないよねという感じでしょうか? 部分ビュー的のことでしょうか? 例えばListBoxとListBoxItemの関係のように非常によくある手法です。
TN8001

2022/01/25 07:34

わたしも過去「DataTemplateで、ViewModelとView(UserControl)を結び付けて表示する」ような例を見て「ほぇ~!」となりました。 たしかかずきさんのブログかなんかだった気がするのですが見つからないです;; イメージとしては↓こちらが近いです。 [【C#】【WPF】MVVMらしく画面遷移する方法! | Step1](https://alfort.online/995
tuyudaku

2022/01/25 07:35

>例えばListBoxとListBoxItemの関係のように非常によくある手法です。 確かに、そう考えたらそうですね。 ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問