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
4 changes: 4 additions & 0 deletions src/ReactiveUI.Blazor/Registrations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ public void Register(Action<Func<object>, Type> registerFunction)
throw new ArgumentNullException(nameof(registerFunction));
}

registerFunction(() => new StringConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new SingleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DoubleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DecimalToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new PlatformOperations(), typeof(IPlatformOperations));

if (Type.GetType("Mono.Runtime") is not null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,12 @@ namespace ReactiveUI
{
public SingleInstanceViewAttribute() { }
}
public class SingleToStringTypeConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger
{
public SingleToStringTypeConverter() { }
public int GetAffinityForObjects(System.Type fromType, System.Type toType) { }
public bool TryConvert(object? from, System.Type toType, object? conversionHint, out object result) { }
}
public class StringConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger
{
public StringConverter() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,12 @@ namespace ReactiveUI
{
public SingleInstanceViewAttribute() { }
}
public class SingleToStringTypeConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger
{
public SingleToStringTypeConverter() { }
public int GetAffinityForObjects(System.Type fromType, System.Type toType) { }
public bool TryConvert(object? from, System.Type toType, object? conversionHint, out object result) { }
}
public class StringConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger
{
public StringConverter() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,12 @@ namespace ReactiveUI
{
public SingleInstanceViewAttribute() { }
}
public class SingleToStringTypeConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger
{
public SingleToStringTypeConverter() { }
public int GetAffinityForObjects(System.Type fromType, System.Type toType) { }
public bool TryConvert(object? from, System.Type toType, object? conversionHint, out object result) { }
}
public class StringConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger
{
public StringConverter() { }
Expand Down
19 changes: 19 additions & 0 deletions src/ReactiveUI.Tests/Mocks/PropertyBindViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class PropertyBindViewModel : ReactiveObject
private string? _property1;
private PropertyBindModel? _model;
private int _property2;
private float _justASingle;
private double _justADouble;
private decimal _justADecimal;
private double? _nullableDouble;
Expand Down Expand Up @@ -100,12 +101,30 @@ public double? NullableDouble
set => this.RaiseAndSetIfChanged(ref _nullableDouble, value);
}

/// <summary>
/// Gets or sets the just a visibility.
/// </summary>
/// <value>
/// The just a visibility.
/// </value>
public Visibility JustAVisibility
{
get => _justAVisibility;
set => this.RaiseAndSetIfChanged(ref _justAVisibility, value);
}

/// <summary>
/// Gets or sets the just a single.
/// </summary>
/// <value>
/// The just a single.
/// </value>
public float JustASingle
{
get => _justASingle;
set => this.RaiseAndSetIfChanged(ref _justASingle, value);
}

/// <summary>
/// Gets some collection of strings.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ public void BindWithFuncToTriggerUpdateTestViewModelToView()
}

[Fact]
public void BindWithFuncToTriggerUpdateTestViewModelToViewWithConverter()
public void BindWithFuncToTriggerUpdateTestViewModelToViewWithDecimalConverter()
{
CompositeDisposable dis = new();
PropertyBindViewModel? vm = new();
Expand Down Expand Up @@ -787,5 +787,101 @@ public void BindWithFuncToTriggerUpdateTestViewToViewModel()
dis.Dispose();
Assert.True(dis.IsDisposed);
}

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

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

var doubleToStringTypeConverter = new DoubleToStringTypeConverter();

view.Bind(vm, x => x.JustADouble, x => x.SomeTextBox.Text, update.AsObservable(), 2, doubleToStringTypeConverter, doubleToStringTypeConverter, TriggerUpdate.ViewModelToView).DisposeWith(dis);

vm.JustADouble = 1.0;

// 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.00");

vm.JustADouble = 2.0;
Assert.Equal(view.SomeTextBox.Text, "1.00");

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

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

view.SomeTextBox.Text = "4.00";
Assert.Equal(vm.JustADouble, 4.0);

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

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

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

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

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

var singleToStringTypeConverter = new SingleToStringTypeConverter();

view.Bind(vm, x => x.JustASingle, x => x.SomeTextBox.Text, update.AsObservable(), 2, singleToStringTypeConverter, singleToStringTypeConverter, TriggerUpdate.ViewModelToView).DisposeWith(dis);

vm.JustASingle = 1.0f;

// 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.00");

vm.JustASingle = 2.0f;
Assert.Equal(view.SomeTextBox.Text, "1.00");

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

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

view.SomeTextBox.Text = "4.00";
Assert.Equal(vm.JustASingle, 4.0f);

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

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

dis.Dispose();
Assert.True(dis.IsDisposed);
}
}
}
4 changes: 4 additions & 0 deletions src/ReactiveUI.Uno/Registrations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public void Register(Action<Func<object>, Type> registerFunction)
registerFunction(() => new PlatformOperations(), typeof(IPlatformOperations));
registerFunction(() => new ActivationForViewFetcher(), typeof(IActivationForViewFetcher));
registerFunction(() => new DependencyObjectObservableForProperty(), typeof(ICreatesObservableForProperty));
registerFunction(() => new StringConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new SingleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DoubleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DecimalToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new BooleanToVisibilityTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook));

