Skip to content

Commit

Permalink
Finish ScrollTo implementations for CollectionView on UWP (xamarin#7509
Browse files Browse the repository at this point in the history
…) partially implements xamarin#3172

* Finish ScrollTo implementations for CollectionView on UWP

* Fix NRE when attempting to scroll to index greater than source length
  • Loading branch information
hartez authored and felipebaltazar committed Oct 16, 2019
1 parent 60d6474 commit 94a7ff9
Show file tree
Hide file tree
Showing 5 changed files with 396 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ internal void Realize()
var formsTemplate = FormsDataTemplate;
var container = FormsContainer;

var itemsView = container as ItemsView;

if (itemsView != null && _renderer?.Element != null)
{
itemsView.RemoveLogicalChild(_renderer.Element);
}

if (dataContext == null || formsTemplate == null || container == null)
{
return;
Expand All @@ -131,7 +138,7 @@ internal void Realize()

Content = _renderer.ContainerElement;

// TODO ezhart Add View as a logical child of the ItemsView
itemsView?.AddLogicalChild(view);

BindableObject.SetInheritedBindingContext(_renderer.Element, dataContext);
}
Expand Down
179 changes: 45 additions & 134 deletions Xamarin.Forms.Platform.UAP/CollectionView/ItemsViewRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,137 +199,6 @@ protected virtual void TearDownOldElement(ItemsView oldElement)
oldElement.ScrollToRequested -= ScrollToRequested;
}

async void ScrollToRequested(object sender, ScrollToRequestEventArgs args)
{
await ScrollTo(args);
}

object FindBoundItem(ScrollToRequestEventArgs args)
{
if (args.Mode == ScrollToMode.Position)
{
return _collectionViewSource.View[args.Index];
}

if (Element.ItemTemplate == null)
{
return args.Item;
}

for (int n = 0; n < _collectionViewSource.View.Count; n++)
{
if (_collectionViewSource.View[n] is ItemTemplateContext pair)
{
if (pair.Item == args.Item)
{
return _collectionViewSource.View[n];
}
}
}

return null;
}

async Task JumpTo(ListViewBase list, object targetItem, ScrollToPosition scrollToPosition)
{
var tcs = new TaskCompletionSource<object>();
void ViewChanged(object s, ScrollViewerViewChangedEventArgs e) => tcs.TrySetResult(null);
var scrollViewer = list.GetFirstDescendant<ScrollViewer>();

try
{
scrollViewer.ViewChanged += ViewChanged;

if (scrollToPosition == ScrollToPosition.Start)
{
list.ScrollIntoView(targetItem, ScrollIntoViewAlignment.Leading);
}
else if (scrollToPosition == ScrollToPosition.MakeVisible)
{
list.ScrollIntoView(targetItem, ScrollIntoViewAlignment.Default);
}
else
{
// Center and End are going to be more complicated.
}

await tcs.Task;
}
finally
{
scrollViewer.ViewChanged -= ViewChanged;
}

}

async Task ChangeViewAsync(ScrollViewer scrollViewer, double? horizontalOffset, double? verticalOffset, bool disableAnimation)
{
var tcs = new TaskCompletionSource<object>();
void ViewChanged(object s, ScrollViewerViewChangedEventArgs e) => tcs.TrySetResult(null);

try
{
scrollViewer.ViewChanged += ViewChanged;
scrollViewer.ChangeView(horizontalOffset, verticalOffset, null, disableAnimation);
await tcs.Task;
}
finally
{
scrollViewer.ViewChanged -= ViewChanged;
}
}

async Task AnimateTo(ListViewBase list, object targetItem, ScrollToPosition scrollToPosition)
{
var scrollViewer = list.GetFirstDescendant<ScrollViewer>();

var targetContainer = list.ContainerFromItem(targetItem) as UIElement;

if (targetContainer == null)
{
var horizontalOffset = scrollViewer.HorizontalOffset;
var verticalOffset = scrollViewer.VerticalOffset;

await JumpTo(list, targetItem, scrollToPosition);
targetContainer = list.ContainerFromItem(targetItem) as UIElement;
await ChangeViewAsync(scrollViewer, horizontalOffset, verticalOffset, true);
}

if (targetContainer == null)
{
// Did not find the target item anywhere
return;
}

// TODO hartez 2018/10/04 16:37:35 Okay, this sort of works for vertical lists but fails totally on horizontal lists.
var transform = targetContainer.TransformToVisual(scrollViewer.Content as UIElement);
var position = transform?.TransformPoint(new Windows.Foundation.Point(0, 0));

if (!position.HasValue)
{
return;
}

// TODO hartez 2018/10/05 17:23:23 The animated scroll works fine vertically if we are scrolling to a greater Y offset.
// If we're scrolling back up to a lower Y offset, it just gives up and sends us to 0 (first item)
// Works fine if we disable animation, but that's not very helpful

scrollViewer.ChangeView(position.Value.X, position.Value.Y, null, false);

//if (scrollToPosition == ScrollToPosition.End)
//{
// // Modify position
//}
//else if (scrollToPosition == ScrollToPosition.Center)
//{
// // Modify position
//}
//else
//{

//}
}

void UpdateVerticalScrollBarVisibility()
{
if (_defaultVerticalScrollVisibility == null)
Expand Down Expand Up @@ -375,18 +244,60 @@ protected virtual async Task ScrollTo(ScrollToRequestEventArgs args)
return;
}

var targetItem = FindBoundItem(args);
var item = FindBoundItem(args);

if (item == null)
{
// Item wasn't found in the list, so there's nothing to scroll to
return;
}

if (args.IsAnimated)
{
await AnimateTo(list, targetItem, args.ScrollToPosition);
await ScrollHelpers.AnimateToItemAsync(list, item, args.ScrollToPosition);
}
else
{
await JumpTo(list, targetItem, args.ScrollToPosition);
await ScrollHelpers.JumpToItemAsync(list, item, args.ScrollToPosition);
}
}

async void ScrollToRequested(object sender, ScrollToRequestEventArgs args)
{
await ScrollTo(args);
}

object FindBoundItem(ScrollToRequestEventArgs args)
{
if (args.Mode == ScrollToMode.Position)
{
if (args.Index >= _collectionViewSource.View.Count)
{
return null;
}

return _collectionViewSource.View[args.Index];
}

if (Element.ItemTemplate == null)
{
return args.Item;
}

for (int n = 0; n < _collectionViewSource.View.Count; n++)
{
if (_collectionViewSource.View[n] is ItemTemplateContext pair)
{
if (pair.Item == args.Item)
{
return _collectionViewSource.View[n];
}
}
}

return null;
}

protected virtual void UpdateEmptyView()
{
if (Element == null || ListViewBase == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Xamarin.Forms.Platform.UWP">

<ItemsPanelTemplate x:Key="HorizontalListItemsPanel">
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="HorizontalListItemsPanel">
<ItemsStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>

<ItemsPanelTemplate x:Key="HorizontalGridItemsPanel">
<!-- Yes, this is counterintuitive. Orientation here means "direction we lay out the items until we hit the
Expand Down

0 comments on commit 94a7ff9

Please sign in to comment.