|
|
|
@ -3,10 +3,11 @@ |
|
|
|
|
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.ComponentModel; |
|
|
|
using System.Reactive.Subjects; |
|
|
|
using Avalonia; |
|
|
|
using Avalonia.Data; |
|
|
|
using Avalonia.Logging; |
|
|
|
using Avalonia.Markup.Xaml.Data; |
|
|
|
using Avalonia.UnitTests; |
|
|
|
using Xunit; |
|
|
|
|
|
|
|
@ -208,7 +209,7 @@ namespace Avalonia.Base.UnitTests |
|
|
|
{ |
|
|
|
var target = new Class1(); |
|
|
|
|
|
|
|
Assert.Throws<ArgumentException>(() => |
|
|
|
Assert.Throws<ArgumentException>(() => |
|
|
|
target.SetValue(Class1.BarProperty, "newvalue")); |
|
|
|
} |
|
|
|
|
|
|
|
@ -217,7 +218,7 @@ namespace Avalonia.Base.UnitTests |
|
|
|
{ |
|
|
|
var target = new Class1(); |
|
|
|
|
|
|
|
Assert.Throws<ArgumentException>(() => |
|
|
|
Assert.Throws<ArgumentException>(() => |
|
|
|
target.SetValue((AvaloniaProperty)Class1.BarProperty, "newvalue")); |
|
|
|
} |
|
|
|
|
|
|
|
@ -227,7 +228,7 @@ namespace Avalonia.Base.UnitTests |
|
|
|
var target = new Class1(); |
|
|
|
var source = new Subject<string>(); |
|
|
|
|
|
|
|
Assert.Throws<ArgumentException>(() => |
|
|
|
Assert.Throws<ArgumentException>(() => |
|
|
|
target.Bind(Class1.BarProperty, source)); |
|
|
|
} |
|
|
|
|
|
|
|
@ -439,12 +440,49 @@ namespace Avalonia.Base.UnitTests |
|
|
|
Assert.Equal(BindingMode.OneWayToSource, bar.GetMetadata<Class2>().DefaultBindingMode); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void SetValue_Should_Not_Cause_StackOverflow_And_Have_Correct_Values() |
|
|
|
{ |
|
|
|
var viewModel = new TestStackOverflowViewModel() |
|
|
|
{ |
|
|
|
Value = 50 |
|
|
|
}; |
|
|
|
|
|
|
|
var target = new Class1(); |
|
|
|
|
|
|
|
//note: if the initialization of the child binding is here there is no stackoverflow!!!
|
|
|
|
//var child = new Class1()
|
|
|
|
//{
|
|
|
|
// [~~Class1.DoubleValueProperty] = target[~~Class1.DoubleValueProperty]
|
|
|
|
//};
|
|
|
|
|
|
|
|
target.Bind(Class1.DoubleValueProperty, new Binding("Value") { Mode = BindingMode.TwoWay, Source = viewModel }); |
|
|
|
|
|
|
|
var child = new Class1() |
|
|
|
{ |
|
|
|
[~~Class1.DoubleValueProperty] = target[~~Class1.DoubleValueProperty] |
|
|
|
}; |
|
|
|
|
|
|
|
Assert.Equal(1, viewModel.SetterInvokedCount); |
|
|
|
|
|
|
|
//here in real life stack overflow exception is thrown issue #855 and #824
|
|
|
|
target.DoubleValue = 51.001; |
|
|
|
|
|
|
|
Assert.Equal(2, viewModel.SetterInvokedCount); |
|
|
|
|
|
|
|
double expected = 51; |
|
|
|
|
|
|
|
Assert.Equal(expected, viewModel.Value); |
|
|
|
Assert.Equal(expected, target.DoubleValue); |
|
|
|
Assert.Equal(expected, child.DoubleValue); |
|
|
|
} |
|
|
|
|
|
|
|
private class Class1 : AvaloniaObject |
|
|
|
{ |
|
|
|
public static readonly DirectProperty<Class1, string> FooProperty = |
|
|
|
AvaloniaProperty.RegisterDirect<Class1, string>( |
|
|
|
"Foo", |
|
|
|
o => o.Foo, |
|
|
|
"Foo", |
|
|
|
o => o.Foo, |
|
|
|
(o, v) => o.Foo = v, |
|
|
|
unsetValue: "unset"); |
|
|
|
|
|
|
|
@ -453,14 +491,21 @@ namespace Avalonia.Base.UnitTests |
|
|
|
|
|
|
|
public static readonly DirectProperty<Class1, int> BazProperty = |
|
|
|
AvaloniaProperty.RegisterDirect<Class1, int>( |
|
|
|
"Bar", |
|
|
|
o => o.Baz, |
|
|
|
(o,v) => o.Baz = v, |
|
|
|
"Bar", |
|
|
|
o => o.Baz, |
|
|
|
(o, v) => o.Baz = v, |
|
|
|
unsetValue: -1); |
|
|
|
|
|
|
|
public static readonly DirectProperty<Class1, double> DoubleValueProperty = |
|
|
|
AvaloniaProperty.RegisterDirect<Class1, double>( |
|
|
|
nameof(DoubleValue), |
|
|
|
o => o.DoubleValue, |
|
|
|
(o, v) => o.DoubleValue = v); |
|
|
|
|
|
|
|
private string _foo = "initial"; |
|
|
|
private readonly string _bar = "bar"; |
|
|
|
private int _baz = 5; |
|
|
|
private double _doubleValue; |
|
|
|
|
|
|
|
public string Foo |
|
|
|
{ |
|
|
|
@ -478,6 +523,12 @@ namespace Avalonia.Base.UnitTests |
|
|
|
get { return _baz; } |
|
|
|
set { SetAndRaise(BazProperty, ref _baz, value); } |
|
|
|
} |
|
|
|
|
|
|
|
public double DoubleValue |
|
|
|
{ |
|
|
|
get { return _doubleValue; } |
|
|
|
set { SetAndRaise(DoubleValueProperty, ref _doubleValue, value); } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private class Class2 : AvaloniaObject |
|
|
|
@ -497,5 +548,40 @@ namespace Avalonia.Base.UnitTests |
|
|
|
set { SetAndRaise(FooProperty, ref _foo, value); } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private class TestStackOverflowViewModel : INotifyPropertyChanged |
|
|
|
{ |
|
|
|
public int SetterInvokedCount { get; private set; } |
|
|
|
|
|
|
|
public const int MaxInvokedCount = 1000; |
|
|
|
|
|
|
|
private double _value; |
|
|
|
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged; |
|
|
|
|
|
|
|
public double Value |
|
|
|
{ |
|
|
|
get { return _value; } |
|
|
|
set |
|
|
|
{ |
|
|
|
if (_value != value) |
|
|
|
{ |
|
|
|
SetterInvokedCount++; |
|
|
|
if (SetterInvokedCount < MaxInvokedCount) |
|
|
|
{ |
|
|
|
_value = (int)value; |
|
|
|
if (_value > 75) _value = 75; |
|
|
|
if (_value < 25) _value = 25; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
_value = value; |
|
|
|
} |
|
|
|
|
|
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value))); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |