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

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

新規登録して質問してみよう
ただいま回答率
85.35%
ReactiveX

ReactiveX(Rx、Reactive Extensions)は、リアクティブプログラミングが可能なライブラリ。Java/Android用のRxJava、JavaScript用のRxJSなどさまざまな言語向けに実装されています。

C#

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

WPF

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

Q&A

解決済

2回答

13080閲覧

ReactiveProperty<List<ReactiveProperty>>で内部のReactivePropertyをまとめたい

fox1

総合スコア7

ReactiveX

ReactiveX(Rx、Reactive Extensions)は、リアクティブプログラミングが可能なライブラリ。Java/Android用のRxJava、JavaScript用のRxJSなどさまざまな言語向けに実装されています。

C#

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

WPF

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

0グッド

1クリップ

投稿2018/02/08 03:01

編集2018/02/13 01:10

前提・実現したいこと

ReactivePropertyを使用して複数のチェックボックスのうちどれかがOnのときにTrueになるReactiveProperty<bool>と、
実行できるReactiveCommandを作成したいです。

該当のソースコード

CSharp

1static class Model{ 2 public static ReactiveProperty<List<Check>> BaseCheckItems {get;} 3 = new ReactiveProperty<List<Check>>( new List<Check>(){ new Check(), new Check() } ); 4} 5 6class ViewModel { 7 public ViewModel() { 8 this.CheckItems = Model.BaseCheckItems.ToReactiveProperty(); 9 this.AnyChecked = this.CheckItems.Value 10 .Select( x => x.IsChecked ) 11 .CombineLatest( x => x.Any( z => z ) ) 12 .ToReactiveProperty(); 13 14 this.ButtonCommand = this.AnyChecked.ToReactiveCommand(); 15 } 16 public ReactiveProperty<List<Check>> CheckItems {get;} 17 public ReactiveProperty<bool> AnyChecked {get;} 18 public ReactiveCommand ButtonCommand {get;} 19} 20 21public class Check { 22 public ReactiveProperty<bool> IsChecked {get;} = new ReactiveProperty<bool>(); 23}

Xml

1// 細かい内容は略します 2<Window DataContext=(ViewModel)> 3 <StackPanel> 4 <ItemsControl ItemsSource="{Binding CheckItems.Value}"> 5 <ItemsControl.ItemTemplate> 6 <DataTemplate> 7 <CheckBox IsChecked="{Binding IsChecked.Value}"> 8 </DataTemplate> 9 </ItemsControl.ItemTemplate> 10 11 <Button Command="{Binding ButtonCommand}"/> 12 </StackPanel> 13</Window>

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

this.AnyChecked = this.CheckItems.Value としているため、以下の処理を行うとAnyCheckedの通知?が来なくなってしまいます。

CSharp

1Model.BaseCheckItems.Value = new List<Check>(){ new Check() };

this.CheckItems.Valueを変更してもAnyCheckedに通知が来るようにはどのように書いたら良いでしょうか?

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

VisualStudio2017 C# WPF
ReactiveProperty 4.1.1

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

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

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

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

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

guest

回答2

0

ベストアンサー

ちょっと長くなりますが、こんな感じとかはどうですか?

cs

