Browse Source

Allow binding container IsVisible with virtualization. (#14276)

Use `SetCurrentValue` to update the container `IsVisible` state when realizing and recycling so that the visible state can be bound.

Fixes #13991
release/11.0.8
Steven Kirk 2 years ago
committed by Max Katz
parent
commit
1ccd563854
  1. 10
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  2. 97
      tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs

10
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -629,7 +629,7 @@ namespace Avalonia.Controls
generator.ItemContainerPrepared(controlItem, item, index);
}
controlItem.IsVisible = true;
controlItem.SetCurrentValue(Visual.IsVisibleProperty, true);
return controlItem;
}
@ -645,7 +645,7 @@ namespace Avalonia.Controls
if (_recyclePool?.TryGetValue(recycleKey, out var recyclePool) == true && recyclePool.Count > 0)
{
var recycled = recyclePool.Pop();
recycled.IsVisible = true;
recycled.SetCurrentValue(Visual.IsVisibleProperty, true);
generator.PrepareItemContainer(recycled, item, index);
generator.ItemContainerPrepared(recycled, item, index);
return recycled;
@ -684,7 +684,7 @@ namespace Avalonia.Controls
}
else if (recycleKey == s_itemIsItsOwnContainer)
{
element.IsVisible = false;
element.SetCurrentValue(Visual.IsVisibleProperty, false);
}
else if (KeyboardNavigation.GetTabOnceActiveElement(ItemsControl) == element)
{
@ -695,7 +695,7 @@ namespace Avalonia.Controls
{
ItemContainerGenerator!.ClearItemContainer(element);
PushToRecyclePool(recycleKey, element);
element.IsVisible = false;
element.SetCurrentValue(Visual.IsVisibleProperty, false);
}
}
@ -713,7 +713,7 @@ namespace Avalonia.Controls
{
ItemContainerGenerator!.ClearItemContainer(element);
PushToRecyclePool(recycleKey, element);
element.IsVisible = false;
element.SetCurrentValue(Visual.IsVisibleProperty, false);
}
}

97
tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs

@ -1048,6 +1048,74 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new(0, 200), scroll.Offset);
}
[Fact]
public void Can_Bind_Item_IsVisible()
{
using var app = App();
var style = CreateIsVisibleBindingStyle();
var items = Enumerable.Range(0, 100).Select(x => new ItemWithIsVisible(x)).ToList();
var (target, scroll, itemsControl) = CreateTarget(items: items, styles: new[] { style });
var container = target.ContainerFromIndex(2)!;
Assert.True(container.IsVisible);
Assert.Equal(20, container.Bounds.Top);
items[2].IsVisible = false;
Layout(target);
Assert.False(container.IsVisible);
// Next container should be in correct position.
Assert.Equal(20, target.ContainerFromIndex(3)!.Bounds.Top);
}
[Fact]
public void IsVisible_Binding_Persists_After_Scrolling()
{
using var app = App();
var style = CreateIsVisibleBindingStyle();
var items = Enumerable.Range(0, 100).Select(x => new ItemWithIsVisible(x)).ToList();
var (target, scroll, itemsControl) = CreateTarget(items: items, styles: new[] { style });
var container = target.ContainerFromIndex(2)!;
Assert.True(container.IsVisible);
Assert.Equal(20, container.Bounds.Top);
items[2].IsVisible = false;
scroll.Offset = new Vector(0, 200);
Layout(target);
scroll.Offset = new Vector(0, 0);
Layout(target);
container = target.ContainerFromIndex(2)!;
Assert.False(container.IsVisible);
}
[Fact]
public void Recycling_A_Hidden_Control_Shows_It()
{
using var app = App();
var style = CreateIsVisibleBindingStyle();
var items = Enumerable.Range(0, 3).Select(x => new ItemWithIsVisible(x)).ToList();
var (target, scroll, itemsControl) = CreateTarget(items: items, styles: new[] { style });
var container = target.ContainerFromIndex(2)!;
Assert.True(container.IsVisible);
Assert.Equal(20, container.Bounds.Top);
items[2].IsVisible = false;
Layout(target);
Assert.False(container.IsVisible);
items.RemoveAt(2);
items.Add(new ItemWithIsVisible(3));
Layout(target);
Assert.True(container.IsVisible);
}
private static IReadOnlyList<int> GetRealizedIndexes(VirtualizingStackPanel target, ItemsControl itemsControl)
{
return target.GetRealizedElements()
@ -1173,6 +1241,17 @@ namespace Avalonia.Controls.UnitTests
return root;
}
private static Style CreateIsVisibleBindingStyle()
{
return new Style(x => x.OfType<ContentPresenter>())
{
Setters =
{
new Setter(Visual.IsVisibleProperty, new Binding("IsVisible")),
}
};
}
private static IDataTemplate DefaultItemTemplate()
{
return new FuncDataTemplate<object>((x, _) => new Canvas { Width = 100, Height = 10 });
@ -1218,6 +1297,24 @@ namespace Avalonia.Controls.UnitTests
public double Height { get; set; }
}
private class ItemWithIsVisible : NotifyingBase
{
private bool _isVisible = true;
public ItemWithIsVisible(int index)
{
Caption = $"Item {index}";
}
public string Caption { get; set; }
public bool IsVisible
{
get => _isVisible;
set => SetField(ref _isVisible, value);
}
}
private class ResettingCollection : List<string>, INotifyCollectionChanged
{
public ResettingCollection(IEnumerable<string> items)

Loading…
Cancel
Save