前提・実現したいこと
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ページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答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
総合スコア185
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
総合スコア60
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/02/13 13:38 編集
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/02/23 04:10 編集