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

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

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

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

MVVM

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

XAML

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

WPF

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

Q&A

解決済

1回答

4403閲覧

MVVM構造のUserControlに依存関係プロパティを生やして親画面のVMにバインドしたい

ry188472

総合スコア74

C#

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

MVVM

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

XAML

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

WPF

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

0グッド

2クリップ

投稿2021/09/30 08:01

質問

VMを持たないUserControlの場合、コードビハインドに依存関係プロパティを実装してDataContextを設定しなければ、親画面のViewで配置したときに親画面のDataContext(親画面のViewModel)が引き継がれますよね。それにより親画面ViewModelのプロパティをUserControlにバインドできると思います。

ここで、UserControlがViewModel(UserControlViewModel)を持つ場合、依存関係プロパティ自体はコードビハインドに実装する必要があると思いますが、UserControlのDataContextはUserControlViewModelになりますよね。こうなると、親画面のViewに配置してもUserControlのDataContextはUserControlViewModelのため依存関係プロパティを親画面のViewModelのプロパティにバインドすることができない(UserControlViewModelにそのようなプロパティが存在しないというエラーが起きる)と思いますが、これはどのように解決すればよいでしょうか?

目的

  • UserConrolにはTextBoxTextblockがあります。TextBoxに文字列が入力されたとき、VMで加工してTextblockに設定します。Textblockが設定されたときに依存関係プロパティを使ってUserControlを配置する親画面に「入力値」「加工された文字列」の両方を通知するのが目的です。
  • 文字列の加工処理はViewのコードビハインドで実装できません。DB問い合わせ等をするのでVMに処理をDIしたいためです。

UserControlのコード

UserControl1.xaml
*UserControl1.xaml.csに依存関係プロパティを定義
*UserControl1ViewModel.csにpublic ReactiveProperty<string>を作ってTextBox,TextblockTextプロパティをバインド

xaml

1<UserControl x:Class="Project1.Views.UserControl1" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:prism="http://prismlibrary.com/" 5 prism:ViewModelLocator.AutoWireViewModel="True"> 6 <Grid> 7 <Grid.ColumnDefinitions> 8 <ColumnDefinition Width="*"/> 9 <ColumnDefinition Width="*"/> 10 </Grid.ColumnDefinitions> 11 <TextBox Name="CodeText" Grid.Column="0" Text="{Binding MyCode.Value}" /> 12 <TextBlock Name="NameText" Grid.Column="1" 13 Text="{Binding MyName.Value, NotifyOnTargetUpdated=True}" 14 TargetUpdated="NameText_TargetUpdated" /> 15 </Grid> 16</UserControl>

UserControl1.xaml.cs

c#

