概要
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
にするとクラス内からも直接書き込めません。
そこでクラス内から書き込む時はprivate
なReactiveProperty
を使用した上で、クラス外には書き込め不可な形で公開します。
ここで上記と同じように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ないでプロパティを書き込むコマンドが含まれています。
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";//コンパイルエラー
}
}
<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