diff --git a/sandbox/WpfApp1/MainWindow.xaml b/sandbox/WpfApp1/MainWindow.xaml
index e0563c71..4925d71d 100644
--- a/sandbox/WpfApp1/MainWindow.xaml
+++ b/sandbox/WpfApp1/MainWindow.xaml
@@ -7,17 +7,21 @@
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
-
-
-
+
+
-
+
+
+
+
+
-
+
diff --git a/sandbox/WpfApp1/MainWindow.xaml.cs b/sandbox/WpfApp1/MainWindow.xaml.cs
index e34bf45a..9e117ffb 100644
--- a/sandbox/WpfApp1/MainWindow.xaml.cs
+++ b/sandbox/WpfApp1/MainWindow.xaml.cs
@@ -59,11 +59,26 @@ public class BasicUsagesViewModel : IDisposable
{
public BindableReactiveProperty Input { get; }
public BindableReactiveProperty Output { get; }
+ public ReadOnlyBindableReactiveProperty OutputRO { get; }
+ public ReadOnlyBindableReactiveProperty OutputRO2 { get; }
+ public ReactiveCommand AddCommand { get; } = new();
public BasicUsagesViewModel()
{
Input = new BindableReactiveProperty("");
Output = Input.Select(x => x.ToUpper()).ToBindableReactiveProperty("");
+ var rrp = Input.ToReadOnlyReactiveProperty("");
+ OutputRO = Input.ToReadOnlyBindableReactiveProperty("");
+ OutputRO2 = rrp.ToReadOnlyBindableReactiveProperty("");
+ AddCommand.SubscribeAwait(async (_, _) =>
+ {
+ var i = 0;
+ while (i++ < 10)
+ {
+ Input.Value += "@";
+ await Task.Delay(500);
+ }
+ });
}
public void Dispose()
diff --git a/src/R3/BindableReactiveProperty.cs b/src/R3/BindableReactiveProperty.cs
index 4eb29fe2..1fef92ee 100644
--- a/src/R3/BindableReactiveProperty.cs
+++ b/src/R3/BindableReactiveProperty.cs
@@ -42,6 +42,7 @@ public ReadOnlyBindableReactiveProperty(T value, IEqualityComparer? equalityC
public class BindableReactiveProperty : ReadOnlyBindableReactiveProperty, INotifyPropertyChanged, INotifyDataErrorInfo, IBindableReactiveProperty
{
IDisposable? subscription;
+ bool isReadOnly = false;
public T Value
{
@@ -82,7 +83,11 @@ public BindableReactiveProperty(T value, IEqualityComparer? equalityComparer)
{
}
- // ToBindableReactiveProperty
+ public BindableReactiveProperty(T value, IEqualityComparer? equalityComparer, bool isReadOnly)
+ : base(value, equalityComparer)
+ {
+ this.isReadOnly = isReadOnly;
+ }
internal BindableReactiveProperty(Observable source, T initialValue, IEqualityComparer? equalityComparer)
: base(initialValue, equalityComparer)
@@ -90,6 +95,14 @@ internal BindableReactiveProperty(Observable source, T initialValue, IEqualit
this.subscription = source.Subscribe(new Observer(this));
}
+ // ToBindableReactiveProperty
+
+ public ReadOnlyBindableReactiveProperty ToReadOnlyBindableReactiveProperty(T initialValue = default!)
+ {
+ isReadOnly = true;
+ return this;
+ }
+
protected override void DisposeCore()
{
subscription?.Dispose();
@@ -131,7 +144,7 @@ protected override void OnValueChanged(T value)
if (!validationContext.TryValidateValue(value, errors))
{
- ErrorsChanged?.Invoke(this, ValueChangedEventArgs.DataErrorsChanged);
+ ErrorsChanged?.Invoke(this, ValueChangedEventArgs.GetDataErrorsChangedEventArgs(isReadOnly));
// set is completed(validation does not call before set) so continue call PropertyChanged
}
@@ -150,12 +163,12 @@ protected override void OnValueChanged(T value)
if (errors != null && errors.Count != 0)
{
errors.Clear();
- ErrorsChanged?.Invoke(this, ValueChangedEventArgs.DataErrorsChanged);
+ ErrorsChanged?.Invoke(this, ValueChangedEventArgs.GetDataErrorsChangedEventArgs(isReadOnly));
}
}
}
- PropertyChanged?.Invoke(this, ValueChangedEventArgs.PropertyChanged);
+ PropertyChanged?.Invoke(this, ValueChangedEventArgs.GetPropertyChangedEventArgs(isReadOnly));
}
// for INotifyDataErrorInfo
@@ -213,7 +226,7 @@ protected override void OnReceiveError(Exception exception)
errors.Add(new ValidationResult(exception.Message));
}
- ErrorsChanged?.Invoke(this, ValueChangedEventArgs.DataErrorsChanged);
+ ErrorsChanged?.Invoke(this, ValueChangedEventArgs.GetDataErrorsChangedEventArgs(isReadOnly));
}
public BindableReactiveProperty EnableValidation()
@@ -302,6 +315,11 @@ public bool TryValidateValue(object? value, ICollection valida
internal static class ValueChangedEventArgs
{
- internal static readonly PropertyChangedEventArgs PropertyChanged = new PropertyChangedEventArgs("Value");
- internal static readonly DataErrorsChangedEventArgs DataErrorsChanged = new DataErrorsChangedEventArgs("Value");
+ static readonly PropertyChangedEventArgs propertyChanged = new PropertyChangedEventArgs("Value");
+ static readonly PropertyChangedEventArgs propertyChangedReadOnly = new PropertyChangedEventArgs("CurrentValue");
+ static readonly DataErrorsChangedEventArgs dataErrorsChanged = new DataErrorsChangedEventArgs("Value");
+ static readonly DataErrorsChangedEventArgs dataErrorsChangedReadOnly = new DataErrorsChangedEventArgs("CurrentValue");
+
+ internal static PropertyChangedEventArgs GetPropertyChangedEventArgs(bool isReadOnly) => isReadOnly ? propertyChangedReadOnly : propertyChanged;
+ internal static DataErrorsChangedEventArgs GetDataErrorsChangedEventArgs(bool isReadOnly) => isReadOnly ? dataErrorsChangedReadOnly : dataErrorsChanged;
}
diff --git a/src/R3/ReactivePropertyExtensions.cs b/src/R3/ReactivePropertyExtensions.cs
index 3e115aa9..1701b2df 100644
--- a/src/R3/ReactivePropertyExtensions.cs
+++ b/src/R3/ReactivePropertyExtensions.cs
@@ -32,21 +32,13 @@ public static BindableReactiveProperty ToBindableReactiveProperty(this Obs
public static ReadOnlyBindableReactiveProperty ToReadOnlyBindableReactiveProperty(this Observable source, T initialValue = default!)
{
- if (source is ReadOnlyBindableReactiveProperty rrp)
- {
- return rrp;
- }
return source.ToReadOnlyBindableReactiveProperty(EqualityComparer.Default, initialValue);
}
public static ReadOnlyBindableReactiveProperty ToReadOnlyBindableReactiveProperty(this Observable source, IEqualityComparer? equalityComparer, T initialValue = default!)
{
- if (source is ReadOnlyBindableReactiveProperty rrp)
- {
- return rrp;
- }
- // allow to cast ReactiveProperty
- return new ConnectedBindableReactiveProperty(source, initialValue, equalityComparer);
+ // allow to cast BindableReactiveProperty
+ return new ConnectedBindableReactiveProperty(source, initialValue, equalityComparer, isReadOnly: true);
}
}
@@ -88,8 +80,8 @@ internal sealed class ConnectedBindableReactiveProperty : BindableReactivePro
{
readonly IDisposable sourceSubscription;
- public ConnectedBindableReactiveProperty(Observable source, T initialValue, IEqualityComparer? equalityComparer)
- : base(initialValue, equalityComparer)
+ public ConnectedBindableReactiveProperty(Observable source, T initialValue, IEqualityComparer? equalityComparer, bool isReadOnly = false)
+ : base(initialValue, equalityComparer, isReadOnly)
{
this.sourceSubscription = source.Subscribe(new Observer(this));
}