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

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

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

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

Visual Studio

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

Windows

Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

WPF

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

Q&A

解決済

2回答

3010閲覧

ReactivePropertyで値が更新されない

arw.tyx-out_mz

総合スコア27

C#

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

Visual Studio

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

Windows

Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

WPF

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

0グッド

0クリップ

投稿2019/06/13 12:35

編集2019/06/14 12:55

概要

ReactivePropertyを使ってWPFアプリを作成しています.
ComboBoxを使うところでハマってしまったので,アドバイスお願いします.

やりたいこと

ある自作クラスのReactivePropertyAと,そのクラスのプロパティのReactivePropertyBを作っています.
Bにおいて,A.Valueと,そのA.Value.Propertyの両方を監視したいです.
自作クラスのReactivePropertyを書換えたときに,連動してプロパティのReactivePropertyも書き換わってほしいのですが,ずっとnullのままです.

ソースコード

PersonList(シングルトン)

C#

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using System.Threading.Tasks; 6using Prism.Mvvm; 7using Reactive.Bindings; 8 9namespace ForTeratail.Models 10{ 11 public sealed class PersonList : BindableBase 12 { 13 static readonly PersonList m_instance = new PersonList(); 14 15 public static PersonList Instance 16 { 17 get { return m_instance; } 18 } 19 20 int count; 21 22 private PersonList() 23 { 24 count = 0; 25 PersonCollection = new ReactiveCollection<Person>(); 26 Add(); 27 } 28 29 ReactiveCollection<Person> m_collection; 30 public ReactiveCollection<Person> PersonCollection 31 { 32 get { return m_collection; } 33 set { SetProperty(ref m_collection, value); } 34 } 35 36 Person m_current; 37 public Person CurrentPerson 38 { 39 get { return m_current; } 40 set { SetProperty(ref m_current, value); } 41 } 42 43 public void Add() 44 { 45 Person p = new Person(count); 46 PersonCollection.Add(p); 47 CurrentPerson = PersonCollection[PersonCollection.Count - 1]; 48 count++; 49 } 50 } 51}

Person

C#

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using System.Threading.Tasks; 6using Prism.Mvvm; 7using Reactive.Bindings; 8 9namespace ForTeratail.Models 10{ 11 public class Person : BindableBase 12 { 13 public int Id { get; set; } 14 15 string m_name; 16 public string Name 17 { 18 get { return m_name; } 19 set { SetProperty(ref m_name, value); } 20 } 21 22 int m_age; 23 public int Age 24 { 25 get { return m_age; } 26 set { SetProperty(ref m_age, value); } 27 } 28 29 public Person(int id) 30 { 31 Id = id; 32 } 33 } 34}

MyViewModel(ComboBoxがあるViewのVM)

C#

1using System.Reactive.Linq; 2using Prism.Mvvm; 3using Reactive.Bindings; 4using Reactive.Bindings.Extensions; 5using ForTeratail.Models; 6 7namespace ForTeratail.ViewModels 8{ 9 public class MainWindowViewModel : BindableBase 10 { 11 private string _title = "Prism Application"; 12 public string Title 13 { 14 get { return _title; } 15 set { SetProperty(ref _title, value); } 16 } 17 18 public ReactiveCollection<Person> Collection { get; } 19 public ReactiveProperty<Person> CurrentPerson { get; } 20 public ReactiveProperty<string> HereName { get; } 21 public ReactiveProperty<int> HereAge { get; } 22 public ReactiveProperty<string> Introduction { get; } 23 24 public ReactiveCommand<string> UIInteractiveNameCommand { get; } 25 public ReactiveCommand<int> UIInteractiveAgeCommand { get; } 26 public ReactiveCommand AddCommand { get; } 27 28 public MainWindowViewModel() 29 { 30 Collection = PersonList.Instance.PersonCollection; 31 CurrentPerson = PersonList.Instance.ToReactivePropertyAsSynchronized(x => x.CurrentPerson); 32 33 HereName = CurrentPerson.Select(x => x.Name).ToReactiveProperty(); 34 HereAge = CurrentPerson.Select(x => x.Age).ToReactiveProperty(); 35 36 Introduction = HereName.Where(x => x != null).Select(x => "I am " + x).ToReactiveProperty(); 37 38 UIInteractiveNameCommand = new ReactiveCommand<string>().WithSubscribe(x => 39 { 40 CurrentPerson.Value.Name = x; 41 }); 42 43 UIInteractiveAgeCommand = new ReactiveCommand<int>().WithSubscribe(x => 44 { 45 CurrentPerson.Value.Age = x; 46 }); 47 48 AddCommand = new ReactiveCommand(); 49 AddCommand.Subscribe(PersonList.Instance.Add); 50 } 51 } 52}

