-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature: Add ReactiveProperty (#3729)
<!-- Please be sure to read the [Contribute](https://github.com/reactiveui/reactiveui#contribute) section of the README --> **What kind of change does this PR introduce?** <!-- Bug fix, feature, docs update, ... --> Feature **What is the current behavior?** <!-- You can also link to an open issue here. --> Boiler plate code is required to get an Observable Property **What is the new behavior?** <!-- If this is a feature change --> A quick and simple ReactiveProperty has been added to suit a small number of properties. ReactiveProperty is a two way bindable declarative observable property with imperative get set Value. Useful when mixing different UI Frameworks without the requirement for a ReactiveObject based ViewModel Declare as ``` IReactiveProperty<string> MyProperty { get; } = new ReactiveProperty<string>(); ``` Use declarativly ``` MyProperty.Subscribe(x => // use x as desired); ``` Use imperativly ``` MyProperty.Value = "Set the value"; var value = MyProperty.Value; ``` Use XAML Bindings ``` {Binding MyProperty.Value} ``` **What might this PR break?** None **Please check if the PR fulfills these requirements** - [ ] Tests for the changes have been added (for bug fixes / features) - [ ] Docs have been added / updated (for bug fixes / features) **Other information**:
- Loading branch information
1 parent
00305af
commit cec4cca
Showing
8 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
src/ReactiveUI.Tests/ReactiveProperty/ReactivePropertyTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for full license information. | ||
|
||
using Microsoft.Reactive.Testing; | ||
|
||
namespace ReactiveUI.Tests.ReactiveProperty | ||
{ | ||
public class ReactivePropertyTest : ReactiveTest | ||
{ | ||
[Fact] | ||
public void NormalCase() | ||
{ | ||
var rp = new ReactiveProperty<string>(); | ||
Assert.Null(rp.Value); | ||
rp.Subscribe(x => Assert.Null(x)); | ||
} | ||
|
||
[Fact] | ||
public void InitialValue() | ||
{ | ||
var rp = new ReactiveProperty<string>("Hello world"); | ||
Assert.Equal(rp.Value, "Hello world"); | ||
rp.Subscribe(x => Assert.Equal(x, "Hello world")); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for full license information. | ||
|
||
namespace ReactiveUI.Tests.ReactiveProperty | ||
{ | ||
internal enum TestEnum | ||
{ | ||
None, | ||
Enum1, | ||
Enum2 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for full license information. | ||
|
||
namespace ReactiveUI; | ||
|
||
/// <summary> | ||
/// Reactive Property. | ||
/// </summary> | ||
/// <typeparam name="T">The type of the property.</typeparam> | ||
/// <seealso cref="IObservable<T>" /> | ||
/// <seealso cref="ICancelable" /> | ||
public interface IReactiveProperty<T> : IObservable<T?>, ICancelable | ||
{ | ||
/// <summary> | ||
/// Gets or sets the value. | ||
/// </summary> | ||
/// <value> | ||
/// The value. | ||
/// </value> | ||
public T? Value { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for full license information. | ||
|
||
namespace ReactiveUI; | ||
|
||
/// <summary> | ||
/// ReactiveProperty - a two way bindable declarative observable property with imperative get set. | ||
/// </summary> | ||
/// <typeparam name="T">The type of the property.</typeparam> | ||
/// <seealso cref="ReactiveObject" /> | ||
/// <seealso cref="IReactiveProperty<T>" /> | ||
[DataContract] | ||
public class ReactiveProperty<T> : ReactiveObject, IReactiveProperty<T> | ||
{ | ||
private readonly IScheduler _scheduler; | ||
private readonly CompositeDisposable _disposables = []; | ||
private T? _value; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="ReactiveProperty{T}"/> class. | ||
/// </summary> | ||
public ReactiveProperty() => _scheduler = RxApp.TaskpoolScheduler; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="ReactiveProperty{T}"/> class. | ||
/// </summary> | ||
/// <param name="initialValue">The initial value.</param> | ||
public ReactiveProperty(T? initialValue) | ||
{ | ||
Value = initialValue; | ||
_scheduler = RxApp.TaskpoolScheduler; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="ReactiveProperty{T}"/> class. | ||
/// </summary> | ||
/// <param name="initialValue">The initial value.</param> | ||
/// <param name="scheduler">The scheduler.</param> | ||
public ReactiveProperty(T? initialValue, IScheduler? scheduler) | ||
{ | ||
Value = initialValue; | ||
_scheduler = scheduler ?? RxApp.TaskpoolScheduler; | ||
} | ||
|
||
/// <summary> | ||
/// Gets a value indicating whether gets a value that indicates whether the object is disposed. | ||
/// </summary> | ||
public bool IsDisposed => _disposables.IsDisposed; | ||
|
||
/// <summary> | ||
/// Gets or sets the value. | ||
/// </summary> | ||
/// <value> | ||
/// The value. | ||
/// </value> | ||
[DataMember] | ||
[JsonInclude] | ||
public T? Value | ||
{ | ||
get => _value; | ||
set => this.RaiseAndSetIfChanged(ref _value, value); | ||
} | ||
|
||
/// <summary> | ||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. | ||
/// </summary> | ||
public void Dispose() | ||
{ | ||
Dispose(true); | ||
GC.SuppressFinalize(this); | ||
} | ||
|
||
/// <summary> | ||
/// Notifies the provider that an observer is to receive notifications. | ||
/// </summary> | ||
/// <param name="observer">The object that is to receive notifications.</param> | ||
/// <returns> | ||
/// A reference to an interface that allows observers to stop receiving notifications before | ||
/// the provider has finished sending them. | ||
/// </returns> | ||
public IDisposable Subscribe(IObserver<T?> observer) => | ||
this.WhenAnyValue(vm => vm.Value) | ||
.ObserveOn(_scheduler) | ||
.Subscribe(observer) | ||
.DisposeWith(_disposables); | ||
|
||
/// <summary> | ||
/// Releases unmanaged and - optionally - managed resources. | ||
/// </summary> | ||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> | ||
protected virtual void Dispose(bool disposing) | ||
{ | ||
if (_disposables?.IsDisposed == false && disposing) | ||
{ | ||
_disposables?.Dispose(); | ||
} | ||
} | ||
} |