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

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

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

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

WPF

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

Q&A

解決済

2回答

17564閲覧

UserControl の DependencyProperty を binding のソースにしたい場合

lazex

総合スコア604

C#

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

WPF

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

0グッド

2クリップ

投稿2016/08/17 13:37

編集2016/08/17 13:38

UserControl で DependencyProperty としているプロパティを UserControl 内のコントロールのプロパティに Binding したいです。

例を挙げると、 NumericUpDown で数値の value を TextBox の Text に binding するようなものです。
value は UserControl を使う側から Binding のターゲットにできるように DependencyProperty にしていて、 UserControl の中では Binding のソースとして、 TextBox に Binding となります。

DependencyProperty のため、 UserControl のプロパティとなるので、単純にするならば DataContext に this を設定して、DependencyProperty の通常のプロパティの方 (valueProperty に対する value) を設定すれば、自分で変更通知しなくても済むようにできました。

便利かつ楽ですが、 DataContext に this を設定するのは良くないと聞いたので別の方法にしようと思うのですが、どういう方法が一般的でしょうか?

DependencyProperty の PropertyMetaData で設定する PropertyChangedCallback を使って DependencyProperty の変更時に ViewModel の対応するプロパティを更新し、ViewModel の対応するプロパティの setter で DependencyProperty を更新するという処理を毎度書くのでしょうか?
動きはしましたが、コードが長くもっと良い方法がありそうに感じました。

良い方法がありましたら教えてもらえないでしょうか。

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

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

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

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

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

guest

回答2

0

ベストアンサー

numをバインドさせつつ、別のViewModelをDataContextとして設定しているので動いていないのです。numプロパティをバインドさせてそれで終わりにするのが普通です。

XAML

1<Window x:Class="WpfApplication2.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfApplication2" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="350" Width="525"> 9 <Window.DataContext> 10 <local:MainWindowViewModel /> 11 </Window.DataContext> 12 <Grid> 13 <local:UserControl1 prop="{Binding num}" /> 14 </Grid> 15</Window>

XAML

1<UserControl x:Class="WpfApplication2.UserControl1" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:WpfApplication2" 7 mc:Ignorable="d" Name="usercontrol" 8 d:DesignHeight="300" d:DesignWidth="300"> 9 <Grid> 10 <TextBox Text="{Binding prop, ElementName=usercontrol}" /> 11 </Grid> 12</UserControl>

C#

1public partial class UserControl1 : UserControl 2{ 3 public UserControl1() 4 { 5 InitializeComponent(); 6 } 7 8 public int prop 9 { 10 get { return (int)GetValue(propProperty); } 11 set { SetValue(propProperty, value); } 12 } 13 14 // Using a DependencyProperty as the backing store for prop. This enables animation, styling, binding, etc... 15 public static readonly DependencyProperty propProperty = 16 DependencyProperty.Register("prop", typeof(int), typeof(UserControl), new PropertyMetadata(0)); 17 18}

C#

1public class MainWindowViewModel : INotifyPropertyChanged 2{ 3 private int _num; 4 public int num 5 { 6 get 7 { 8 return _num; 9 } 10 set 11 { 12 _num = value; 13 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("num")); 14 } 15 } 16 public event PropertyChangedEventHandler PropertyChanged; 17 18 public MainWindowViewModel() 19 { 20 num = 10; 21 } 22}

投稿2016/08/20 12:39

編集2016/08/20 12:41
Tak1wa

総合スコア4791

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

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

lazex

2016/08/20 12:55

ありがとうございます。 では、UserControl に ViewModel をもたせることはできないのでしょうか? 最初の質問の例に書いた NumericUpdown ですと、 UserControl の上下ボタンを押したコマンドで UserControl1 の値を変更する というような MainWindow とは別に UserControl の中だけで完結する ViewModel を使った操作をしたいときはどうするのでしょうか。
Tak1wa

2016/08/20 13:16

出来ます。その場合は質問者さんが実装されたようにUserControl.DataContextにUserControl用のViewModelを設定するのが正しいです。 ただし、その場合ですと、UserControlに依存関係プロパティを用意してnumをバインドさせていますがそれは何故なのでしょうか。 そこを明確にして頂ければ一歩解決に近づけるかもしれません。 少し乱暴な言い方かもしれませんが、現状のやり方だとUserControlに2つのViewModelを設定しようとしています。
lazex

2016/08/20 13:47

やりたいこととしましては、まず、UserControlだけでViewとViewModelを使い、UserControl 内のボタンを押すなどのコマンドや、テキストボックスの変更時に UserControl の ViewModel で処理を行い値を書き換えてそれを UserControl 内の TextBox に反映したいです。 なので、UserControl と UserControlVM 間での Binding をしています。 また、UserControl を使う側の MainWindow からも UserControl のプロパティを参照したり書き換えたりしたいです。 伝わりづらいかもしれないので、NumericUpdown とした例も書きます。(実際つくりたいのはもう少し複雑なものですが、今回質問したいことは NumericUpdown の機能で満たせてると思うので NumericUpdown として回答いただければと思います) まず、NumericUpdown と NumericUpdownVM で、上下ボタンを押したときに TextBox の数字を増減させたり、マイナス値の入力は0にするなどの処理をします。 この処理は MainWindow など NumericUpdown を使うところによらないので、 NumericUpdown 用の ViewModel で行いたいです。 次に使う側の MainWindow では、 NumericUpdown の値に応じて別のコントロールの個数を制御したり、ボタン押したときに NumericUpdown の値を初期値にリセットするなどの処理をしたいので、 MainWindow の ViewModel に NumericUpdown の数値を Binding させる必要があると思います。 長くなりましたが、こういう場合はどうするのが良いでしょうか。
Tak1wa

2016/08/20 13:59

なるほど。よくわかりました。 いくつか方法があると思いますが、最も単純なのはおそらくViewModelを入れ子にすることです。 ユーザーインターフェース上MainWindowの中にUserControlがあるように、ビューモデルもMainWindowViewModelの中にUserControlViewModelを持てば良いです。 MainWindowViewModelは自分が保持しているUserControlViewModelインスタンスにアクセスすれば良く、Viewを経由する必要はありません。 あとはXAMLまたはコードビハインドでViewを構築するときに、UserControlのDataContextにMainWindowsが保持しているUserControlViewModelをバインドしてやればOKです。 *** 別の解としては当初のようにUserControlに依存関係プロパティを用意してやる方法もあります。それについてはソースコード載せてみますので別で回答します少々お待ちを
Tak1wa

2016/08/20 15:04

後者についてやってみました。やはりDataContext2つが存在しているので親子関係を持たずにView経由でプロパティのリンクを行いたいということであればコードビハインドで値の結合をやってやらねばならない気がしますね。(今回は単純にTextChangedイベント入れました) ```C# public partial class UserControl1 : UserControl { public UserControl1() { InitializeComponent(); } public int Num { get { return (int)GetValue(NumProperty); } set { SetValue(NumProperty, value); } } // Using a DependencyProperty as the backing store for Num. This enables animation, styling, binding, etc... public static readonly DependencyProperty NumProperty = DependencyProperty.Register("Num", typeof(int), typeof(UserControl1), new PropertyMetadata(0)); private void TextBox_TextChanged(object sender, TextChangedEventArgs e) { Num = int.Parse(txt.Text); } } public class UserControlViewModel : BindableBase { private int _Num; public int Num { get { return _Num; } set { SetProperty(ref _Num, value); } } private DelegateCommand _CommandHoge; public DelegateCommand CommandHoge { get { if(_CommandHoge == null) { _CommandHoge = new DelegateCommand(() => Num++); } return _CommandHoge; } } } ``` ```XAML <UserControl x:Class="WpfApplication3.UserControl1" 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" xmlns:local="clr-namespace:WpfApplication3" mc:Ignorable="d" Name="usercontrol" d:DesignHeight="300" d:DesignWidth="300"> <Grid Name="grid"> <Grid.DataContext> <local:UserControlViewModel /> </Grid.DataContext> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBox Grid.Column="0" Name="txt" Text="{Binding Num}" TextChanged="TextBox_TextChanged" /> <Button Grid.Column="1" Name="btn" Content="hoge" Command="{Binding CommandHoge}" /> </Grid> </UserControl> ```
lazex

2016/08/20 16:00

丁寧にありがとうございます。 TextBox などのように View でプロパティを Binding するとなるとやっぱり自分で値を同期させる処理がいるのですね。 ViewModel のネストは構造が複雑そうですが簡単にやれそうなのでこの方法を使ってみようかと思います。 ありがとうございました。
guest

0

こんにちは。
文章だけでしたので構成を誤解していたらすみません。

今回のケースであれば、UserControl内部の子エレメントのDependencyPropertyへUserControlのDependencyPropertyを直接バインドすれば良いだけです。
その方法はDataContextを経由するのではなく、BindingのSourceでElementNameまたはAncestorTypeなどを使ってUserControl自身をバインディングソースにして、BindingのPathはUserControlのDependencyPropertyを指定します。

投稿2016/08/19 16:43

編集2016/08/19 16:44
Tak1wa

総合スコア4791

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

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

lazex

2016/08/20 07:40 編集

直接という方法があるんですね、全然思いつきませんでした。 ですがこの方法ですと、UserControl の ViewModel 側で binding している値を参照したり書き換えたりしたい場合はどうすればいいのでしょうか?
Tak1wa

2016/08/20 09:06

普通の参照できませんか? UserControl.HogeというDependencyPropertyとViewModelのプロパティをバインドさせれば、UserControl内部でバインドさせた結果が反映されると思います。 もしこれでわからなければ、現在の実装がどのようになっているかソース提示して頂けるともう少しピンポイントで答えれるかもしれませぬ。
lazex

2016/08/20 12:14

直接 bind するようにしてみた時点のコードはこのようになっています。 usercontrol の xaml ```xaml <UserControl x:Name="usercontrol"              (略)>     <Grid>         <TextBox Text="{Binding prop, ElementName=usercontrol}" />     </Grid> </UserControl> ``` 使う側のxaml ```xaml <Window (略)     <Grid>         <local:UserControl1 prop="{Binding num}" />     </Grid> </Window> ``` usercontrol のコード ```c# public partial class UserControl1 : UserControl {     public UserControl1()     {         InitializeComponent();         this.DataContext = new UserControl1VM(this);     }     public int prop     {         get { return (int)GetValue(propProperty); }         set { SetValue(propProperty, value); }     }     public static readonly DependencyProperty propProperty =         DependencyProperty.Register("prop", typeof(int), typeof(UserControl1), new PropertyMetadata(0)); } ``` これに UserControl の DependencyProperty と ViewModel プロパティを Binding させるように ViewModel のコードを以下のようにしてみました。 ```c# public class UserControl1VM {     public int vm_prop { get; set; }     public UserControl1VM(UserControl1 v)     {         v.SetBinding(UserControl1.propProperty, new Binding("vm_prop")         {             Source = this         });     } } ``` Binding は ViewModel じゃなくて View 側でやったほうがいいのかもしれませんが、どちらでやるにせよ、この方法では動かなかったです。 どこがおかしいのでしょうか。
Tak1wa

2016/08/20 12:37 編集

見づらいので別でリプライしまs
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問