そのプロパティ、ReactivePropertyでどう書く？

# 概要
ViewModelにある変更通知プロパティをReactivePropertyに書き変える時はどうすればいいのか。
タイプ別にまとめてみました。

# ViewModel
ViewModelの変更通知プロパティをアクセサごとに分類して比較します。
どの場合でも公開するReactivePropertyのアクセサは`{ get; }`で用が足ります。
またReactivePropertySlimの場合でも同様に動きます。

## 読み書き可能プロパティ `MyProperty { get; set; }`
最も一般的なクラス外からでも書込み可能なプロパティは通常の`ReactiveProperty`に変えられます。

```csharp
private string _ReadAndWrite = "Read And Write";
public string ReadAndWrite
{
    get => _ReadAndWrite;
    set
    {
        _ReadAndWrite = value;
        RaisePropertyChanged();
        //RaisePropertyChanged(nameof(ReadOnly));//読み取りのみプロパティ通知用
    }
}
```

👇

```csharp
public ReactiveProperty<string> ReadAndWrite_RP { get; }
 = new ReactiveProperty<string>("Read And Write");
```

## 読み取りのみプロパティ `MyProperty { get; }`
クラス内外を問わず、直接書き込めずに、別のプロパティなどに依存して内容が変わるプロパティの場合です。
今回は上の読み書き可能プロパティに依存していますが、Model層のプロパティなどに依存する形が多いです。
変更通知プロパティの場合は**依存先の**プロパティに、このプロパティの変更通知イベント呼び出しも書く必要があります。
ReactivePropertyの場合はコンストラクタ内に依存先との関係を書く必要があります。

```csharp
public string ReadOnly => ReadAndWrite.ToUpper();

//ReadAndWriteプロパティ内に下記行を追加
RaisePropertyChanged(nameof(ReadOnly));
```

👇

```csharp
public ReadOnlyReactiveProperty<string> ReadOnly_RP { get; }

//コンストラクタ内で
ReadOnly_RP = ReadAndWrite_RP
    .Select(x => x.ToUpper())
    .ToReadOnlyReactiveProperty();
```

## クラス内からのみ書き込み可能プロパティ `MyProperty { get; private set; }`
クラス外からは書き込み不可、クラス内からは直接書き込み可能なプロパティの場合です。

`ReactiveProperty`ですと、書き込みだけ制限することはできませんし、`ReadOnlyReactiveProperty`にするとクラス内からも直接書き込めません。
そこでクラス内から書き込む時は`private`な`ReactiveProperty`を使用した上で、クラス外には書き込め不可な形で公開します。
ここで上記と同じように`ReactiveProperty`から`ReadOnlyReactiveProperty`を生成しても良いですが、より簡単な方法があります。
それは`IReadOnlyReactiveProperty`として`ReactiveProperty`を公開することです。

```csharp
private string _WritePrivate = "WritePrivate";
public string WritePrivate
{
    get => _WritePrivate;
    private set
    {
        _WritePrivate = value;
        RaisePropertyChanged();
    }
}
```

👇

```csharp
private ReactiveProperty<string> writePrivate_RP = new ReactiveProperty<string>("WritePrivate");
public IReadOnlyReactiveProperty<string> WritePrivate_RP => writePrivate_RP;
```

ただしクラス外からもキャストすれば変更出来てしまいます。
自作のプロジェクト内のViewに公開するだけなら問題ありませんが、ライブラリとして外部に出す場合などは注意が必要です。

```csharp:クラス外
(vm.WritePrivate_RP as ReactiveProperty<string>).Value ="change"; 
```

# View
View側は常に`.Value`を付け足すだけです。
書き込み不可の場合は`TwoWay`でバインディングすると例外が発生します。

# 全体コード
以下がViewとViewModelの全体コードです。
上記内容以外にViewModelないでプロパティを書き込むコマンドが含まれています。

```csharp:MainWindowViewModel.cs
public class MainWindowViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged([CallerMemberName]string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    #region 読み書き可能
    private string _ReadAndWrite = "Read And Write";
    public string ReadAndWrite
    {
        get => _ReadAndWrite;
        set
        {
            _ReadAndWrite = value;
            RaisePropertyChanged();
            RaisePropertyChanged(nameof(ReadOnly));//読み取りのみプロパティ通知用
        }
    }

    public ReactiveProperty<string> ReadAndWrite_RP { get; } = new ReactiveProperty<string>("Read And Write");
    #endregion


    #region 読み取りのみ
    public string ReadOnly => ReadAndWrite.ToUpper();

    public ReadOnlyReactiveProperty<string> ReadOnly_RP { get; }
    #endregion


    #region クラス内からのみ書き込み可能
    private string _WritePrivate = "WritePrivate";
    public string WritePrivate
    {
        get => _WritePrivate;
        private set
        {
            _WritePrivate = value;
            RaisePropertyChanged();
        }
    }

    private ReactiveProperty<string> writePrivate_RP = new ReactiveProperty<string>("WritePrivate");
    public IReadOnlyReactiveProperty<string> WritePrivate_RP => writePrivate_RP;
    #endregion

    /// <summary>
    /// プロパティ書き換えコマンド
    /// </summary>
    public ReactiveCommand ChangePropertiesCommand { get; } = new ReactiveCommand();

    public MainWindowViewModel()
    {
        ReadOnly_RP = ReadAndWrite_RP
            .Select(x => x.ToUpper())
            .ToReadOnlyReactiveProperty();

        ChangePropertiesCommand.Subscribe(() => ChangeProperties());
    }

    private void ChangeProperties()
    {
        ReadAndWrite = "Changed from VM";
        WritePrivate = "Changed from VM";
        //ReadOnly = "Changed from VM";//コンパイルエラー

        ReadAndWrite_RP.Value = "Changed from VM";
        writePrivate_RP.Value = "Changed from VM";
        //ReadOnly_RP.Value = "Changed from VM";//コンパイルエラー
    }
}
```

```xml:MainWindow.xaml
<Window
   x:Class="PropertyToReactiveProperty.Views.MainWindow"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:vm="clr-namespace:PropertyToReactiveProperty.ViewModels"
   Width="525"
   Height="150">
   <Window.DataContext>
      <vm:MainWindowViewModel />
   </Window.DataContext>
   <Window.Resources>
      <Style TargetType="{x:Type UniformGrid}">
         <Setter Property="Columns" Value="3" />
      </Style>
   </Window.Resources>

   <StackPanel Orientation="Vertical">
      <!--  読み書き可能  -->
      <UniformGrid>
         <TextBlock Text="ReadAndWrite:" />
         <TextBox Text="{Binding ReadAndWrite}" />
         <TextBox Text="{Binding ReadAndWrite_RP.Value}" />
      </UniformGrid>

      <!--  読み取りのみ  -->
      <UniformGrid>
         <TextBlock Text="ReadOnly:" />
         <TextBox Text="{Binding ReadOnly, Mode=OneWay}" />
         <TextBox Text="{Binding ReadOnly_RP.Value, Mode=OneWay}" />
      </UniformGrid>

      <!--  クラス内からのみ書き込み可能  -->
      <UniformGrid>
         <TextBlock Text="WritePrivate:" />
         <TextBox Text="{Binding WritePrivate, Mode=OneWay}" />
         <TextBox Text="{Binding WritePrivate_RP.Value, Mode=OneWay}" />
      </UniformGrid>

      <!--  プロパティ書き換えコマンド  -->
      <Button Command="{Binding ChangePropertiesCommand}" Content="Change Properties" />
   </StackPanel>
</Window>
```

# 環境
VisualStudio2017
.NET Framework 4.7.1
C#7.1
ReactiveProperty 5.2.0