Browse Source

Merge pull request #7892 from 0x0ade/recycledanchors

Fix IsRegisteredAsAnchorCandidate desync, mainly affecting recycled anchors
pull/8023/head
Steven Kirk 5 years ago
committed by GitHub
parent
commit
94780b5bc5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml
  2. 10
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  3. 13
      samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
  4. 8
      src/Avalonia.Controls/Repeater/ItemsRepeater.cs
  5. 29
      src/Avalonia.Controls/Repeater/ViewportManager.cs

2
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml

@ -53,10 +53,12 @@
<ComboBoxItem>UniformGrid - Horizontal</ComboBoxItem> <ComboBoxItem>UniformGrid - Horizontal</ComboBoxItem>
</ComboBox> </ComboBox>
<Button Command="{Binding AddItem}">Add Item</Button> <Button Command="{Binding AddItem}">Add Item</Button>
<Button Command="{Binding RemoveItem}">Remove Item</Button>
<Button Command="{Binding RandomizeHeights}">Randomize Heights</Button> <Button Command="{Binding RandomizeHeights}">Randomize Heights</Button>
<Button Command="{Binding ResetItems}">Reset items</Button> <Button Command="{Binding ResetItems}">Reset items</Button>
<Button x:Name="scrollToLast">Scroll to Last</Button> <Button x:Name="scrollToLast">Scroll to Last</Button>
<Button x:Name="scrollToRandom">Scroll to Random</Button> <Button x:Name="scrollToRandom">Scroll to Random</Button>
<Button x:Name="scrollToSelected">Scroll to Selected</Button>
</StackPanel> </StackPanel>
<Border BorderThickness="1" BorderBrush="{DynamicResource SystemControlHighlightBaseMediumLowBrush}" Margin="0 0 0 16"> <Border BorderThickness="1" BorderBrush="{DynamicResource SystemControlHighlightBaseMediumLowBrush}" Margin="0 0 0 16">
<ScrollViewer Name="scroller" <ScrollViewer Name="scroller"

10
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs

@ -15,8 +15,10 @@ namespace ControlCatalog.Pages
private readonly ItemsRepeaterPageViewModel _viewModel; private readonly ItemsRepeaterPageViewModel _viewModel;
private ItemsRepeater _repeater; private ItemsRepeater _repeater;
private ScrollViewer _scroller; private ScrollViewer _scroller;
private int _selectedIndex;
private Button _scrollToLast; private Button _scrollToLast;
private Button _scrollToRandom; private Button _scrollToRandom;
private Button _scrollToSelected;
private Random _random = new Random(0); private Random _random = new Random(0);
public ItemsRepeaterPage() public ItemsRepeaterPage()
@ -26,10 +28,12 @@ namespace ControlCatalog.Pages
_scroller = this.FindControl<ScrollViewer>("scroller"); _scroller = this.FindControl<ScrollViewer>("scroller");
_scrollToLast = this.FindControl<Button>("scrollToLast"); _scrollToLast = this.FindControl<Button>("scrollToLast");
_scrollToRandom = this.FindControl<Button>("scrollToRandom"); _scrollToRandom = this.FindControl<Button>("scrollToRandom");
_scrollToSelected = this.FindControl<Button>("scrollToSelected");
_repeater.PointerPressed += RepeaterClick; _repeater.PointerPressed += RepeaterClick;
_repeater.KeyDown += RepeaterOnKeyDown; _repeater.KeyDown += RepeaterOnKeyDown;
_scrollToLast.Click += scrollToLast_Click; _scrollToLast.Click += scrollToLast_Click;
_scrollToRandom.Click += scrollToRandom_Click; _scrollToRandom.Click += scrollToRandom_Click;
_scrollToSelected.Click += scrollToSelected_Click;
DataContext = _viewModel = new ItemsRepeaterPageViewModel(); DataContext = _viewModel = new ItemsRepeaterPageViewModel();
} }
@ -121,6 +125,7 @@ namespace ControlCatalog.Pages
{ {
var item = (e.Source as TextBlock)?.DataContext as ItemsRepeaterPageViewModel.Item; var item = (e.Source as TextBlock)?.DataContext as ItemsRepeaterPageViewModel.Item;
_viewModel.SelectedItem = item; _viewModel.SelectedItem = item;
_selectedIndex = _viewModel.Items.IndexOf(item);
} }
private void RepeaterOnKeyDown(object sender, KeyEventArgs e) private void RepeaterOnKeyDown(object sender, KeyEventArgs e)
@ -140,5 +145,10 @@ namespace ControlCatalog.Pages
{ {
ScrollTo(_random.Next(_viewModel.Items.Count - 1)); ScrollTo(_random.Next(_viewModel.Items.Count - 1));
} }
private void scrollToSelected_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{
ScrollTo(_selectedIndex);
}
} }
} }

13
samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs

@ -31,6 +31,19 @@ namespace ControlCatalog.ViewModels
Items.Insert(index + 1, new Item(index + 1) { Text = $"New Item {_newItemIndex++}" }); Items.Insert(index + 1, new Item(index + 1) { Text = $"New Item {_newItemIndex++}" });
} }
public void RemoveItem()
{
if (SelectedItem is not null)
{
Items.Remove(SelectedItem);
SelectedItem = null;
}
else if (Items.Count > 0)
{
Items.RemoveAt(Items.Count - 1);
}
}
public void RandomizeHeights() public void RandomizeHeights()
{ {
var random = new Random(); var random = new Random();

8
src/Avalonia.Controls/Repeater/ItemsRepeater.cs

@ -401,11 +401,7 @@ namespace Avalonia.Controls
var newBounds = element.Bounds; var newBounds = element.Bounds;
virtInfo.ArrangeBounds = newBounds; virtInfo.ArrangeBounds = newBounds;
if (!virtInfo.IsRegisteredAsAnchorCandidate) _viewportManager.RegisterScrollAnchorCandidate(element, virtInfo);
{
_viewportManager.RegisterScrollAnchorCandidate(element);
virtInfo.IsRegisteredAsAnchorCandidate = true;
}
} }
} }
@ -490,7 +486,7 @@ namespace Avalonia.Controls
_processingItemsSourceChange.Action == NotifyCollectionChangedAction.Reset); _processingItemsSourceChange.Action == NotifyCollectionChangedAction.Reset);
_viewManager.ClearElement(element, isClearedDueToCollectionChange); _viewManager.ClearElement(element, isClearedDueToCollectionChange);
_viewportManager.OnElementCleared(element); _viewportManager.OnElementCleared(element, GetVirtualizationInfo(element));
} }
private int GetElementIndexImpl(IControl element) private int GetElementIndexImpl(IControl element)

29
src/Avalonia.Controls/Repeater/ViewportManager.cs

@ -249,9 +249,10 @@ namespace Avalonia.Controls
virtInfo.IsRegisteredAsAnchorCandidate = false; virtInfo.IsRegisteredAsAnchorCandidate = false;
} }
public void OnElementCleared(IControl element) public void OnElementCleared(IControl element, VirtualizationInfo virtInfo)
{ {
_scroller?.UnregisterAnchorCandidate(element); _scroller?.UnregisterAnchorCandidate(element);
virtInfo.IsRegisteredAsAnchorCandidate = false;
} }
public void OnOwnerMeasuring() public void OnOwnerMeasuring()
@ -358,9 +359,12 @@ namespace Avalonia.Controls
{ {
foreach (var child in _owner.Children) foreach (var child in _owner.Children)
{ {
if (child != targetChild) var info = ItemsRepeater.GetVirtualizationInfo(child);
if (child != targetChild && info.IsRegisteredAsAnchorCandidate)
{ {
_scroller.UnregisterAnchorCandidate(child); _scroller.UnregisterAnchorCandidate(child);
info.IsRegisteredAsAnchorCandidate = false;
} }
} }
} }
@ -377,9 +381,13 @@ namespace Avalonia.Controls
} }
} }
public void RegisterScrollAnchorCandidate(IControl element) public void RegisterScrollAnchorCandidate(IControl element, VirtualizationInfo virtInfo)
{ {
_scroller?.RegisterAnchorCandidate(element); if (!virtInfo.IsRegisteredAsAnchorCandidate)
{
_scroller?.RegisterAnchorCandidate(element);
virtInfo.IsRegisteredAsAnchorCandidate = true;
}
} }
private IControl? GetImmediateChildOfRepeater(IControl descendant) private IControl? GetImmediateChildOfRepeater(IControl descendant)
@ -405,15 +413,18 @@ namespace Avalonia.Controls
_isBringIntoViewInProgress = false; _isBringIntoViewInProgress = false;
_makeAnchorElement = null; _makeAnchorElement = null;
// Undo the anchor deregistrations done by OnBringIntoViewRequested.
if (_scroller is object) if (_scroller is object)
{ {
foreach (var child in _owner.Children) foreach (var child in _owner.Children)
{ {
var info = ItemsRepeater.GetVirtualizationInfo(child); var info = ItemsRepeater.GetVirtualizationInfo(child);
if (info.IsRealized && info.IsHeldByLayout) // The item brought into view is still registered - don't register it more than once.
if (info.IsRealized && info.IsHeldByLayout && !info.IsRegisteredAsAnchorCandidate)
{ {
_scroller.RegisterAnchorCandidate(child); _scroller.RegisterAnchorCandidate(child);
info.IsRegisteredAsAnchorCandidate = true;
} }
} }
} }
@ -430,7 +441,13 @@ namespace Avalonia.Controls
{ {
foreach (var child in _owner.Children) foreach (var child in _owner.Children)
{ {
_scroller.UnregisterAnchorCandidate(child); var info = ItemsRepeater.GetVirtualizationInfo(child);
if (info.IsRegisteredAsAnchorCandidate)
{
_scroller.UnregisterAnchorCandidate(child);
info.IsRegisteredAsAnchorCandidate = false;
}
} }
_scroller = null; _scroller = null;

Loading…
Cancel
Save