.NET 4.0 Windows Formで Reactive Extensionsを使う準備


レガシーなFormでもMVVMしたい

過去の資産に縛られたWindowsForms開発をしております。
しかし過去の資産の余りにも・・・。な姿に涙目です。
.NET 4.0、C#4.0縛りです。

少しでも見通しを良くしたいと思い、
MVVMパターンの導入を検討しました。

試しにやってみたら.NET 4.0だと結構躓きましたので記録。

ReactivePropertyを使わないMVVMの準備

Windows Forms で MVVM」を参考にしたのですが、.NET 4.0縛りの私の環境ではそのままでは使えませんでした。

.NETのRx周りはStandard2.0以降で開花した為か、それ以前の環境ではそのままでは上手く実装できません。

とりあえずここで自作されているFormsMvvmをビルドする最低限のパッケージは以下で導入できました。

nugetパッケージマネージャコンソールにて

Install-Package Rx-Main -Version 2.2.5
Install-Package Microsoft.Bcl

これによって導入されるパッケージは以下でした。

  • Microsoft.Bcl v1.1.10
  • Microsoft.Bcl.Async v1.0.14
  • Rx-Core v2.2.5
  • Rx-Interface v2.2.5
  • Rx-Linq v2.2.5
  • Rx-Main v2.2.5

2018/12/4時点ではMicrosoft.Bclは最新版のVersion1.1.10で導入されました。
よってバージョン指定はしていません。

一緒に入ってくるMicrosoft.Bcl.Build は 1.0.14が導入されましたが、
2018/12/4時点で1.0.21が公開されていますので、場合によってはこれも手動でアップデートする必要が有るかもしれません。

.NET 4.0で Rxに手を出そうとすると Rx-Main Version 2.2.5というのが肝のようです。

Rx-MainはGUIのnuget管理Iから検索すると見つかりません。
https://stackoverflow.com/questions/40004669/system-reactive-rx-net-4-0-on-nuget

ReactivePropertyを使うMVVMの準備

Windows Forms で MVVM 2 (ReactiveProperty 編)」を参考にReactivePropertyを使おうとすると、最新版(5.3.0)は同様に.NET 4.0では導入できません。
2.9.0以下でエラー無く導入できました。

Install-Package Reactiveproperty -version 2.9.0

後述するエラーに悩まされた結果、
更に

  • PresentationCore v4.0.0.0の参照の追加
  • ViewModelの一部コードの変更

が発生しました。

変更したコード

ViewModel.cs
using System;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using Reactive.Bindings;

namespace reactivepropertytest2
{
    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public ReactiveProperty<int> Counter { get; } = new ReactiveProperty<int>();

        public ReactiveCommand UpCommand { get; private set; }
        public ReactiveCommand DownCommand { get; private set; }

        public ViewModel()
        {
            UpCommand = Counter.Select(_ => Counter.Value < 10).ToReactiveCommand();
            UpCommand.Subscribe(_ => Counter.Value++);//(() => Counter.Value++)ではコンパイルエラー
            DownCommand = Counter.Select(_ => Counter.Value > 0).ToReactiveCommand();
            DownCommand.Subscribe(_ => Counter.Value--);//(() => Counter.Value--)ではコンパイルエラー
        }
    }
}

ラムダ式の引数を理解してなくてエラーに悩まされた件

UpCommand.Subscribe(() => Counter.Value++);

の部分で

エラー   CS1660  ラムダ式 はデリゲート型ではないため、'IObserver<object>' 型に変換できません。

と怒られます。

この記述ではラムダ式の左辺が()なので引数0を示していますが、
Subscribeobjectを引数に取ることを要求しています。
よって上記のエラーが吐かれてます。
ラムダ式を良く分かってなかった私はこれが良く分からずうろうろしてしまいました。

因みにこのobjectUpCommand.Execute(object)で渡せますが、
引数を使いたい場合はUpCommandの型を指定するべきでしょう。

public ReactiveCommand<int> UpCommand { get; private set; }

の様に。

まだエラーは残ってます。

ラムダ式の左辺を()から_に変えたところまたエラーが。

'ICommand' は、参照されていないアセンブリに定義されています。
アセンブリ 'PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' に参照を追加する必要があります。

メッセージに従い、PresentationCoreの参照を追加してみます。

これでやっとコンパイルが通ります。