🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
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回答

1958閲覧

WPFで二つのクラスの内容を一つのDataContextで表示

Q10

総合スコア12

C#

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

XAML

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

WPF

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

1グッド

0クリップ

投稿2020/01/03 14:50

前提・実現したいこと

C#のWPFで二つのクラス(PrintTextとPrintFilename)の内容を一つのDataContextで両方とも表示したいです。具体的な動きとしては、実行直後にOKという文字を表示し、Dataボタンをクリックして選択したファイル名を表示したいです。

発生している問題・エラーメッセージ

二つのクラスを一つのViewModelにまとめてDataContextに設定しましたが、何も表示されません。

該当のソースコード

MainWindow.xaml

<Window.DataContext> <view:MainWindowViewModel/> </Window.DataContext> <Grid> <StackPanel Orientation="Vertical"> <Button x:Name="Data_Button" Content="Data" Click="Data_Button_Click" Margin="5" Width="75"/> <TextBlock x:Name="TextBlock_Result" Text="{Binding Path=printText.Result}"/> <TextBlock x:Name="TextBlock_Filename" Text="{Binding filename.Filename}"/> </StackPanel> </Grid>

MainWindow.xaml.cs

public partial class MainWindow : Window { OpenFileDialog oFileDialog; public MainWindow() { InitializeComponent(); var printText = new PrintText(); printText.Result = "OK"; var viewModel = new MainWindowViewModel(); DataContext = viewModel; MessageBox.Show(printText.Result); } private void Data_Button_Click(object sender, RoutedEventArgs e) { oFileDialog = new OpenFileDialog { Filter = "Excel (*.xlsx)|*.xlsx" }; if (oFileDialog.ShowDialog() == false) { return; } var filename = new PrintFilename(); filename.Filename = oFileDialog.FileName; MessageBox.Show(filename.Filename); } }

Model.PrintFilename.cs

class PrintFilename { public string Filename { get; set; } }

Model.PrintText.cs

class PrintText { public string Result { get; set; } }

ViewModel.MainWindowViewModel.cs

class MainWindowViewModel { public PrintText PrintText { get; private set; } public PrintFilename PrintFilename { get; private set; } public MainWindowViewModel() { PrintText = new PrintText(); PrintFilename = new PrintFilename(); } }

試したこと

MessageBoxで値がちゃんと入っていることは確認しました。

補足情報(FW/ツールのバージョンなど)

Visual Studio 2019 Community, C# 7.3
他にも必要な情報があれば補足します。

TN8001👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

1点目

xamlの間違いを直します。

xml

1<TextBlock x:Name="TextBlock_Result" Text="{Binding Path=printText.Result}"/> 2<TextBlock x:Name="TextBlock_Filename" Text="{Binding filename.Filename}"/>

xml

1<TextBlock x:Name="TextBlock_Result" Text="{Binding PrintText.Result}"/> 2<TextBlock x:Name="TextBlock_Filename" Text="{Binding PrintFilename.Filename}"/>

バインディングのエラーは出力ウィンドウに出るだけで、特に止まったりしないので気が付きにくいです。

System.Windows.Data Error: 40 : BindingExpression path error: 'printText' property not found on 'object' ''MainWindowViewModel' (HashCode=64554036)'. BindingExpression:Path=printText.Result; DataItem='MainWindowViewModel' (HashCode=64554036); target element is 'TextBlock' (Name='TextBlock_Result'); target property is 'Text' (type 'String') System.Windows.Data Error: 40 : BindingExpression path error: 'filename' property not found on 'object' ''MainWindowViewModel' (HashCode=64554036)'. BindingExpression:Path=filename.Filename; DataItem='MainWindowViewModel' (HashCode=64554036); target element is 'TextBlock' (Name='TextBlock_Filename'); target property is 'Text' (type 'String')

のようなエラーが出ているはずです。

2点目

バインディングを使って表示したいのでしょうから、変更通知(INotifyPropertyChanged)の実装が必要になります(ViewModelBase・BindableBase・Observable等名前はいろいろだったりしますが、中身は大体同じで定型コードです)

MainWindowViewModelPrintTextPrintFilenameを丸ごと入れ替えるような想定であればこんな感じになります。

cs

1using System.ComponentModel; 2using System.Runtime.CompilerServices; 3using System.Windows; 4using Microsoft.Win32; 5 6namespace Questions233114 7{ 8 class PrintFilename 9 { 10 public string Filename { get; set; } 11 } 12 13 class PrintText 14 { 15 public string Result { get; set; } 16 } 17 18 class MainWindowViewModel : Observable 19 { 20 public PrintText PrintText { get => _PrintText; set => Set(ref _PrintText, value); } 21 private PrintText _PrintText; 22 public PrintFilename PrintFilename { get => _PrintFilename; set => Set(ref _PrintFilename, value); } 23 private PrintFilename _PrintFilename; 24 25 public MainWindowViewModel() 26 { 27 PrintText = new PrintText(); 28 PrintFilename = new PrintFilename(); 29 } 30 } 31 32 abstract class Observable : INotifyPropertyChanged 33 { 34 public event PropertyChangedEventHandler PropertyChanged; 35 protected bool Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) 36 { 37 if(Equals(storage, value)) return false; 38 39 storage = value; 40 OnPropertyChanged(propertyName); 41 return true; 42 } 43 protected void OnPropertyChanged([CallerMemberName] string propertyName = null) 44 => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 45 } 46 47 public partial class MainWindow : Window 48 { 49 private readonly MainWindowViewModel vm; 50 public MainWindow() 51 { 52 InitializeComponent(); 53 54 vm = new MainWindowViewModel(); 55 DataContext = vm; 56 57 var printText = new PrintText { Result = "OK" }; 58 vm.PrintText = printText; 59 60 MessageBox.Show(vm.PrintText.Result); 61 } 62 63 private void Data_Button_Click(object sender, RoutedEventArgs e) 64 { 65 var oFileDialog = new OpenFileDialog { Filter = "Excel (*.xlsx)|*.xlsx" }; 66 67 if(oFileDialog.ShowDialog() == false) 68 { 69 return; 70 } 71 72 var filename = new PrintFilename { Filename = oFileDialog.FileName }; 73 vm.PrintFilename = filename; 74 75 MessageBox.Show(vm.PrintFilename.Filename); 76 } 77 } 78}

