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

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

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

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

WPF

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

Q&A

解決済

1回答

21251閲覧

WPFの開発手法の配列変数を用いて値をBindingする方法が知りたい・

Shiokawa

総合スコア10

C#

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

WPF

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

0グッド

0クリップ

投稿2016/08/05 22:49

編集2016/08/06 04:00

###前提・実現したいこと
.NET4.0 c#で WPFの開発をしています。
ViewModelからViewへ配列変数を用いて値をBindingし表示したい。
View側はLoad時に動的に画面を作成している。

###発生している問題・エラーメッセージ
コーディング方法を教えてほしい。

エラーメッセージ
なし

###該当のソースコード
現在のソースはすべて変数名でBindingさせて表示させている。
以下

private string listName0; private string listName1; public string ListName0 { get { return listName0; } set { SetProperty(ref listName0, value, "ListName0"); } } public string ListName1 { get { return listName1; } set { SetProperty(ref listName1, value, "ListName1"); } } public virtual bool SetProperty<T>(ref T target, T value, string name) { if (target == null) { if (value == null) return false; } else { if (target.Equals(value)) return false; } target = value; RaisePropertyChanged(name); return true; }

最終的にINotifyPropertyChanged を継承したクラスでView側にBindingさせる。

protected virtual void RaisePropertyChanged(string name) { var h = PropertyChangedHandler; if (h != null) h(this, new PropertyChangedEventArgs(name)); }

View側ソース
loadイベントで実行するのコード

Binding textNamebinding = new Binding(); string name = "ListName" + (ikoumokuNo).ToString(); textNamebinding.Path = new PropertyPath(name); BindingOperations.SetBinding(labelns[iLabelNo], Label.ContentProperty, textNamebinding);

ikoumokuNoの変数でBindingする変数を特定させている。

###試したこと
listName[]配列でコーディングしてみたがBindingできませんでした。
多分コーディングの問題と思います。

###補足情報(言語/FW/ツール等のバージョンなど)
開発環境:Microsoft Visual Studio 2010
言語:c# WPF

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

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

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

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

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

flied_onion

2016/08/06 03:35

