複数条件で、コントロールの有効無効を切り替える

tera1707.com

やりたいこと

ReactivePropertyを使っていないときにやっていたような、複数MultiBindingしたプロパティで条件判定してボタンの有効無効を切り替えるようなことを、ReactivePropertyでやりたい。

やりかた

Observable.Merge()、もしくは CombineLatest() を使う。

前提

以下の環境で実験した。

  • VisualStudio2022
  • .NET6
  • WinUI3
  • ReactiveProperty8.0.4

サンプルコード

xamlの中の「複数条件で有効化1」「複数条件で有効化2」と書いてるボタンを、カウントUPボタン1,2を押すとカウントアップする2つの数字を使って有効無効の判定をする実験プログラム。

カウントUPボタン1,2の両方を6回押すと、複数条件で有効化1,2ボタンが有効になるイメージ。

<Window
    x:Class="ReactiveRenshuu.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ReactiveRenshuu"
    xmlns:converter="using:ReactiveRenshuu.Converter"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <!--<Grid.Resources>
            <converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
        </Grid.Resources>-->
        
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock Text = "{x:Bind DC.Counter.Value, Mode=OneWay}" />
            <Button Content = "カウントUPボタン"  Command="{x:Bind DC.Cmd}"/>
            <Button Content = "カウントUPボタン2"  Command="{x:Bind DC.Cmd2}"/>
            
            <Button Content = "複数条件で有効化1" IsEnabled="{x:Bind DC.BtIsEnabled.Value, Mode=OneWay}"/>
            <Button Content = "複数条件で有効化2" IsEnabled="{x:Bind DC.BtIsEnabled2.Value, Mode=OneWay}"/>
        </StackPanel>
    </Grid>
</Window>
using Microsoft.UI.Xaml;
using Reactive.Bindings;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reactive.Linq;

namespace ReactiveRenshuu
{
    public sealed partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string propertyName) => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        // ---------------------------------
        public ReactiveCommand Cmd { get; set; } = new ReactiveCommand();
        public ReactiveCommand Cmd2 { get; set; } = new ReactiveCommand();
        public ReactiveProperty<int> Counter { get; set; } = new ReactiveProperty<int>();
        public ReactiveProperty<int> Counter2 { get; set; } = new ReactiveProperty<int>();
        public ReadOnlyReactiveProperty<bool> BtIsEnabled { get; set; }
        public ReadOnlyReactiveProperty<bool> BtIsEnabled2 { get; set; }

        public MainWindow DC;
        public MainWindow()
        {
            this.InitializeComponent();
            DC = this;

            Counter.Subscribe(_ => Debug.WriteLine("カウンター1 変化"));
            Counter2.Subscribe(_ => Debug.WriteLine("カウンター2 変化"));

            Cmd.Subscribe(_ =>
            {
                Counter.Value++;
                Debug.WriteLine("ボタン押下コマンド1 {0}", Counter.Value);
            });
            Cmd2.Subscribe(_ =>
            {
                Counter2.Value++;
                Debug.WriteLine("ボタン押下コマンド2 {0}", Counter2.Value);
            });

            // CounterとCouonter2の両方を監視して、その2つのどちらかの変化時にBtIsEnabledも変化させ、画面を更新したいときの書き方

            BtIsEnabled = Observable.Merge(Counter, Counter2).Select(x => Counter.Value > 5 && Counter2.Value > 5).ToReadOnlyReactiveProperty();

            BtIsEnabled2 = Counter.CombineLatest(Counter2, (a, b) => a > 5 && b > 5 ) .ToReadOnlyReactiveProperty();
        }
    }
}

参考

kitunechan.hatenablog.jp

ぼやき

数日前から使い始めて、ものすごく便利オーラを感じている。
が、ReactiveProperty、結構情報量少なめ?これをやるだけでも結構つまずいてしまった...