Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ namespace ReactiveUI
}
public static class ObservableFuncMixins
{
public static System.IObservable<TResult?> ToObservable<TSource, TResult>(this TSource? source, System.Linq.Expressions.Expression<System.Func<TSource, TResult?>> expression, bool beforeChange = false, bool skipInitial = false) { }
public static System.IObservable<TResult?> ToObservable<TSource, TResult>(this System.Linq.Expressions.Expression<System.Func<TSource, TResult?>> expression, TSource? source, bool beforeChange = false, bool skipInitial = false) { }
}
public static class ObservableLoggingMixin
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ namespace ReactiveUI
}
public static class ObservableFuncMixins
{
public static System.IObservable<TResult?> ToObservable<TSource, TResult>(this TSource? source, System.Linq.Expressions.Expression<System.Func<TSource, TResult?>> expression, bool beforeChange = false, bool skipInitial = false) { }
public static System.IObservable<TResult?> ToObservable<TSource, TResult>(this System.Linq.Expressions.Expression<System.Func<TSource, TResult?>> expression, TSource? source, bool beforeChange = false, bool skipInitial = false) { }
}
public static class ObservableLoggingMixin
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ namespace ReactiveUI
}
public static class ObservableFuncMixins
{
public static System.IObservable<TResult?> ToObservable<TSource, TResult>(this TSource? source, System.Linq.Expressions.Expression<System.Func<TSource, TResult?>> expression, bool beforeChange = false, bool skipInitial = false) { }
public static System.IObservable<TResult?> ToObservable<TSource, TResult>(this System.Linq.Expressions.Expression<System.Func<TSource, TResult?>> expression, TSource? source, bool beforeChange = false, bool skipInitial = false) { }
}
public static class ObservableLoggingMixin
{
Expand Down
56 changes: 32 additions & 24 deletions src/ReactiveUI.Tests/Mocks/PropertyBindViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using DynamicData.Binding;

namespace ReactiveUI.Tests
Expand All @@ -18,14 +19,15 @@ namespace ReactiveUI.Tests
/// <seealso cref="ReactiveUI.ReactiveObject" />
public class PropertyBindViewModel : ReactiveObject
{
private string? _Property1;
private PropertyBindModel? _Model;
private int _Property2;
private double _JustADouble;
private decimal _JustADecimal;
private double? _NullableDouble;
private int _JustAInt32;
private bool _JustABoolean;
private string? _property1;
private PropertyBindModel? _model;
private int _property2;
private double _justADouble;
private decimal _justADecimal;
private double? _nullableDouble;
private int _justAInt32;
private bool _justABoolean;
private Visibility _justAVisibility;

/// <summary>
/// Initializes a new instance of the <see cref="PropertyBindViewModel"/> class.
Expand All @@ -42,26 +44,26 @@ public PropertyBindViewModel(PropertyBindModel? model = null)
/// </summary>
public string? Property1
{
get => _Property1;
set => this.RaiseAndSetIfChanged(ref _Property1, value);
get => _property1;
set => this.RaiseAndSetIfChanged(ref _property1, value);
}

/// <summary>
/// Gets or sets the property2.
/// </summary>
public int Property2
{
get => _Property2;
set => this.RaiseAndSetIfChanged(ref _Property2, value);
get => _property2;
set => this.RaiseAndSetIfChanged(ref _property2, value);
}

/// <summary>
/// Gets or sets the just a double.
/// </summary>
public double JustADouble
{
get => _JustADouble;
set => this.RaiseAndSetIfChanged(ref _JustADouble, value);
get => _justADouble;
set => this.RaiseAndSetIfChanged(ref _justADouble, value);
}

/// <summary>
Expand All @@ -72,35 +74,41 @@ public double JustADouble
/// </value>
public bool JustABoolean
{
get => _JustABoolean;
set => this.RaiseAndSetIfChanged(ref _JustABoolean, value);
get => _justABoolean;
set => this.RaiseAndSetIfChanged(ref _justABoolean, value);
}

/// <summary>
/// Gets or sets the just a decimal.
/// </summary>
public decimal JustADecimal
{
get => _JustADecimal;
set => this.RaiseAndSetIfChanged(ref _JustADecimal, value);
get => _justADecimal;
set => this.RaiseAndSetIfChanged(ref _justADecimal, value);
}

/// <summary>
/// Gets or sets the just a int32.
/// </summary>
public int JustAInt32
{
get => _JustAInt32;
set => this.RaiseAndSetIfChanged(ref _JustAInt32, value);
get => _justAInt32;
set => this.RaiseAndSetIfChanged(ref _justAInt32, value);
}

/// <summary>
/// Gets or sets the nullable double.
/// </summary>
public double? NullableDouble
{
get => _NullableDouble;
set => this.RaiseAndSetIfChanged(ref _NullableDouble, value);
get => _nullableDouble;
set => this.RaiseAndSetIfChanged(ref _nullableDouble, value);
}

public Visibility JustAVisibility
{
get => _justAVisibility;
set => this.RaiseAndSetIfChanged(ref _justAVisibility, value);
}

/// <summary>
Expand All @@ -116,8 +124,8 @@ public double? NullableDouble
/// </summary>
public PropertyBindModel? Model
{
get => _Model;
set => this.RaiseAndSetIfChanged(ref _Model, value);
get => _model;
set => this.RaiseAndSetIfChanged(ref _model, value);
}
}
}
113 changes: 111 additions & 2 deletions src/ReactiveUI.Tests/Platforms/windows-xaml/PropertyBindingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
using System.Globalization;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using DynamicData.Binding;
using Xunit;