1using System.Windows; 2using System.Windows.Data; 3using System.Windows.Controls; 4using System; 5using Reactive.Bindings.Interactivity; 6 7namespace Project1.Views 8{ 9 public partial class UserControl1 : UserControl 10 { 11 public UserControl1() 12 { 13 InitializeComponent(); 14 } 15 16 private void NameText_TextChanged(object sender, RoutedEventArgs e) 17 { 18 InputedMyCode = CodeText.Text; 19 DbMyName = NameText.Text; 20 } 21 22 // string型のInputedMyCodeとDbMyNameのDependencyPropertyを実装 23 public static readonly DependencyProperty InputedMyCodeProperty = 24 DependencyProperty.Register(nameof(InputedMyCode), 25 typeof(string), typeof(UserControl1), new PropertyMetadata("")); 26 public string InputedMyCode 27 { 28 get => (string)GetValue(InputedMyCodeProperty); 29 set => SetValue(InputedMyCodeProperty, value); 30 } 31 public static readonly DependencyProperty DbMyNameProperty = 32 DependencyProperty.Register(nameof(DbMyName), 33 typeof(string), typeof(UserControl1), new PropertyMetadata("")); 34 public string DbMyName 35 { 36 get => (string)GetValue(DbMyNameProperty); 37 set => SetValue(DbMyNameProperty, value); 38 } 39 } 40}

環境

  • .NET 5
  • C# 9.0 WPFアプリケーション
  • MVVMフレームワークにPrismを使用→ViewとViewModelはxaml上でViewModelLocator.AutoWireViewModel="True"にして紐付け

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

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

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

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

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

hihijiji

2021/10/01 06:35

ざっと読んだ限りでは、 > VMで加工 ここに問題があるような気がします。 加工にDBに問い合わせが必要なら尚更にModelの仕事かと… 文字列加工用のModelを別途作って、そのインスタンスを複数のVMで共有すれば それで済む話では?
hihijiji

2021/10/01 06:48

あと細かいようですが、DependencyPropertyはコードビハインドに書く必要はありません。 コードビハインドに書いたほうが収まりがいいからほとんどの人がそうしているだけです。
ry188472

2021/10/01 07:31

質問本文に書いてませんが、DB問い合わせ処理はModelで実施してます。当然の内容なので省略しておりました。わかりにくくてすみません。 DependencyPropertyってVMに書けるんですか?VMにDependencyObjectを継承させるんですかね。でもそうすると親画面でUserControlを配置したときにxaml上で親画面のVMにバインドできないのでは?
hihijiji

2021/10/01 09:19

どこにでも書けるからってVMに書くなんてとんでもない。 ご自身で書かれている通り、public static だからって言うのが唯一の理由。 これで分からなければC#の入門からやり直してください。
guest

回答1

0

ベストアンサー

なんか堂々巡りですね^^;
どうもprismユーザーと話が合わないのは、私がわかってないだけなんでしょうか?(だったらいいのですが^^;

理屈上はRelativeSourceWindowとでもすれば引っ張ってこれますが、そんなのイヤすぎますよね?

そもそもガッツリprismしている場合、UserControlの表示は↓のようになりませんか?(Prism Full App(.NET Core)テンプレートより)

xml

1<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.ContentRegion}" />

DependencyPropertyを設定するタイミングもないです(まあやりようはあるんでしょうけど)

一度Prism Full App(.NET Core)テンプレートを作っていただいて、ViewAViewAViewModelMessageServiceの関係を見ていただけたらいいと思います。


例がないと話の前提が合いませんので雑に作りました(Func<T,R>が気持ち悪いのは気にしないでください^^;
NuGet Gallery | Prism.DryIoc 8.1.97
NuGet Gallery | ReactiveProperty 7.12.0

TextBoxTextBlockの組が4つあります。どれも動作は同じです(入力値の小文字が大文字になって表示される)

4つのうち1つだけおかしい(異質な)ものがあります。

xml:MainWindow.xaml

1<Window 2 x:Class="Questions362091.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 xmlns:views="clr-namespace:Questions362091.Views" 7 Width="525" 8 SizeToContent="Height"> 9 <StackPanel> 10 11 <GroupBox Header="ベタ置き"> 12 <StackPanel> 13 <TextBox Text="{Binding Input.Value, UpdateSourceTrigger=PropertyChanged}" /> 14 <TextBlock HorizontalAlignment="Center" Text="" /> 15 <TextBlock Text="{Binding Output.Value}" /> 16 </StackPanel> 17 </GroupBox> 18 19 <GroupBox Header="UserControl1"> 20 <views:UserControl1 /> 21 </GroupBox> 22 23 <GroupBox Header="PrismUserControl1"> 24 <ContentControl prism:RegionManager.RegionName="ContentRegion" /> 25 </GroupBox> 26 27 <GroupBox Header="UserControl2"> 28 <views:UserControl2 Converter="{Binding Converter}" Input="{Binding Input.Value}" /> 29 </GroupBox> 30 </StackPanel> 31</Window>

xml:UserControl1.xaml

1<UserControl 2 x:Class="Questions362091.Views.UserControl1" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 5 <StackPanel> 6 <TextBox Text="{Binding Input.Value, UpdateSourceTrigger=PropertyChanged}" /> 7 <TextBlock HorizontalAlignment="Center" Text="" /> 8 <TextBlock Text="{Binding Output.Value}" /> 9 </StackPanel> 10</UserControl>

xml:PrismUserControl1.xaml

1<UserControl 2 x:Class="Questions362091.Views.PrismUserControl1" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 5 <StackPanel> 6 <TextBox Text="{Binding Input.Value, UpdateSourceTrigger=PropertyChanged}" /> 7 <TextBlock HorizontalAlignment="Center" Text="" /> 8 <TextBlock Text="{Binding Output.Value}" /> 9 </StackPanel> 10</UserControl>

xml:UserControl2.xaml

1<UserControl 2 x:Class="Questions362091.Views.UserControl2" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:views="clr-namespace:Questions362091.Views"> 6 <StackPanel> 7 <TextBox Text="{Binding Input, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType={x:Type views:UserControl2}}}" /> 8 <TextBlock HorizontalAlignment="Center" Text="" /> 9 <TextBlock Text="{Binding Output, RelativeSource={RelativeSource AncestorType={x:Type views:UserControl2}}}" /> 10 </StackPanel> 11</UserControl>

cs:UserControl2.xaml.cs

1using System; 2using System.Windows; 3using System.Windows.Controls; 4 5namespace Questions362091.Views 6{ 7 public partial class UserControl2 : UserControl 8 { 9 public string Input { get => (string)GetValue(InputProperty); set => SetValue(InputProperty, value); } 10 public static readonly DependencyProperty InputProperty = DependencyProperty.Register(nameof(Input), typeof(string), typeof(UserControl2), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, InputChanged)); 11 private static void InputChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 12 { 13 if (d is UserControl2 userControl2) 14 userControl2.Output = userControl2.Converter?.Invoke((string)e.NewValue); 15 } 16 17 public string Output { get => (string)GetValue(OutputProperty); protected set => SetValue(OutputPropertyKey, value); } 18 private static readonly DependencyPropertyKey OutputPropertyKey = DependencyProperty.RegisterReadOnly(nameof(Output), typeof(string), typeof(UserControl2), new()); 19 public static readonly DependencyProperty OutputProperty = OutputPropertyKey.DependencyProperty; 20 21 public Func<string, string> Converter { get => (Func<string, string>)GetValue(ConverterProperty); set => SetValue(ConverterProperty, value); } 22 public static readonly DependencyProperty ConverterProperty = DependencyProperty.Register(nameof(Converter), typeof(Func<string, string>), typeof(UserControl2), new((Func<string, string>)(s => s))); 23 24 public UserControl2() => InitializeComponent(); 25 } 26}

cs:MainWindowViewModel.cs

1using Prism.Mvvm; 2using Prism.Regions; 3using Questions362091.Models; 4using Reactive.Bindings; 5using Reactive.Bindings.Extensions; 6using System; 7 8namespace Questions362091.ViewModels 9{ 10 public class MainWindowViewModel : BindableBase 11 { 12 public ReactiveProperty<string> Input { get; } 13 public ReadOnlyReactiveProperty<string> Output { get; } 14 public Func<string, string> Converter => model.Converter; 15 16 private readonly Model model; 17 18 public MainWindowViewModel(Model model, IRegionManager regionManager) 19 { 20 this.model = model; 21 Input = model.Input.ToReactivePropertyAsSynchronized(x => x.Value); 22 Output = model.Output.ToReadOnlyReactiveProperty(); 23 } 24 } 25}

cs:PrismUserControl1ViewModel.cs

1using Prism.Mvvm; 2using Questions362091.Models; 3using Reactive.Bindings; 4using Reactive.Bindings.Extensions; 5 6namespace Questions362091.ViewModels 7{ 8 public class PrismUserControl1ViewModel : BindableBase 9 { 10 public ReactiveProperty<string> Input { get; } 11 public ReadOnlyReactiveProperty<string> Output { get; } 12 13 public PrismUserControl1ViewModel(Model model) 14 { 15 Input = model.Input.ToReactivePropertyAsSynchronized(x => x.Value); 16 Output = model.Output.ToReadOnlyReactiveProperty(); 17 } 18 } 19}

cs:Model.cs

1using Reactive.Bindings; 2using System; 3using System.Reactive.Linq; 4 5namespace Questions362091.Models 6{ 7 public class Model 8 { 9 public ReactivePropertySlim<string> Input { get; } = new(); 10 public ReadOnlyReactivePropertySlim<string> Output { get; } 11 public Func<string, string> Converter { get; } = s => s?.ToUpper(); 12 13 public Model() => Output = Input.Select(Converter).ToReadOnlyReactivePropertySlim(); 14 } 15}

cs:App.xaml.cs

1using Prism.Ioc; 2using Prism.Regions; 3using Questions362091.Models; 4using Questions362091.Views; 5using System.Windows; 6 7namespace Questions362091 8{ 9 public partial class App 10 { 11 protected override Window CreateShell() => Container.Resolve<MainWindow>(); 12 protected override void OnInitialized() 13 { 14 base.OnInitialized(); 15 var regionManager = Container.Resolve<IRegionManager>(); 16 regionManager.RegisterViewWithRegion("ContentRegion", typeof(PrismUserControl1)); 17 } 18 protected override void RegisterTypes(IContainerRegistry containerRegistry) 19 { 20 containerRegistry.RegisterSingleton<Model>(); 21 containerRegistry.RegisterForNavigation<PrismUserControl1>(); 22 } 23 } 24}

投稿2021/09/30 10:10

編集2023/07/29 06:42
TN8001

総合スコア9326

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

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

ry188472

2021/10/01 07:36

RelativeSourceを使って、子の依存関係プロパティを親のVMにバインドできました。ありがとうございます。 ただ、なぜ「そんなのイヤすぎますよね?」となるのでしょうか(xamlでの記述が長くなるから?)。 UserControlは「Prism UserControl (WPF)」の項目テンプレートを使うと<UserControl>タグから始まってますね。プロジェクトはFull AppでなくBlank Appで作ったので、それで違うのかもしれません。
TN8001

2021/10/01 09:38 編集

> RelativeSourceを使って、子の依存関係プロパティを親のVMにバインドできました。ありがとうございます。 やりたいことができたのであれば特に言うこともないんですが、聞かれてしまったので私の考えを書きます(が、おそらく賛同いただけないんでしょう) > ただ、なぜ「そんなのイヤすぎますよね?」となるのでしょうか(xamlでの記述が長くなるから?)。 独立性が高いからUserControlに切り出してるんですよね?(もしかしてこの前提すら違う??) であれば子が親を気にする時点で間違っていると思うのですが(相互に依存が発生してしまう) 親にベタ書きしていたがごちゃついてきたので、一部をUserControlにするというのはよくあると思います。 ただAutoWire=trueでDependencyPropertyまで作ってあれこれするんじゃ元より余計ごちゃついていませんか? すっきり書けないということは、ViewかVMの切り出し範囲を間違っているんだと思います。 FullAppテンプレートは見ていただけました? [Prism.Templates/Wpf/FullApp at master · PrismLibrary/Prism.Templates](https://github.com/PrismLibrary/Prism.Templates/tree/master/Wpf/FullApp これなんかは確かにきれいに分かれていて(分かれすぎなぐらいですが^^;、なるほどなーと思う反面「で?」となってしまうのもあります(テンプレートなのでこれで正しいんですが^^; 公式サンプルで、もうちょっと具体的な例が総集編的にでもあればいいんですが。。。 [PrismLibrary/Prism-Samples-Wpf: Samples that demonstrate how to use various Prism features with WPF](https://github.com/PrismLibrary/Prism-Samples-Wpf
TN8001

2021/10/02 09:57

RelativeSourceのくだりは私が勘違いしていたかもしれません。 例を作っている最中に気が付きましたが、RelativeSourceを書くのはMainWindow.xamlなんですね? <views:PrismUserControl Input="{Binding DataContext.Input.Value, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" /> のような。 私はPrismUserControl.xamlの中に書くのかと思っていました。 で、「AutoWire=trueでDependencyPropertyまで作ってあれこれする」のを再現しようとしたんですが訳が分からな過ぎて諦めました^^; 例は「DependencyPropertyなんていらない」ってことを言いたいのですが(「dependencyproperty usercontrol」と検索して出てくるほとんどは無駄なことをしています)、「実はUserControlを複数置きたいのです」って言われちゃうのかな?^^; その場合はViewModelを複数持つだけなのですが、それも例が必要ですかねぇ。。。
ry188472

2021/10/04 00:48 編集

とりあえず当初のやりかったことはできたのでBAに選ばせていただきました。 TN8001さんが考えているのって、「UserControlのDataContext用に独自のクラスを定義して、親画面VMでそのクラスをnewしてUserControlにバインドする。そのクラスのpublicプロパティやメソッドでデータの受け渡し/加工をする。こうすればDependencyPropertyはいらない」ということであってます? >ViewかVMの切り出し範囲を間違っている これは自分でもありえると思います。利用者となる親画面の実装を減らすのを第一目的に切り出ししてるので、範囲は不適切になっててわかりにくくなっている部分はあるかもしれません。
TN8001

2021/10/04 02:17

> とりあえず当初のやりかったことはできたのでBAに選ばせていただきました。 BAには特にこだわりはないので、できた方法を簡単な例で回答していただけるとありがたいのですが。 もし私が盛大に勘違いをしているなら、どういうことだったのかを知りたいです(いまだにわかっていません) > ということであってます? 意味は同じ気がしますが、単にhihijijiさんの言っている > 文字列加工用のModelを別途作って、そのインスタンスを複数のVMで共有すれば っていうのをそのままやっているだけです。 Modelのステートを変化させれば、わざわざDependencyPropertyで教える必要もなくなるってことです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問