コードは ``` で括ってください。コードを選択して </> ボタンでも可能です。 あとタグがCになっていますが、C#に変更してください。読みやすい質問の方が回答は付きやすいと思います。
guest

回答1

0

ベストアンサー

ちょっとコードが断片的なので問題の特定はできてないですが、
サンプルとして以下の様に書くと単方向バインディングは可能です。
for文内の Bindingのコードは提示されたコードに合わせています。

  1. WPFアプリケーションを作成
  2. WindowにLabelを二つ置き、NameをLabel0, Label1とする。
  3. WindowのLoadイベントを作成し、コードを以下のようにする。

※ Window_LoadedとMyModelクラスの定義をしただけです。

cs

1 public partial class MainWindow : Window { 2 public MainWindow() { 3 InitializeComponent(); 4 } 5 6 private void Window_Loaded(object sender, RoutedEventArgs e) { 7 8 List<Label> labelns = new List<Label> { Label0, Label1 }; 9 for (var i = 0; i < labelns.Count; i++) { 10 var iKoumokuNo = i; 11 var iLabelNo = i; 12 Binding textNamebinding = new Binding(); 13 string name = "ListName" + (iKoumokuNo).ToString(); 14 textNamebinding.Path = new PropertyPath(name); 15 BindingOperations.SetBinding(labelns[iLabelNo], Label.ContentProperty, textNamebinding); 16 } 17 18 this.DataContext = new MyModel(); 19 } 20 21 public class MyModel { 22 private string value = "Hello 0"; 23 24 public string ListName0 { 25 get { return value; } 26 set { this.value = value; } 27 } 28 29 public string ListName1 { 30 get { return value; } 31 set { this.value = value; } 32 } 33 } 34 }

質問でわからなかったのは

  • 「最終的にINotifyPropertyChanged を継承したクラス」と最初のコード「private string listName0;」のクラス構成
  • 「listName[]配列」 とはなにか(その配列をどう宣言したのか)

ちなみに、簡単にINotifyPropertyChangedを実装してみました。

  • Windowsにはボタンを1つ追加する必要があります。
  • MyModelをnewするタイミングも変わっています。
  • var h = PropertyChangedHandler;は、var h = PropertyChanged;と変わっています。(

そのイベントに誰かが設定しているか判断するものなので)

  • Bind時に textNamebinding.UpdateSourceTrigger の指定が増えている事にも注意してください。
  • SetPropertyは簡単のためにジェネリックメソッドではなくしました。

cs

1 public partial class MainWindow : Window { 2 public MainWindow() { 3 InitializeComponent(); 4 } 5 6 private MyModel vm = new MyModel(); 7 8 private void Window_Loaded(object sender, RoutedEventArgs e) { 9 10 List<Label> labelns = new List<Label> { Label0, Label1 }; 11 for (var i = 0; i < labelns.Count; i++) { 12 Binding textNamebinding = new Binding(); 13 var iKoumokuNo = i; 14 var iLabelNo = i; 15 string name = "ListName" + (iKoumokuNo).ToString(); 16 textNamebinding.Path = new PropertyPath(name); 17 textNamebinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; 18 BindingOperations.SetBinding(labelns[iLabelNo], Label.ContentProperty, textNamebinding); 19 } 20 21 this.DataContext = vm; 22 } 23 24 private void Button_Click(object sender, RoutedEventArgs e) { 25 vm.ListName0 = "a"; 26 vm.ListName1 = "b"; 27 } 28 29 30 public class MyModel : INotifyPropertyChanged { 31 private string listName0 = "Hello 0"; 32 private string listName1 = "Hello 1"; 33 34 public string ListName0 { 35 get { return listName0; } 36 set { SetProperty(ref listName0, value, "ListName0"); } 37 } 38 39 public string ListName1 { 40 get { return listName1; } 41 set { SetProperty(ref listName1, value, "ListName1"); } 42 } 43 44 public virtual bool SetProperty(ref String target, String value, string name) { 45 if (target == null) { 46 if (value == null) 47 return false; 48 } 49 else { 50 if (target.Equals(value)) 51 return false; 52 } 53 target = value; 54 RaisePropertyChanged(name); 55 return true; 56 } 57 58 protected virtual void RaisePropertyChanged(string name) { 59 var h = PropertyChanged; 60 if (h != null) 61 h(this, new PropertyChangedEventArgs(name)); 62 } 63 64 public event PropertyChangedEventHandler PropertyChanged; 65 }

追記

上記のListName0 と ListName1 を別個のプロパティではなく、配列 ListName[] として持たせたいということですね。

この場合、ListNameのsetterの処理によってRaisePropertyChangedが呼ばれても、そもそも 配列ListNameをまるごと更新することはないので、そのsetterは使わることがなく、RaisePropertyChangedが発行されることはありません。

  • MyModel のプロパティを配列にした場合

(MainWindowの変更は省略。Bindingに設定するnameは "ListName[" + iKoumokuNo + "]"; にすれば良い)

cs

1 private string[] listName = {"Hello 0", "Hello 1"}; 2 public string[] ListName { 3 get { return listName; } 4 set { SetProperty(ref listName, value, "ListName"); } 5 }

このセッターは要素への代入では呼ばれない。

cs

1 private void Button_Click(object sender, RoutedEventArgs e) { 2 vm.ListName[0] = "a"; // これではListNameのセッターは呼ばれない

一番単純な解決策は、要素へのSetメソッドを用意してPropertyChangedを呼ぶことです。

MyModel

public void SetListName(int index, string value) { listName[index] = value; var h = PropertyChanged; if (h != null) h(this, new PropertyChangedEventArgs("ListName")); }

MainWindow#Button_Click

private void Button_Click(object sender, RoutedEventArgs e) { vm.SetListName(0, "a"); vm.SetListName(1, "b"); }

ただ使い方が変わるので扱いにくいですね。
WPFには System.Collections.ObjectModel.ObservableCollection<T> があるのでこれを利用することができます。

cs

1// using System.Collections.ObjectModel; を追加する。 2 public partial class MainWindow : Window { 3 public MainWindow() { 4 InitializeComponent(); 5 } 6 7 private MyModel vm = new MyModel(); 8 private void Window_Loaded(object sender, RoutedEventArgs e) { 9 10 List<Label> labelns = new List<Label> { Label0, Label1 }; 11 for (var i = 0; i < labelns.Count; i++) { 12 Binding textNamebinding = new Binding(); 13 var iKoumokuNo = i; var iLabelNo = i; 14 string name = "ListName[" + iKoumokuNo + "]"; 15 textNamebinding.Path = new PropertyPath(name); 16 textNamebinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; 17 BindingOperations.SetBinding(labelns[iLabelNo], Label.ContentProperty, textNamebinding); 18 } 19 20 this.DataContext = vm; 21 } 22 23 private void Button_Click(object sender, RoutedEventArgs e) { 24 vm.ListName[0] = "a"; 25 vm.ListName[1] = "b"; 26 } 27 28 29 public class MyModel : INotifyPropertyChanged { 30 private ObservableCollection<string> listName = new ObservableCollection<string>(); 31 32 public MyModel() { 33 listName.Add("Hello 0"); 34 listName.Add("Hello 1"); 35 36 listName.CollectionChanged += listName_CollectionChanged; 37 } 38 39 40 public ObservableCollection<string> ListName { 41 get { 42 return listName; 43 } 44 set { 45 SetProperty(ref listName, value, "ListName"); 46 } 47 } 48 49 void listName_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { 50 // 項目の変更やリスト全体の変更時に発生するイベント 51 var h = PropertyChanged; 52 if (h != null) 53 h(this, new PropertyChangedEventArgs("ListName")); 54 55 } 56 57 public virtual bool SetProperty(ref ObservableCollection<string> target, ObservableCollection<string> value, string name) { 58 // サンプルでは使ってないのでnullチェック省略省略 59 target = value; 60 RaisePropertyChanged(name); 61 return true; 62 } 63 64 protected virtual void RaisePropertyChanged(string name) { 65 var h = PropertyChanged; 66 if (h != null) 67 h(this, new PropertyChangedEventArgs(name)); 68 } 69 70 public event PropertyChangedEventHandler PropertyChanged; 71 } 72 }

もっと良いやり方もあるかもしれませんが、これで目的は達成できるかと思います。

投稿2016/08/06 04:27

編集2016/08/06 08:42
flied_onion

総合スコア2604

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

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

Shiokawa

2016/08/06 05:22

質問がわかりにくかったと思います。 特定した文字列毎にBindingする方法はこの方法で実現できました。 MyModel側に文字列の配列に要素分の文字データが存在し、その配列の中の文字列とBindingするコーディングがうまくいきませんでした。 文字列配列名をListName[x]とし、x個の文字列がセットされていて、その中身の文字列は逐次更新されていきます。 ビュー側は string name = "ListName" + (iKoumokuNo).ToString(); は string name = "ListName[" + (iKoumokuNo).ToString() +"]"; で良いと思っています。 INotifyPropertyChanged側のコーディングがわかりましたらよろしくお願いします。 グーグル検索して情報検索しましたが、はまった話がでてきますがどう直しかがいまだにわかっていません。
flied_onion

2016/08/06 08:21

モデルに配列プロパティを持たせたいという事でしたか。回答に追記します。
Shiokawa

2016/08/06 13:13

回答ありがとうございます。 「要素へのSetメソッドを用意してPropertyChanged」の方法でまず検証してみます。 その後 「System.Collections.ObjectModel.ObservableCollection<T>」の利用した方法でも 検証致します。 また後日コメントさせて頂きます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問