Browse Source

Fix exception when `SelectedValueBinding` evaluates to null (#14171)

* Add an item with null values to the SelectingItemsControl test data

* Fixed NullReferenceException when SelectedValueBinding evulates to null
Fixed SelectedValue bindings being overwritten when a new item is selected
release/11.0.7
Tom Edwards 3 years ago
committed by Max Katz
parent
commit
07eb5d8daa
  1. 12
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  2. 39
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_SelectedValue.cs

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

@ -690,7 +690,7 @@ namespace Avalonia.Controls.Primitives
if (value is null) if (value is null)
{ {
// Clearing SelectedValueBinding makes the SelectedValue the item itself // Clearing SelectedValueBinding makes the SelectedValue the item itself
SelectedValue = SelectedItem; SetCurrentValue(SelectedValueProperty, SelectedItem);
return; return;
} }
@ -710,7 +710,7 @@ namespace Avalonia.Controls.Primitives
} }
// Re-evaluate SelectedValue with the new binding // Re-evaluate SelectedValue with the new binding
SelectedValue = _bindingHelper.Evaluate(selectedItem); SetCurrentValue(SelectedValueProperty, _bindingHelper.Evaluate(selectedItem));
} }
finally finally
{ {
@ -1080,7 +1080,7 @@ namespace Avalonia.Controls.Primitives
{ {
var itemValue = _bindingHelper.Evaluate(item); var itemValue = _bindingHelper.Evaluate(item);
if (itemValue.Equals(value)) if (Equals(itemValue, value))
{ {
return item; return item;
} }
@ -1103,7 +1103,7 @@ namespace Avalonia.Controls.Primitives
try try
{ {
_isSelectionChangeActive = true; _isSelectionChangeActive = true;
SelectedValue = item; SetCurrentValue(SelectedValueProperty, item);
} }
finally finally
{ {
@ -1117,7 +1117,7 @@ namespace Avalonia.Controls.Primitives
try try
{ {
_isSelectionChangeActive = true; _isSelectionChangeActive = true;
SelectedValue = _bindingHelper.Evaluate(item); SetCurrentValue(SelectedValueProperty, _bindingHelper.Evaluate(item));
} }
finally finally
{ {
@ -1381,7 +1381,7 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<object> ValueProperty = public static readonly StyledProperty<object> ValueProperty =
AvaloniaProperty.Register<BindingHelper, object>("Value"); AvaloniaProperty.Register<BindingHelper, object>("Value");
public object Evaluate(object? dataContext) public object? Evaluate(object? dataContext)
{ {
// Only update the DataContext if necessary // Only update the DataContext if necessary
if (!Equals(dataContext, DataContext)) if (!Equals(dataContext, DataContext))

39
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_SelectedValue.cs

@ -26,9 +26,9 @@ namespace Avalonia.Controls.UnitTests.Primitives
Template = Template() Template = Template()
}; };
sic.SelectedItem = items[0]; sic.SelectedItem = items[1];
Assert.Equal(items[0].Name, sic.SelectedValue); Assert.Equal(items[1].Name, sic.SelectedValue);
} }
[Fact] [Fact]
@ -42,9 +42,9 @@ namespace Avalonia.Controls.UnitTests.Primitives
Template = Template() Template = Template()
}; };
sic.SelectedIndex = 0; sic.SelectedIndex = 1;
Assert.Equal(items[0].Name, sic.SelectedValue); Assert.Equal(items[1].Name, sic.SelectedValue);
} }
[Fact] [Fact]
@ -60,14 +60,14 @@ namespace Avalonia.Controls.UnitTests.Primitives
sic.SelectedItems = new List<TestClass> sic.SelectedItems = new List<TestClass>
{ {
items[1], items[2],
items[3], items[4],
items[4] items[5]
}; };
// When interacting, SelectedItem is the first item in the SelectedItems collection // When interacting, SelectedItem is the first item in the SelectedItems collection
// But when set here, it's the last // But when set here, it's the last
Assert.Equal(items[4].Name, sic.SelectedValue); Assert.Equal(items[5].Name, sic.SelectedValue);
} }
[Fact] [Fact]
@ -85,9 +85,9 @@ namespace Avalonia.Controls.UnitTests.Primitives
Prepare(sic); Prepare(sic);
sic.SelectedValue = items[1].Name; sic.SelectedValue = items[2].Name;
Assert.Equal(1, sic.SelectedIndex); Assert.Equal(2, sic.SelectedIndex);
} }
} }
@ -108,7 +108,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
sic.SelectedValue = "Item2"; sic.SelectedValue = "Item2";
Assert.Equal(items[1], sic.SelectedItem); Assert.Equal(items[2], sic.SelectedItem);
} }
} }
@ -130,7 +130,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
sic.SelectedValueBinding = new Binding("AltProperty"); sic.SelectedValueBinding = new Binding("AltProperty");
// Ensure SelectedItem didn't change // Ensure SelectedItem didn't change
Assert.Equal(items[1], sic.SelectedItem); Assert.Equal(items[2], sic.SelectedItem);
Assert.Equal("Alt2", sic.SelectedValue); Assert.Equal("Alt2", sic.SelectedValue);
@ -147,9 +147,9 @@ namespace Avalonia.Controls.UnitTests.Primitives
Template = Template() Template = Template()
}; };
sic.SelectedIndex = 0; sic.SelectedIndex = 1;
Assert.Equal(items[0], sic.SelectedValue); Assert.Equal(items[1], sic.SelectedValue);
} }
[Fact] [Fact]
@ -167,7 +167,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
sic.BeginInit(); sic.BeginInit();
sic.EndInit(); sic.EndInit();
Assert.Equal(items[1].Name, sic.SelectedValue); Assert.Equal(items[2].Name, sic.SelectedValue);
} }
[Fact] [Fact]
@ -186,7 +186,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
sic.SelectedValue = "Item1"; sic.SelectedValue = "Item1";
sic.EndInit(); sic.EndInit();
Assert.Equal(items[0].Name, sic.SelectedValue); Assert.Equal(items[1].Name, sic.SelectedValue);
} }
[Fact] [Fact]
@ -234,7 +234,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
var called = false; var called = false;
sic.SelectionChanged += (s, e) => sic.SelectionChanged += (s, e) =>
{ {
Assert.Same(items[1], e.AddedItems.Cast<object>().Single()); Assert.Same(items[2], e.AddedItems.Cast<object>().Single());
Assert.Empty(e.RemovedItems); Assert.Empty(e.RemovedItems);
called = true; called = true;
}; };
@ -259,7 +259,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
var called = false; var called = false;
sic.SelectionChanged += (s, e) => sic.SelectionChanged += (s, e) =>
{ {
Assert.Same(items[1], e.RemovedItems.Cast<object>().Single()); Assert.Same(items[2], e.RemovedItems.Cast<object>().Single());
Assert.Empty(e.AddedItems); Assert.Empty(e.AddedItems);
called = true; called = true;
}; };
@ -276,7 +276,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
var sic = new SelectingItemsControl var sic = new SelectingItemsControl
{ {
ItemsSource = items, ItemsSource = items,
SelectedIndex = 0, SelectedIndex = 1,
SelectedValueBinding = new Binding("Name"), SelectedValueBinding = new Binding("Name"),
Template = Template() Template = Template()
}; };
@ -333,6 +333,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
{ {
return new List<TestClass> return new List<TestClass>
{ {
new TestClass(null, null),
new TestClass("Item1", "Alt1"), new TestClass("Item1", "Alt1"),
new TestClass("Item2", "Alt2"), new TestClass("Item2", "Alt2"),
new TestClass("Item3", "Alt3"), new TestClass("Item3", "Alt3"),

Loading…
Cancel
Save