Expand Down Expand Up @@ -37,7 +40,7 @@ public void TwoWayBindWithFuncConvertersSmokeTest()
vm.JustADecimal = 123.45m;
Assert.NotEqual(vm.JustADecimal.ToString(CultureInfo.InvariantCulture), view.SomeTextBox.Text);

var disp = fixture.Bind(vm, view, x => x.JustADecimal, x => x.SomeTextBox.Text, (IObservable<Unit>?)null, d => d.ToString(), decimal.Parse);
var disp = fixture.Bind(vm, view, x => x.JustADecimal, x => x.SomeTextBox.Text, (IObservable<Unit>?)null, d => d.ToString(), t => decimal.TryParse(t, out var res) ? res : decimal.Zero);

Assert.Equal(vm.JustADecimal.ToString(CultureInfo.InvariantCulture), view.SomeTextBox.Text);
Assert.Equal(123.45m, vm.JustADecimal);
Expand Down Expand Up @@ -551,7 +554,21 @@ public void BindWithFuncShouldWorkAsExtensionMethodSmokeTest()
PropertyBindViewModel? vm = new();
var view = new PropertyBindView { ViewModel = vm };

view.Bind(vm, x => x.JustADecimal, x => x.SomeTextBox.Text, d => d.ToString(CultureInfo.InvariantCulture), decimal.Parse);
vm.JustADecimal = 123.45m;
Assert.NotEqual(vm.JustADecimal.ToString(CultureInfo.InvariantCulture), view.SomeTextBox.Text);

view.Bind(vm, x => x.JustADecimal, x => x.SomeTextBox.Text, d => d.ToString(CultureInfo.InvariantCulture), t => decimal.TryParse(t, out var res) ? res : 0m);

Assert.Equal(view.SomeTextBox.Text, "123.45");

vm.JustADecimal = 1.0M;
Assert.Equal(view.SomeTextBox.Text, "1.0");

vm.JustADecimal = 2.0M;
Assert.Equal(view.SomeTextBox.Text, "2.0");

view.SomeTextBox.Text = "3.0";
Assert.Equal(vm.JustADecimal, 3.0M);
}

/// <summary>
Expand All @@ -578,5 +595,97 @@ public void BindInitialViewModelShouldBeGarbageCollectedWhenOverwritten()

Assert.False(weakRef.IsAlive);
}

[Fact]
public void OneWayBindWithHintTest()
{
CompositeDisposable dis = new();
PropertyBindViewModel? vm = new();
PropertyBindView view = new() { ViewModel = vm };
PropertyBinderImplementation fixture = new();

fixture.OneWayBind(vm, view, vm => vm.JustABoolean, v => v.SomeTextBox.Visibility, BooleanToVisibilityHint.Inverse).DisposeWith(dis);
Assert.Equal(view.SomeTextBox.Visibility, System.Windows.Visibility.Visible);

vm.JustABoolean = true;
Assert.Equal(view.SomeTextBox.Visibility, System.Windows.Visibility.Collapsed);

dis.Dispose();
Assert.True(dis.IsDisposed);
}

[Fact]
public void OneWayBindWithHintTestDisposeWithFailure()
{
CompositeDisposable? dis = null;
PropertyBindViewModel? vm = new();
PropertyBindView view = new() { ViewModel = vm };
PropertyBinderImplementation fixture = new();

Assert.Throws<ArgumentNullException>(() => fixture.OneWayBind(vm, view, vm => vm.JustABoolean, v => v.SomeTextBox.Visibility, BooleanToVisibilityHint.Inverse).DisposeWith(dis!));
}

[Fact]
public void BindToWithHintTest()
{
CompositeDisposable dis = new();
PropertyBindViewModel? vm = new();
var view = new PropertyBindView { ViewModel = vm };
var obs = vm.WhenAnyValue(x => x.JustABoolean);
var a = new PropertyBinderImplementation().BindTo(obs, view, v => v.SomeTextBox.Visibility, BooleanToVisibilityHint.Inverse).DisposeWith(dis);
Assert.Equal(view.SomeTextBox.Visibility, System.Windows.Visibility.Visible);

vm.JustABoolean = true;
Assert.Equal(view.SomeTextBox.Visibility, System.Windows.Visibility.Collapsed);

dis.Dispose();
Assert.True(dis.IsDisposed);
}

[Fact]
public void BindWithFuncToTriggerUpdateTest()
{
CompositeDisposable dis = new();
PropertyBindViewModel? vm = new();
var view = new PropertyBindView { ViewModel = vm };
var update = new Subject<bool>();

vm.JustADecimal = 123.45m;
Assert.NotEqual(vm.JustADecimal.ToString(CultureInfo.InvariantCulture), view.SomeTextBox.Text);

view.Bind(vm, x => x.JustADecimal, x => x.SomeTextBox.Text, update.AsObservable(), d => d.ToString(CultureInfo.InvariantCulture), t => decimal.TryParse(t, out var res) ? res : decimal.Zero).DisposeWith(dis);

vm.JustADecimal = 1.0M;

// value should have pre bind value
Assert.Equal(view.SomeTextBox.Text, "123.45");

// trigger UI update
update.OnNext(true);
Assert.Equal(view.SomeTextBox.Text, "1.0");

vm.JustADecimal = 2.0M;
Assert.Equal(view.SomeTextBox.Text, "1.0");

update.OnNext(true);
Assert.Equal(view.SomeTextBox.Text, "2.0");

// test reverse bind no trigger required
view.SomeTextBox.Text = "3.0";
Assert.Equal(vm.JustADecimal, 3.0M);

view.SomeTextBox.Text = "4.0";
Assert.Equal(vm.JustADecimal, 4.0M);

// test forward bind to ensure trigger is still honoured.
vm.JustADecimal = 2.0M;
Assert.Equal(view.SomeTextBox.Text, "4.0");

update.OnNext(true);
Assert.Equal(view.SomeTextBox.Text, "2.0");

dis.Dispose();
Assert.True(dis.IsDisposed);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public void CommandBindByNameWireup()
var fixture = new CommandBinderImplementation();

var invokeCount = 0;
vm.Command1.Subscribe(_ => invokeCount += 1);
vm.Command1.Subscribe(_ => ++invokeCount);

var disp = fixture.BindCommand(vm, view, x => x.Command1, x => x.Command1);

Expand Down Expand Up @@ -53,7 +53,7 @@ public void CommandBindToExplicitEventWireup()
var fixture = new CommandBinderImplementation();

var invokeCount = 0;
vm.Command2.Subscribe(_ => invokeCount += 1);
vm.Command2.Subscribe(_ => ++invokeCount);

var disp = fixture.BindCommand(vm, view, x => x.Command2, x => x.Command2, "MouseUp");

Expand All @@ -64,5 +64,66 @@ public void CommandBindToExplicitEventWireup()
view.Command2.RaiseMouseUpEvent(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
Assert.Equal(1, invokeCount);
}

[Fact]
public void CommandBindByNameWireupWithParameter()
{
var vm = new WinformCommandBindViewModel();
var view = new WinformCommandBindView { ViewModel = vm };
ICommandBinderImplementation fixture = new CommandBinderImplementation();

var invokeCount = 0;
vm.Command3.Subscribe(_ => ++invokeCount);

var disp = CommandBinderImplementationMixins.BindCommand(fixture, vm, view, vm => vm.Command3, v => v.Command1, vm => vm.Parameter);

view.Command1.PerformClick();
Assert.Equal(1, invokeCount);
Assert.Equal(10, vm.ParameterResult);

// update the parameter to ensure its updated when the command is executed
vm.Parameter = 2;
view.Command1.PerformClick();
Assert.Equal(2, invokeCount);
Assert.Equal(20, vm.ParameterResult);

// break the Command3 subscription
var newCmd = ReactiveCommand.Create<int>(i => vm.ParameterResult = i * 2);
vm.Command3 = newCmd;

// ensure that the invoke count does not update and that the Command3 is now using the new math
view.Command1.PerformClick();
Assert.Equal(2, invokeCount);
Assert.Equal(4, vm.ParameterResult);

disp.Dispose();
}

[Fact]
public void CommandBindToExplicitEventWireupWithParameter()
{
var vm = new WinformCommandBindViewModel();
var view = new WinformCommandBindView { ViewModel = vm };
var fixture = new CommandBinderImplementation();

var invokeCount = 0;
vm.Command3.Subscribe(_ => ++invokeCount);

var disp = CommandBinderImplementationMixins.BindCommand(fixture, vm, view, x => x.Command3, x => x.Command2, vm => vm.Parameter, "MouseUp");

view.Command2.RaiseMouseUpEvent(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
Assert.Equal(10, vm.ParameterResult);
Assert.Equal(1, invokeCount);

vm.Parameter = 2;
view.Command2.RaiseMouseUpEvent(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
Assert.Equal(20, vm.ParameterResult);
Assert.Equal(2, invokeCount);

disp.Dispose();

view.Command2.RaiseMouseUpEvent(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
Assert.Equal(2, invokeCount);
}
}
}
Loading