概要
ReactiveProperty
を使ってWPFアプリを作成しています.
ComboBoxを使うところでハマってしまったので,アドバイスお願いします.
やりたいこと
ある自作クラスのReactiveProperty
Aと,そのクラスのプロパティのReactiveProperty
Bを作っています.
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
の値が変更されないという問題です.
上記クラスのCurrentPerson
のName
プロパティやAge
プロパティは適切に変更されますが,HereName
やHereAge
がnull
のままで書き換わってくれません.
しかし,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

回答2件
あなたの回答
tips
プレビュー