MyView (ComboBoxがあるView)

C#

1<Window x:Class="ForTeratail.Views.MainWindow" 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 Title="{Binding Title}" Height="350" Width="525"> 7 <Grid> 8 <Grid.RowDefinitions> 9 <RowDefinition/> 10 <RowDefinition/> 11 <RowDefinition/> 12 <RowDefinition/> 13 </Grid.RowDefinitions> 14 <Grid.ColumnDefinitions> 15 <ColumnDefinition/> 16 <ColumnDefinition/> 17 </Grid.ColumnDefinitions> 18 <ComboBox Grid.Column="0" Margin="10 20" 19 ItemsSource="{Binding Collection}" 20 SelectedValue="{Binding CurrentPerson.Value}" 21 DisplayMemberPath="Id" /> 22 <Button Grid.Column="1" Content="新規追加" Command="{Binding AddCommand}" Margin="20" /> 23 24 <TextBox Grid.Row="1" Grid.Column="0" Margin="10" Height="30" FontSize="18" 25 TextAlignment="Center" VerticalContentAlignment="Center" 26 x:Name="NameBlock"/> 27 <Button Grid.Row="1" Grid.Column="1" Content="登録" Margin="20" 28 Command="{Binding UIInteractiveNameCommand}" 29 CommandParameter="{Binding Text, ElementName=NameBlock}" /> 30 31 <TextBox Grid.Row="2" Grid.Column="0" Margin="10" Height="30" FontSize="18" 32 TextAlignment="Center" VerticalContentAlignment="Center" 33 x:Name="AgeBlock"/> 34 <Button Grid.Row="2" Grid.Column="1" Content="登録" Margin="20" 35 Command="{Binding UIInteractiveAgeCommand}" 36 CommandParameter="{Binding Text, ElementName=AgeBlock}" /> 37 38 <Label Grid.Row="3" Grid.ColumnSpan="2" Content="{Binding Introduction.Value}" /> 39 </Grid> 40</Window>

問題

新規追加ボタンを押すとPersonListクラスのPersonCollectionに空のPersonオブジェクトが追加されます.
それがComboBoxに紐付いています.
また,登録ボタンを押すとテキストボックスにあるテキストがCommandの引数として渡され,現在選択されているPersonオブジェクトのプロパティとしてセットされます.
ここはあくまで発生している問題を再現する簡易的な例なので,TextBoxの内容をReactivePropertyとBindingするということは考えないでください.
あくまで,コマンド経由で現在選択されているオブジェクトのプロパティを変更したときに,それを監視しているReactivePropertyの値が変更されないという問題です.

上記クラスのCurrentPersonNameプロパティやAgeプロパティは適切に変更されますが,HereNameHereAgenullのままで書き換わってくれません.
しかし,ComboBoxで選択しているオブジェクトを変更し(例えばId=0のオブジェクトからId=1のオブジェクトへ),その後同じオブジェクト(Id=0)に戻ってくると,プロパティの値がちゃんとHere〇〇に設定されています.
CurrentPersonが保持しているPersonオブジェクト自体の変更と,そのPersonオブジェクト内のプロパティ両方の変更を検知するためにはどのように修正すればいいでしょうか?

バージョン情報

Visual Studio 2017 Community
.NET Framework 4.6.1
Reactive Property v5.5.1

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

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

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

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

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

Zuishin

2019/06/13 13:00

Instance プロパティが無かったり、CurrentPerson が初期化されてなかったりで、こちらで補わなければならないので違うソースになります。
arw.tyx-out_mz

2019/06/13 13:29

失礼しました.足りないメソッドを追加しました.
Zuishin

2019/06/13 13:57

CurrentPerson の Name プロパティはどこにありますか?
Zuishin

2019/06/13 13:58

MyViewModel のローカル変数の話です。
Zuishin

2019/06/13 14:02

失礼しました。ローカル変数ではなく MyViewModel のプロパティでした。
arw.tyx-out_mz

2019/06/13 14:05

失礼しました.Valueが抜けておりました.
Zuishin

2019/06/14 11:30

やっぱりコンパイルできないし、空の Person が追加されるだけなんですが、やりたいこととしては、ボタンを押せばテキストボックスに入力された値から Person が作られて PersonList に入り、それがバインドされたコンボボックスを選択するとテキストボックスにそのデータが入るということでいいんでしょうか?
arw.tyx-out_mz