1using Reactive.Bindings; 2using Reactive.Bindings.Extensions; 3using System; 4using System.ComponentModel; 5using System.Diagnostics; 6using System.Linq; 7using System.Reactive.Linq; 8using System.Windows; 9 10namespace WpfApp1 11{ 12 /// <summary> 13 /// MainWindow.xaml の相互作用ロジック 14 /// </summary> 15 public partial class MainWindow : Window 16 { 17 public MainWindow() 18 { 19 InitializeComponent(); 20 } 21 } 22 23 public class MainWindowViewModel : INotifyPropertyChanged 24 { 25 private readonly SampleModel _model = new SampleModel(); 26 27 public event PropertyChangedEventHandler PropertyChanged; 28 29 public ReactiveCommand ReplaceItemsCommand { get; } 30 public ReadOnlyReactiveProperty<bool> AnyChecked { get; } 31 public ReactiveCommand ButtonCommand { get; } 32 33 public ReadOnlyReactiveProperty<CheckViewModel[]> Items { get; } 34 35 public MainWindowViewModel() 36 { 37 ReplaceItemsCommand = new ReactiveCommand() 38 .WithSubscribe(() => _model.ReplaceItems()); 39 40 AnyChecked = _model.Items 41 .SelectMany(x => x.Select(y => y.IsChecked).CombineLatest(y => y.Any(z => z))) 42 .ToReadOnlyReactiveProperty(); 43 44 ButtonCommand = AnyChecked.ToReactiveCommand() 45 .WithSubscribe(() => Debug.WriteLine("Executed")); 46 47 Items = _model.Items 48 .Select(x => x.Select(y => new CheckViewModel(y)).ToArray()) 49 .ToReadOnlyReactiveProperty(); 50 } 51 } 52 53 public class CheckViewModel : INotifyPropertyChanged 54 { 55 public event PropertyChangedEventHandler PropertyChanged; 56 57 public ReactiveProperty<bool> IsChecked { get; } 58 59 public CheckViewModel(Check check) 60 { 61 IsChecked = check.IsChecked 62 .ToReactivePropertyAsSynchronized(x => x.Value); 63 } 64 } 65 66 public class Check 67 { 68 public ReactiveProperty<bool> IsChecked { get; } = new ReactiveProperty<bool>(); 69 } 70 71 public class SampleModel 72 { 73 public ReactiveProperty<Check[]> Items { get; } = new ReactiveProperty<Check[]>(); 74 75 public SampleModel() 76 { 77 ReplaceItems(); 78 } 79 80 public void ReplaceItems() 81 { 82 var r = new Random(); 83 Items.Value = Enumerable.Range(0, r.Next(10) + 1) 84 .Select(x => new Check()) 85 .ToArray(); 86 } 87 } 88}

xml

1<Window x:Class="WpfApp1.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfApp1" 7 mc:Ignorable="d" 8 Title="MainWindow" 9 Height="350" 10 Width="525"> 11 <Window.DataContext> 12 <local:MainWindowViewModel /> 13 </Window.DataContext> 14 <Grid> 15 <Grid.RowDefinitions> 16 <RowDefinition Height="Auto" /> 17 <RowDefinition Height="Auto" /> 18 <RowDefinition /> 19 </Grid.RowDefinitions> 20 21 <Button Content="Replace" 22 Command="{Binding ReplaceItemsCommand}" /> 23 <Button Content="Button" 24 Command="{Binding ButtonCommand}" 25 Grid.Row="1" /> 26 <ListBox ItemsSource="{Binding Items.Value}" 27 Grid.Row="2"> 28 <ListBox.ItemTemplate> 29 <DataTemplate> 30 <CheckBox IsChecked="{Binding IsChecked.Value, Mode=TwoWay}" /> 31 </DataTemplate> 32 </ListBox.ItemTemplate> 33 </ListBox> 34 </Grid> 35</Window>

中で使ってる WithSubscribe もしかしたら最近入れたやつなのでお使いのバージョンでは動かないかもしれません。
その場合は以下のようにわけてかいてください。

cs

1// わけるまえ 2var cmd = new ReactiveCommand() 3 .WithSubscribe(...); 4 5// わけたあと 6var cmd = new ReactiveCommand(); 7cmd.Subscribe(...);

投稿2018/02/23 02:43

okazuki0130

総合スコア185

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

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

fox1

2018/02/23 04:10 編集

AnyChecked = _model.Items .SelectMany(x => x.Select(y => y.IsChecked).CombineLatest(y => y.Any(z => z))) .ToReadOnlyReactiveProperty(); SelectManyの使い方がわからなかったので参考になりました! ありがとうございました。
guest

0

コレクションを扱う場合、ReactiveCollectionを使うと良いでしょう。

ちょっとサンプルコードを書いてみましたので、確認してみてください。

ModelのObservableCollectionをToReadOnlyReactiveCollection()でReactiveCollectionに変換して使います。

Model

  • CheckItem (個々のチェック状態を保持。INotifyPropertyChangedを実装)
  • CheckItems (CheckItemをObservableCollectionで保持)

ViewModel

  • CheckViewModel (CheckItemのチェック状態(IsChecked)を監視するReactivePropertyをもつ)
  • MainWindowViewModel (CheckItemsを変換したReactiveCollectionをもつ)

このように実装しておけば、Modelのコレクション(CheckItems)にItemを追加・削除するだけでM→VM→Vと変更が伝わり、UIに反映されます。 model.Items.Add(new CheckItem())
(*サンプルでは、ボタンをクリックする度にチェックボックスが1つ追加されるようになっています。)

ただ、ButtonCommandの方はコレクションの変更に追従してくれないので、追加・削除の度に作り直しています。(もっといい方法があるかもしれませんが。)

cs

1//コレクションに変更があったら 2CheckItems.CollectionChangedAsObservable().Subscribe(_ => 3{ 4 disposable?.Dispose(); 5 //コマンド付けなおし 6 disposable = InitializeButtonCommand(); 7});

サンプルコード

cs

1using Reactive.Bindings; 2using Reactive.Bindings.Extensions; 3using System; 4using System.Collections.ObjectModel; 5using System.ComponentModel; 6using System.Linq; 7using System.Reactive.Linq; 8using System.Windows; 9 10namespace WpfApp2 11{ 12 /// <summary> 13 /// MainWindow.xaml の相互作用ロジック 14 /// </summary> 15 public partial class MainWindow : Window 16 { 17 18 public MainWindow() 19 { 20 InitializeComponent(); 21 } 22 } 23 24 public class MainWindowViewModel:INotifyPropertyChanged 25 { 26 private CheckItems model = new CheckItems(); 27 private IDisposable disposable; 28 private ReactiveCommand buttonCommand; 29 30 public event PropertyChangedEventHandler PropertyChanged; 31 32 public ReadOnlyReactiveCollection<CheckViewModel> CheckItems { get; set; } 33 public ReactiveCommand ButtonCommand 34 { 35 get => buttonCommand; 36 set 37 { 38 if (buttonCommand == value) { return; } 39 buttonCommand = value; 40 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonCommand))); 41 } 42 } 43 44 public MainWindowViewModel() 45 { 46 CheckItems = model.Items.ToReadOnlyReactiveCollection(item => new CheckViewModel(item)); 47 CheckItems.CollectionChangedAsObservable().Subscribe(_ => 48 { 49 disposable?.Dispose(); 50 disposable = InitializeButtonCommand(); 51 }); 52 53 disposable = InitializeButtonCommand(); 54 } 55 56 private IDisposable InitializeButtonCommand() 57 { 58 ButtonCommand = CheckItems.Select(vm => vm.IsChecked).CombineLatest(x => x.Any(b => b)).ToReactiveCommand(); 59 return ButtonCommand.Subscribe(() => 60 { 61 model.Items.Add(new CheckItem()); 62 }); 63 } 64 } 65 66 67 public class CheckViewModel 68 { 69 public ReactiveProperty<bool> IsChecked { get; } 70 public CheckViewModel(CheckItem item) 71 { 72 IsChecked = item.ToReactivePropertyAsSynchronized(x => x.IsChecked); 73 } 74 } 75 76 public class CheckItems 77 { 78 public ObservableCollection<CheckItem> Items { get; } = new ObservableCollection<CheckItem>(); 79 public CheckItems() 80 { 81 Items.Add(new CheckItem()); 82 Items.Add(new CheckItem()); 83 Items.Add(new CheckItem()); 84 Items.Add(new CheckItem()); 85 } 86 } 87 88 public class CheckItem : INotifyPropertyChanged 89 { 90 private bool isChecked; 91 public bool IsChecked 92 { 93 get => isChecked; 94 set 95 { 96 if (isChecked == value) { return; } 97 isChecked = value; 98 RaisePropertyChanged(nameof(IsChecked)); 99 } 100 } 101 102 public CheckItem() 103 { 104 } 105 106 public event PropertyChangedEventHandler PropertyChanged; 107 private void RaisePropertyChanged(string propertyName) 108 { 109 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 110 } 111 } 112} 113 114

xaml

1<Window x:Class="WpfApp2.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfApp2" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="350" Width="525"> 9 <Window.DataContext> 10 <local:MainWindowViewModel/> 11 </Window.DataContext> 12 <Grid> 13 <StackPanel> 14 <Button Command="{Binding ButtonCommand}" Content="OK" Width="120" 15 HorizontalAlignment="Left"/> 16 <ItemsControl ItemsSource="{Binding CheckItems}"> 17 <ItemsControl.ItemTemplate> 18 <DataTemplate> 19 <CheckBox IsChecked="{Binding IsChecked.Value}"/> 20 </DataTemplate> 21 </ItemsControl.ItemTemplate> 22 </ItemsControl> 23 </StackPanel> 24 </Grid> 25</Window> 26

投稿2018/02/10 04:57

pierre_3

総合スコア60

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

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

fox1

2018/02/13 01:20

回答ありがとうございます。 Modelのコレクションに追加・削除ではなく、Modelのコレクション自体が差し替わるのをRxで監視してButtonCommandにできないでしょうか?
pierre_3

2018/02/13 13:38 編集

ViewModelにINotifyPropertyChangedを実装して、ButtonCommandの変更を通知できるようにしたうえで、 コレクション変更後に、ButtonCommandを再設定してあげれば動きますが、もっといい方法がありそうな気もします。 Model.BaseCheckItems.Value = new List<Check>(){ new Check() }; this.AnyChecked = this.CheckItems.Value .Select( x => x.IsChecked ) .CombineLatest( x => x.Any( z => z ) ) .ToReactiveProperty(); this.ButtonCommand = AnyChecked.ToReactiveCommand(); public class ViewModel:INotifyPropertyChanged { public ReactiveCommand ButtonCommand {     get => _buttonCommand; set { if(_buttonCommand == value) { return; } _buttonCommand = value; RaisePropertyChanged(nameof(ButtonCommand)); } } public event PropertyChangedEventHandler PropertyChanged; public void RaisePropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問