画面とやり取りするための最低限の構成(ReactiveProperty版)

tera1707.com

やりたいこと

今まで使ったことがなかったReactivePropertyを使う必要が出てきた。
取り合えず小難しいことはおいておいて、最低限値の表示、ボタン押下時の処理の記述だけやり方メモっておきたい。

今回試した前提

★全然ReactivePropertyを全然知らない状態からとにかく明日まずは使いたいので、ネットのサンプルをかき集め、自分用に保存する。(ので、間違いあるかも)

  • 使用したもの
    • WinUI3
    • NET6
    • C#

サンプルアプリ

「カウントUPボタン」を押すと、Cmdが発火し、その中でCounterを++する。
そのCmdとCounterを、ReactivePropertyで作る。

f:id:tera1707:20220306232226p:plain

サンプルコード

■画面

<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:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBlock Text = "{x:Bind DC.Counter.Value, Mode=OneWay}" />
        <Button Content = "カウントUPボタン"  Command="{x:Bind DC.Cmd}"/>
    </StackPanel>
</Window>

■ViewModelなど

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
    {
        public MainWindowViewModel DC = new MainWindowViewModel();
        public MainWindow() => this.InitializeComponent();
    }

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        // ReactivePropertyでは本来INotifyPropertyChangedを実装しなくてよいが、
        // 実装しておかないとメモリリークするらしい(WinUI3でどうなるかは不明)
        // https://qiita.com/YSRKEN/items/5a36fb8071104a989fb8#q-%E3%81%82%E3%82%8Creactiveproperty%E3%81%A0%E3%81%A8inotifypropertychanged%E8%A6%81%E3%82%89%E3%81%AA%E3%81%84%E3%82%93%E3%81%98%E3%82%83%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%A3%E3%81%91
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string propertyName) => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        // ---------------------------------
        // ボタン押下時コマンド
        public ReactiveCommand Cmd { get; set; } = new ReactiveCommand();
        // ボタン押下時にカウントUPするカウンタ
        public ReactiveProperty<int> Counter { get; set; } = new ReactiveProperty<int>();

        public MainWindowViewModel()
        {
            Counter.Subscribe(_ => Debug.WriteLine("カウンター 変化"));

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

■別解

C#コードの部分を下記のようにしても動いた。(xamlは同じものを使用)

ViewModelを書くのがめんどくさい場合などは、これでもよいのかも。

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 ReactiveProperty<int> Counter { get; set; } = new ReactiveProperty<int>();

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

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

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

気づいたこと

WindowsクラスにDataContextがない!

→バインドするときに、VMのフィールド名.プロパティ名.Valueとする。

バインディングするときの書き方が{x:Bind プロパティ名}

→WinUI3じゃないWPFのときの{Binding プロパティ名}とすると、値が表示されなかった。

参考

ReactivePropertyの使い方

qiita.com

blog.okazuki.jp

INotifyPropertyChangedを実装しないとメモリリークする件

qiita.com

aridai.net

blog.okazuki.jp