2019/06/14 12:56

すみません.ちゃんとコンパイルが通ることも確認して,すべてのソースコードを添付したので確認お願いします.
arw.tyx-out_mz

2019/06/14 12:56

度々不備があり申し訳ありません...
Zuishin

2019/06/15 02:47

やりたいことは HereName を実装することですか? それとも私が書いたことですか?
arw.tyx-out_mz

2019/06/15 05:25

やりたいことは,HereNameを実装し,Introductionを作成することです
Zuishin

2019/06/15 05:39

「このように動くものが作りたい」というのではなく「ある特殊な実装ができないか研究したい」ということですか?
arw.tyx-out_mz

2019/06/15 05:52

すみませんやりたいことは,「Introductionを作成すること」が正しいです. HereNameとかは別に作らなくてもよく,comboboxによるPersonの変更と,PersonオブジェクトのNameプロパティの変更に応じてIntroductionを表示したいです. この例がそのまま自分の作るアプリケーションではないですが,この実装がそのまま作っているアプリケーションに直結しているという感じです.
arw.tyx-out_mz

2019/06/15 08:31

ありがとうございます.参考にしてみます. ただ,ReactivePropertyでやる新しいアイディアが浮かび,それで実際にIntroductionの中身を変えることに成功しました. しかしUIの値が切り替わらないという問題が今度は発生しているので,その問題を別の質問で投稿したので,よければご確認お願いします. https://teratail.com/questions/195092?modal=q-comp
guest

回答2

0

やりたいことを実現するには、CurrentPerson.Value.Name?の更新通知をリスニングする必要があります。
一般的にはIWeakEventListenerでリッスンしますが、Prismには使いやすい実装がないのでかなり面倒です。
ですから多くの人はVMに新たにHereNameプロパティを作ったりしません。

投稿2019/06/15 02:40

編集2019/06/15 02:47
hihijiji

総合スコア4150

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

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

arw.tyx-out_mz

2019/06/15 05:31

だとすると,Introductionは直接CurrentPerson.Value.Nameを監視するように書くのですか? Introduction = CurrentPerson.Value.ObserveProperty(x => x.Name).Where(x => x != null).Select(x => "I am " + x).ToReactiveProperty(); Introduction = CurrentPerson.Select(x => x.Name).Where(x => x.Name).Select(x => "I am "+ x).ToReactiveProperty(); の2通りを試したのですが,前者はcomboboxによるCurrentPersonの変更を検知できず(Id = 0からId = 1)のPersonに変更された),Introductionの中身が変更されません. 後者は,UIInteractiveNameCommandでCurrentPersonのNameプロパティを変更してもその変更が検知できておらず,Introductionの中身がnullのままです.しかし,comboBoxで他のPerson(Id = 1)に変更したあと,元のPerson(Id = 0)に戻ってくると,Introductionの中身が設定されます. これはどのように解決すればいいですか?
hihijiji

2019/06/15 05:48

CurrentPersonや中のNameなどHereNameを取得するために必要な変更の変更通知をすべてリスニングして それらの変更を検出したらHereNameを変更するのを全て自分で実装する必要があります。 とても面倒なのでふつうはやりませんし私もやりません。 HereNameが必要ないように作り変えるのが現実的です。
guest

0

ベストアンサー

これでどうでしょうか?

C#

1UIInteractiveCommand = new ReactiveCommand<string>().WithSubscribe(x => 2{ 3 CurrentPerson.Value = new Person() { Name = x }; 4 Debug.WriteLine(HereName); 5 Debug.WriteLine(PersonList.Instance.CurrentPerson.Name); 6}); 7

投稿2019/06/13 14:24

Zuishin

総合スコア28656

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

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

arw.tyx-out_mz

2019/06/13 14:55

それでHereNameの値も変わりましたが,あくまでCurrentPersonはPersonList.Instance.PersonCollectionの中の1つの要素である必要があるため,newしたものをCurrentPersonに直接代入という手段は取れないです.....
Zuishin

2019/06/13 15:01

だとしたら CurrentPerson のセッター仕様がおかしいのでは? ここに代入されたもので要素を書き換えるか、そもそも代入できないようにしなければいけません。
arw.tyx-out_mz

2019/06/14 02:38

やりたいことが明確に伝わっていなかったかもしれません..... 投稿を書換えたので,もしよろしければ確認していただけると嬉しいです. やりたいこととしては,ComboBoxによるObject自体の変更と,Objectのプロパティの変更の両方を検知したいということがやりたいです.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問