LoginSignup
20
27

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-10-15

概要

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

ViewModel

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

読み書き可能プロパティ MyProperty { get; set; }

最も一般的なクラス外からでも書込み可能なプロパティは通常のReactivePropertyに変えられます。

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");

読み取りのみプロパティ MyProperty { get; }

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

public string ReadOnly => ReadAndWrite.ToUpper();

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

👇

public ReadOnlyReactiveProperty<string> ReadOnly_RP { get; }

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

クラス内からのみ書き込み可能プロパティ MyProperty { get; private set; }

クラス外からは書き込み不可、クラス内からは直接書き込み可能なプロパティの場合です。

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

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;

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

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

View

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

全体コード

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

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";//コンパイルエラー
    }
}
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

20
27
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
27