Filename・Resultを直接変えるのであればこんな感じになります。

cs

1using System.ComponentModel; 2using System.Runtime.CompilerServices; 3using System.Windows; 4using Microsoft.Win32; 5 6namespace Questions233114 7{ 8 class PrintFilename : Observable 9 { 10 public string Filename { get => _Filename; set => Set(ref _Filename, value); } 11 private string _Filename; 12 } 13 14 class PrintText : Observable 15 { 16 public string Result { get => _Result; set => Set(ref _Result, value); } 17 private string _Result; 18 } 19 20 class MainWindowViewModel 21 { 22 public PrintText PrintText { get; private set; } 23 public PrintFilename PrintFilename { get; private set; } 24 25 public MainWindowViewModel() 26 { 27 PrintText = new PrintText(); 28 PrintFilename = new PrintFilename(); 29 } 30 } 31 32 abstract class Observable : INotifyPropertyChanged 33 { 34 public event PropertyChangedEventHandler PropertyChanged; 35 protected bool Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) 36 { 37 if(Equals(storage, value)) return false; 38 39 storage = value; 40 OnPropertyChanged(propertyName); 41 return true; 42 } 43 protected void OnPropertyChanged([CallerMemberName] string propertyName = null) 44 => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 45 } 46 47 public partial class MainWindow : Window 48 { 49 private readonly MainWindowViewModel vm; 50 51 public MainWindow() 52 { 53 InitializeComponent(); 54 vm = new MainWindowViewModel(); 55 DataContext = vm; 56 57 vm.PrintText.Result = "OK"; 58 59 MessageBox.Show(vm.PrintText.Result); 60 } 61 62 private void Data_Button_Click(object sender, RoutedEventArgs e) 63 { 64 var oFileDialog = new OpenFileDialog { Filter = "Excel (*.xlsx)|*.xlsx" }; 65 66 if(oFileDialog.ShowDialog() == false) 67 { 68 return; 69 } 70 71 vm.PrintFilename.Filename = oFileDialog.FileName; 72 73 MessageBox.Show(vm.PrintFilename.Filename); 74 } 75 } 76}

どちらのパターンもあり得ますが、どちらがいいかはこれだけだとちょっと判断できません。

投稿2020/01/03 16:04

編集2023/07/17 13:27
TN8001

総合スコア9855

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

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

Q10

2020/01/04 00:01

ベストアンサーを差し上げます。 まず、XAMLのBinding Pathですが、インスタンス名じゃなくてクラス名を書くんですね。 次回から出力ウィンドウを確認します。 次にC#の方ですが、変更通知が必要なんですね。 正直、自分のコードはあと一歩で完成かと思っていましたが、まだまだ遠かったです。 ①「PrintText・PrintFilenameを丸ごと入れ替え」と②「Filename・Resultを直接変える」で比較してみました。今回は変更するプロパティが一つしかなかったのでコーディングは②が楽ですが、他にも変更するプロパティがあって、それらすべてを変更するなら①なんでしょうね。TN8001さんのコードを参考にしていろいろ組んでみます。C#は奥が深いですね。ありがとうございました!
TN8001

2020/01/04 05:22

> クラス名を書くんですね。 いえ インスタンス名(変数名)です 同名なので分かりにくかったですが class MainWindowViewModel { public PrintText Text { get; set; } } class PrintText { public string Result { get; set; } } だったとしたら {Binding Path=Text.Result}か {Binding Text.Result}です DataContext(MainWindowViewModel)の Text(PrintText)の Result(string) あとプロパティでないとバインディングできないのを忘れてよくハマりますw({ get; set; }の書き忘れ) ①②の使い分けですが 最近はModelにINotifyPropertyChangedの実装を許して②の上で、状況によっては①②両方みたいなことが多いかもしれません(値の変更のコードを書いてみて楽なほうでいいと思いますw) お約束?のコードが多く面倒に思うかもしれませんが、バインディングで表示が自動的に追従するのはそれを上回る便利さです 最低限のコードを作ってプロジェクトテンプレートにしておくといいと思います https://docs.microsoft.com/ja-jp/visualstudio/ide/how-to-create-project-templates?view=vs-2019 ReactiveProperty https://blog.okazuki.jp/entry/2018/03/24/135318 を使うのもいいですね
Q10

2020/01/05 13:44

なるほど、インスタンス名でしたか。元々私がそれらを同名にしていたのがいけなかったですね。 { get; set; }も忘れないようにします。 INotifyPropertyChangedは定型文なのかもしれないですが、私にはスラスラと書ける自信が無いです。プロジェクトテンプレートは「毎回この文書いているな」と思い始めたら挑戦してみます。ReactivePropertyだと短く書けそうですね。 また近いうちに質問すると思います。 ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問