|
|
|
@ -2,7 +2,11 @@ |
|
|
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
|
|
|
|
|
|
using System; |
|
|
|
using System.ComponentModel; |
|
|
|
using Avalonia.Controls.Primitives; |
|
|
|
using Avalonia.Controls.Templates; |
|
|
|
using Avalonia.Data; |
|
|
|
using Avalonia.Markup.Xaml.Data; |
|
|
|
using Xunit; |
|
|
|
|
|
|
|
namespace Avalonia.Controls.UnitTests.Primitives |
|
|
|
@ -87,8 +91,93 @@ namespace Avalonia.Controls.UnitTests.Primitives |
|
|
|
Assert.Throws<ArgumentException>(() => target.Value = double.NegativeInfinity); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void SetValue_Should_Not_Cause_StackOverflow() |
|
|
|
{ |
|
|
|
var viewModel = new TestStackOverflowViewModel() |
|
|
|
{ |
|
|
|
Value = 50 |
|
|
|
}; |
|
|
|
|
|
|
|
Track track = null; |
|
|
|
|
|
|
|
var target = new TestRange() |
|
|
|
{ |
|
|
|
Template = new FuncControlTemplate<RangeBase>(c => |
|
|
|
{ |
|
|
|
return track = new Track() |
|
|
|
{ |
|
|
|
Width = 100, |
|
|
|
Orientation = Orientation.Horizontal, |
|
|
|
[~~Track.MinimumProperty] = c[~~RangeBase.MinimumProperty], |
|
|
|
[~~Track.MaximumProperty] = c[~~RangeBase.MaximumProperty], |
|
|
|
[~~Track.ValueProperty] = c[~~RangeBase.ValueProperty], |
|
|
|
Name = "PART_Track", |
|
|
|
Thumb = new Thumb() |
|
|
|
}; |
|
|
|
}), |
|
|
|
Minimum = 0, |
|
|
|
Maximum = 100, |
|
|
|
DataContext = viewModel |
|
|
|
}; |
|
|
|
|
|
|
|
target.Bind(TestRange.ValueProperty, new Binding("Value") { Mode = BindingMode.TwoWay }); |
|
|
|
|
|
|
|
target.ApplyTemplate(); |
|
|
|
track.Measure(new Size(100, 0)); |
|
|
|
track.Arrange(new Rect(0, 0, 100, 0)); |
|
|
|
|
|
|
|
Assert.Equal(1, viewModel.SetterInvokedCount); |
|
|
|
|
|
|
|
//here in real life stack overflow exception is thrown issue #855 and #824
|
|
|
|
target.Value = 51.001; |
|
|
|
|
|
|
|
Assert.Equal(2, viewModel.SetterInvokedCount); |
|
|
|
|
|
|
|
double expected = 51; |
|
|
|
|
|
|
|
Assert.Equal(expected, viewModel.Value); |
|
|
|
Assert.Equal(expected, target.Value); |
|
|
|
Assert.Equal(expected, track.Value); |
|
|
|
} |
|
|
|
|
|
|
|
private class TestRange : RangeBase |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
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))); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |