private ItemsRepeater SetupRepeater(CustomItemsSource dataSource, ElementFactory elementFactory, ref ScrollViewer scrollViewer, VirtualizingLayout layout) { var repeater = new ItemsRepeater() { ItemsSource = dataSource, ItemTemplate = elementFactory, Layout = layout, VerticalCacheLength = 0, HorizontalCacheLength = 0 }; scrollViewer = new ScrollViewer { Content = repeater }; Content = new ItemsRepeaterScrollHost() { Width = 200, Height = 200, ScrollViewer = scrollViewer }; Content.UpdateLayout(); if (dataSource.Count > 0) { int realized = VerifyRealizedRange(repeater, dataSource); Verify.IsGreaterThan(realized, 0); } return(repeater); }
private ItemsRepeater SetupRepeater(object dataSource, VirtualizingLayout layout, string itemContent, out ScrollViewer scrollViewer) { ItemsRepeater repeater = null; ScrollViewer sv = null; RunOnUIThread.Execute(() => { var elementFactory = new RecyclingElementFactory(); elementFactory.RecyclePool = new RecyclePool(); elementFactory.Templates["Item"] = (DataTemplate)XamlReader.Parse( @"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'> " + itemContent + @"</DataTemplate>"); repeater = new ItemsRepeater() { ItemsSource = dataSource, ItemTemplate = elementFactory, Layout = layout, HorizontalCacheLength = 0.0, VerticalCacheLength = 0.0 }; sv = new ScrollViewer { Content = repeater }; Content = new ItemsRepeaterScrollHost() { Width = 200, Height = 200, ScrollViewer = sv }; }); IdleSynchronizer.Wait(); scrollViewer = sv; return(repeater); }
public void ValidateRecycledElementOwnerAffinity() { RunOnUIThread.Execute(() => { ItemsRepeater repeater1 = null; ItemsRepeater repeater2 = null; const int numItems = 10; var dataCollection = new ObservableCollection <int>(Enumerable.Range(0, numItems)); const string recycleKey = "key"; var dataSource = MockItemsSource.CreateDataSource <int>(dataCollection, true); var layout = new StackLayout(); var recyclePool = new RecyclePool(); var itemTemplate = (DataTemplate)XamlReader.Parse( @"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'> <TextBlock Text='{Binding}' /> </DataTemplate>"); repeater1 = new ItemsRepeater() { ItemsSource = dataSource, Layout = layout, ItemTemplate = new RecyclingElementFactoryDerived() { Templates = { { "key", itemTemplate } }, RecyclePool = recyclePool, SelectTemplateIdFunc = (object data, UIElement owner) => recycleKey } }; repeater2 = new ItemsRepeater() { ItemsSource = dataSource, Layout = layout, ItemTemplate = new RecyclingElementFactoryDerived() { Templates = { { "key", itemTemplate } }, RecyclePool = recyclePool, SelectTemplateIdFunc = (object data, UIElement owner) => recycleKey } }; var root = new StackPanel(); root.Children.Add(repeater1); root.Children.Add(repeater2); Content = new ItemsRepeaterScrollHost() { Width = 400, Height = 400, ScrollViewer = new ScrollViewer() { Content = root } }; Content.UpdateLayout(); Verify.AreEqual(numItems, VisualTreeHelper.GetChildrenCount(repeater1)); Verify.AreEqual(numItems, VisualTreeHelper.GetChildrenCount(repeater2)); // Throw all the elements into the recycle pool dataCollection.Clear(); Content.UpdateLayout(); for (int i = 0; i < numItems; i++) { var element1 = (FrameworkElement)recyclePool.TryGetElement(recycleKey, repeater1); Verify.AreSame(repeater1, element1.Parent); var element2 = (FrameworkElement)recyclePool.TryGetElement(recycleKey, repeater2); Verify.AreSame(repeater2, element2.Parent); } }); }
// [TestMethod] Temporarily disabled for bug 18866003 public void ValidateItemsRepeaterScrollHostScenario() { var realizationRects = new List <Rect>(); var scrollViewer = (ScrollViewer)null; var viewChangedEvent = new ManualResetEvent(false); int waitTime = 2000; // 2 seconds RunOnUIThread.Execute(() => { var repeater = new ItemsRepeater() { Layout = GetMonitoringLayout(new Size(500, 600), realizationRects), HorizontalCacheLength = 0.0, VerticalCacheLength = 0.0 }; scrollViewer = new ScrollViewer { Content = repeater, HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden, VerticalScrollBarVisibility = ScrollBarVisibility.Hidden, }; scrollViewer.ViewChanged += (sender, args) => { if (!args.IsIntermediate) { viewChangedEvent.Set(); } }; var tracker = new ItemsRepeaterScrollHost() { Width = 200, Height = 300, ScrollViewer = scrollViewer }; Content = tracker; Content.UpdateLayout(); // First layout pass will invalidate measure during the first arrange // so that we can get a viewport during the second measure/arrange pass. // That's why Measure gets invoked twice. // After that, ScrollViewer.SizeChanged is raised and it also invalidates // layout (third pass). Verify.AreEqual(3, realizationRects.Count); Verify.AreEqual(new Rect(0, 0, 0, 0), realizationRects[0]); Verify.AreEqual(new Rect(0, 0, 200, 300), realizationRects[1]); Verify.AreEqual(new Rect(0, 0, 200, 300), realizationRects[2]); realizationRects.Clear(); viewChangedEvent.Reset(); scrollViewer.ChangeView(null, 100.0, 1.0f, disableAnimation: true); }); Verify.IsTrue(viewChangedEvent.WaitOne(waitTime), "Waiting for view changed"); IdleSynchronizer.Wait(); RunOnUIThread.Execute(() => { Verify.AreEqual(new Rect(0, 100, 200, 300), realizationRects.Last()); realizationRects.Clear(); viewChangedEvent.Reset(); // Max viewport offset is (300, 400). Horizontal viewport offset // is expected to get coerced from 400 to 300. scrollViewer.ChangeView(400, 100.0, 1.0f, disableAnimation: true); }); Verify.IsTrue(viewChangedEvent.WaitOne(waitTime), "Waiting for view changed"); IdleSynchronizer.Wait(); RunOnUIThread.Execute(() => { Verify.AreEqual(new Rect(300, 100, 200, 300), realizationRects.Last()); realizationRects.Clear(); viewChangedEvent.Reset(); scrollViewer.ChangeView(null, null, 2.0f, disableAnimation: true); }); Verify.IsTrue(viewChangedEvent.WaitOne(waitTime), "Waiting for view changed"); IdleSynchronizer.Wait(); RunOnUIThread.Execute(() => { Verify.AreEqual(new Rect(300, 100, 100, 150), realizationRects.Last()); realizationRects.Clear(); }); }
public void ValidateElementAnimator() { ItemsRepeater repeater = null; ElementAnimatorDerived animator = null; var data = new ObservableCollection <string>(Enumerable.Range(0, 10).Select(i => string.Format("Item #{0}", i))); var renderingEvent = new ManualResetEvent(false); RunOnUIThread.Execute(() => { var elementFactory = new RecyclingElementFactory(); elementFactory.RecyclePool = new RecyclePool(); elementFactory.Templates["Item"] = (DataTemplate)XamlReader.Parse( @"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'> <TextBlock Text='{Binding}' Height='50' /> </DataTemplate>"); CompositionTarget.Rendering += (sender, args) => { renderingEvent.Set(); }; repeater = new ItemsRepeater() { ItemsSource = data, ItemTemplate = elementFactory, }; Content = new ItemsRepeaterScrollHost() { Width = 400, Height = 800, ScrollViewer = new ScrollViewer { Content = repeater } }; }); IdleSynchronizer.Wait(); Verify.IsTrue(renderingEvent.WaitOne(), "Waiting for rendering event"); List <CallInfo> showCalls = new List <CallInfo>(); List <CallInfo> hideCalls = new List <CallInfo>(); List <CallInfo> boundsChangeCalls = new List <CallInfo>(); RunOnUIThread.Execute(() => { animator = new ElementAnimatorDerived() { HasShowAnimationValue = true, HasHideAnimationValue = true, HasBoundsChangeAnimationValue = true, StartShowAnimationFunc = (UIElement element, AnimationContext context) => { showCalls.Add(new CallInfo(repeater.GetElementIndex(element), context)); }, StartHideAnimationFunc = (UIElement element, AnimationContext context) => { hideCalls.Add(new CallInfo(repeater.GetElementIndex(element), context)); }, StartBoundsChangeAnimationFunc = (UIElement element, AnimationContext context, Rect oldBounds, Rect newBounds) => { boundsChangeCalls.Add(new CallInfo(repeater.GetElementIndex(element), context, oldBounds, newBounds)); } }; repeater.Animator = animator; renderingEvent.Reset(); data.Insert(0, "new item"); data.RemoveAt(2); }); Verify.IsTrue(renderingEvent.WaitOne(), "Waiting for rendering event"); IdleSynchronizer.Wait(); Verify.AreEqual(1, showCalls.Count); var call = showCalls[0]; Verify.AreEqual(0, call.Index); Verify.AreEqual(AnimationContext.CollectionChangeAdd, call.Context); Verify.AreEqual(1, hideCalls.Count); call = hideCalls[0]; Verify.AreEqual(-1, call.Index); // not in the repeater anymore Verify.AreEqual(AnimationContext.CollectionChangeRemove, call.Context); Verify.AreEqual(1, boundsChangeCalls.Count); call = boundsChangeCalls[0]; Verify.AreEqual(1, call.Index); Verify.AreEqual(AnimationContext.CollectionChangeAdd | AnimationContext.CollectionChangeRemove, call.Context); Verify.AreEqual(0, call.OldBounds.Y); Verify.AreEqual(50, call.NewBounds.Y); showCalls.Clear(); hideCalls.Clear(); boundsChangeCalls.Clear(); // Hookup just for show animations and validate. RunOnUIThread.Execute(() => { animator.HasShowAnimationValue = true; animator.HasHideAnimationValue = false; animator.HasBoundsChangeAnimationValue = false; renderingEvent.Reset(); data.Insert(0, "new item"); data.RemoveAt(2); }); Verify.IsTrue(renderingEvent.WaitOne(), "Waiting for rendering event"); IdleSynchronizer.Wait(); Verify.AreEqual(1, showCalls.Count); call = showCalls[0]; Verify.AreEqual(0, call.Index); Verify.AreEqual(AnimationContext.CollectionChangeAdd, call.Context); Verify.AreEqual(0, hideCalls.Count); Verify.AreEqual(0, boundsChangeCalls.Count); }