Browse Source

Fix SelectedItemsControl and RangeBase Stackoverflow bugs.

pull/856/head
Jeremy Koritzinsky 9 years ago
parent
commit
440f2cafc5
  1. 26
      src/Avalonia.Base/AvaloniaObject.cs
  2. 44
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  3. 2
      tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs

26
src/Avalonia.Base/AvaloniaObject.cs

@ -54,7 +54,7 @@ namespace Avalonia
/// <summary>
/// Delayed setter helper for direct properties. Used to fix #855.
/// </summary>
protected readonly DelayedSetter<AvaloniaProperty, object> directDelayedSetter = new DelayedSetter<AvaloniaProperty, object>();
private readonly DelayedSetter<AvaloniaProperty, object> directDelayedSetter = new DelayedSetter<AvaloniaProperty, object>();
/// <summary>
@ -589,6 +589,30 @@ namespace Avalonia
}
}
protected void SetAndRaise<T>(AvaloniaProperty<T> property, Action<T, Action<Action>> setterCallback, T value, Action<T> delayedSet)
{
Contract.Requires<ArgumentNullException>(setterCallback != null);
Contract.Requires<ArgumentNullException>(delayedSet != null);
if (!directDelayedSetter.IsNotifying(property))
{
setterCallback(value, notification =>
{
using (directDelayedSetter.MarkNotifying(property))
{
notification();
}
});
if (directDelayedSetter.HasPendingSet(property))
{
delayedSet((T)directDelayedSetter.GetFirstPendingSet(property));
}
}
else
{
directDelayedSetter.AddPendingSet(property, value);
}
}
/// <summary>
/// Tries to cast a value to a type, taking into account that the value may be a
/// <see cref="BindingNotification"/>.

44
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -151,15 +151,18 @@ namespace Avalonia.Controls.Primitives
{
if (_updateCount == 0)
{
var old = SelectedIndex;
var effective = (value >= 0 && value < Items?.Cast<object>().Count()) ? value : -1;
if (old != effective)
SetAndRaise(SelectedIndexProperty, (val, notifierWrapper) =>
{
_selectedIndex = effective;
RaisePropertyChanged(SelectedIndexProperty, old, effective, BindingPriority.LocalValue);
SelectedItem = ElementAt(Items, effective);
}
var old = SelectedIndex;
var effective = (val >= 0 && val < Items?.Cast<object>().Count()) ? val : -1;
if (old != effective)
{
_selectedIndex = effective;
notifierWrapper(() => RaisePropertyChanged(SelectedIndexProperty, old, effective, BindingPriority.LocalValue));
SelectedItem = ElementAt(Items, effective);
}
}, value, val => SelectedIndex = val);
}
else
{
@ -183,19 +186,17 @@ namespace Avalonia.Controls.Primitives
{
if (_updateCount == 0)
{
if (!directDelayedSetter.IsNotifying(SelectedItemProperty))
SetAndRaise(SelectedItemProperty, (val, notifyWrapper) =>
{
var old = SelectedItem;
var index = IndexOf(Items, value);
var effective = index != -1 ? value : null;
var index = IndexOf(Items, val);
var effective = index != -1 ? val : null;
if (!object.Equals(effective, old))
{
_selectedItem = effective;
using (directDelayedSetter.MarkNotifying(SelectedItemProperty))
{
RaisePropertyChanged(SelectedItemProperty, old, effective, BindingPriority.LocalValue);
}
notifyWrapper(() => RaisePropertyChanged(SelectedItemProperty, old, effective, BindingPriority.LocalValue));
SelectedIndex = index;
@ -213,17 +214,8 @@ namespace Avalonia.Controls.Primitives
{
SelectedItems.Clear();
}
if (directDelayedSetter.HasPendingSet(SelectedItemProperty))
{
SelectedItem = directDelayedSetter.GetFirstPendingSet(SelectedItemProperty);
}
}
}
else
{
directDelayedSetter.AddPendingSet(SelectedItemProperty, value);
}
}
}, value, val => SelectedItem = val);
}
else
{

2
tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs

@ -151,7 +151,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
//here in real life stack overflow exception is thrown issue #855 and #824
target.Value = 51.001;
Assert.Equal(2, viewModel.SetterInvokedCount);
Assert.Equal(3, viewModel.SetterInvokedCount);
double expected = 51;

Loading…
Cancel
Save