Expand Down
4 changes: 4 additions & 0 deletions src/ReactiveUI.Winforms/Registrations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ public void Register(Action<Func<object>, Type> registerFunction)
registerFunction(() => new ActivationForViewFetcher(), typeof(IActivationForViewFetcher));
registerFunction(() => new PanelSetMethodBindingConverter(), typeof(ISetMethodBindingConverter));
registerFunction(() => new TableContentSetMethodBindingConverter(), typeof(ISetMethodBindingConverter));
registerFunction(() => new StringConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new SingleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DoubleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DecimalToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new ComponentModelTypeConverter(), typeof(IBindingTypeConverter));

if (!ModeDetector.InUnitTestRunner())
Expand Down
4 changes: 4 additions & 0 deletions src/ReactiveUI.Wpf/Registrations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public void Register(Action<Func<object>, Type> registerFunction)

registerFunction(() => new ActivationForViewFetcher(), typeof(IActivationForViewFetcher));
registerFunction(() => new DependencyObjectObservableForProperty(), typeof(ICreatesObservableForProperty));
registerFunction(() => new StringConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new SingleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DoubleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DecimalToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new BooleanToVisibilityTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook));
registerFunction(() => new ComponentModelTypeConverter(), typeof(IBindingTypeConverter));
Expand Down
4 changes: 4 additions & 0 deletions src/ReactiveUI.XamForms/Registrations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public void Register(Action<Func<object>, Type> registerFunction)
throw new ArgumentNullException(nameof(registerFunction));
}

registerFunction(() => new StringConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new SingleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DoubleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DecimalToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new ActivationForViewFetcher(), typeof(IActivationForViewFetcher));
}
}
Expand Down
19 changes: 10 additions & 9 deletions src/ReactiveUI/Bindings/Converter/DecimalToStringTypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,19 @@ public bool TryConvert(object? from, Type toType, object? conversionHint, out ob

if (from is string fromString)
{
var outDecimal = decimal.Zero;
decimal.TryParse(fromString, out outDecimal);

if (conversionHint is int decimalHint)
var success = decimal.TryParse(fromString, out var outDecimal);
if (success)
{
result = Math.Round(outDecimal, decimalHint);
return true;
}
if (conversionHint is int decimalHint)
{
result = Math.Round(outDecimal, decimalHint);
return true;
}

result = outDecimal;
result = outDecimal;

return true;
return true;
}
}

result = null!;
Expand Down
19 changes: 10 additions & 9 deletions src/ReactiveUI/Bindings/Converter/DoubleToStringTypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,19 @@ public bool TryConvert(object? from, Type toType, object? conversionHint, out ob

if (from is string fromString)
{
var outDouble = double.NaN;
double.TryParse(fromString, out outDouble);

if (conversionHint is int doubleHint)
var success = double.TryParse(fromString, out var outDouble);
if (success)
{
result = Math.Round(outDouble, doubleHint);
return true;
}
if (conversionHint is int doubleHint)
{
result = Math.Round(outDouble, doubleHint);
return true;
}

result = outDouble;
result = outDouble;

return true;
return true;
}
}

result = null!;
Expand Down
68 changes: 68 additions & 0 deletions src/ReactiveUI/Bindings/Converter/SingleToStringTypeConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) 2021 .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 System;

namespace ReactiveUI
{
/// <summary>
/// Single To String Type Converter.
/// </summary>
/// <seealso cref="ReactiveUI.IBindingTypeConverter" />
public class SingleToStringTypeConverter : IBindingTypeConverter
{
/// <inheritdoc/>
public int GetAffinityForObjects(Type fromType, Type toType)
{
if (fromType == typeof(float) && toType == typeof(string))
{
return 10;
}

if (fromType == typeof(string) && toType == typeof(float))
{
return 10;
}

return 0;
}

/// <inheritdoc/>
public bool TryConvert(object? from, Type toType, object? conversionHint, out object result)
{
if (toType == typeof(string) && from is float fromSingle)
{
if (conversionHint is int singleHint)
{
result = fromSingle.ToString($"F{singleHint}");
return true;
}

result = fromSingle.ToString();
return true;
}

if (from is string fromString)
{
var success = float.TryParse(fromString, out var outSingle);
if (success)
{
if (conversionHint is int singleHint)
{
result = Convert.ToSingle(Math.Round(outSingle, singleHint));
return true;
}

result = outSingle;

return true;
}
}

result = null!;
return false;
}
}
}
2 changes: 0 additions & 2 deletions src/ReactiveUI/Platforms/uap/PlatformRegistrations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ public void Register(Action<Func<object>, Type> registerFunction)
registerFunction(() => new ActivationForViewFetcher(), typeof(IActivationForViewFetcher));
registerFunction(() => new DependencyObjectObservableForProperty(), typeof(ICreatesObservableForProperty));
registerFunction(() => new BooleanToVisibilityTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DoubleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DecimalToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook));

if (!ModeDetector.InUnitTestRunner())
Expand Down
3 changes: 3 additions & 0 deletions src/ReactiveUI/Registration/Registrations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ public void Register(Action<Func<object>, Type> registerFunction)
registerFunction(() => new POCOObservableForProperty(), typeof(ICreatesObservableForProperty));
registerFunction(() => new EqualityTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new StringConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new SingleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DoubleToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DecimalToStringTypeConverter(), typeof(IBindingTypeConverter));
registerFunction(() => new DefaultViewLocator(), typeof(IViewLocator));
registerFunction(() => new CanActivateViewFetcher(), typeof(IActivationForViewFetcher));
registerFunction(() => new CreatesCommandBindingViaEvent(), typeof(ICreatesCommandBinding));
Expand Down