private void SetItemsHostInsetForChild(int index, UIElement child, IContainItemStorage itemStorageProvider, bool isHorizontal) { Debug.Assert(!IsVSP45Compat, "SetItemsHostInset should not be called in VSP45-compat mode"); // this only applies to a hierarchical element with a visible ItemsHost bool isChildHorizontal = isHorizontal; IHierarchicalVirtualizationAndScrollInfo virtualizingChild = GetVirtualizingChild(child, ref isChildHorizontal); Panel itemsHost = (virtualizingChild == null) ? null : virtualizingChild.ItemsHost; if (itemsHost == null || !itemsHost.IsVisible) return; // get the transformation from child coords to itemsHost coords GeneralTransform transform = child.TransformToDescendant(itemsHost); if (transform == null) return; // when transform is undefined, ItemsHost is effectively invisible // build a rect (in child coords) describing the child's extended frame FrameworkElement fe = virtualizingChild as FrameworkElement; Thickness margin = (fe == null) ? new Thickness() : fe.Margin; Rect childRect = new Rect(new Point(), child.DesiredSize); childRect.Offset(-margin.Left, -margin.Top); // transform to itemsHost coords Rect itemsRect = transform.TransformBounds(childRect); // compute the desired inset, avoiding catastrophic cancellation errors Size itemsSize = itemsHost.DesiredSize; double left = DoubleUtil.AreClose(0, itemsRect.Left) ? 0 : -itemsRect.Left; double top = DoubleUtil.AreClose(0, itemsRect.Top) ? 0 : -itemsRect.Top; double right = DoubleUtil.AreClose(itemsSize.Width, itemsRect.Right) ? 0 : itemsRect.Right-itemsSize.Width; double bottom = DoubleUtil.AreClose(itemsSize.Height, itemsRect.Bottom) ? 0 : itemsRect.Bottom-itemsSize.Height; Thickness inset = new Thickness(left, top, right, bottom); // get the item to use as the key into items storage object item = GetItemFromContainer(child); if (item == DependencyProperty.UnsetValue) { Debug.Assert(false, "SetInset should only be called for a container"); return; } // see whether inset is changing object box = itemStorageProvider.ReadItemValue(item, ItemsHostInsetProperty); bool changed = (box == null); bool remeasure = changed; if (!changed) { Thickness oldInset = (Thickness)box; changed = !( DoubleUtil.AreClose(oldInset.Left, inset.Left) && DoubleUtil.AreClose(oldInset.Top, inset.Top ) && DoubleUtil.AreClose(oldInset.Right, inset.Right) && DoubleUtil.AreClose(oldInset.Bottom, inset.Bottom) ); // only changes along the scrolling axis require a remeasure. // use a less stringent "AreClose" test; experiments show that the // trailing inset can change due to roundoff error by an amount // that is larger than the tolerance in DoubleUtil, but not large // enough to warrant an expensive remeasure. remeasure = changed && ( (isHorizontal && !(AreInsetsClose(oldInset.Left, inset.Left) && AreInsetsClose(oldInset.Right, inset.Right))) || (!isHorizontal && !(AreInsetsClose(oldInset.Top, inset.Top) && AreInsetsClose(oldInset.Bottom, inset.Bottom))) ); } if (changed) { // store the new inset itemStorageProvider.StoreItemValue(item, ItemsHostInsetProperty, inset); child.SetValue(ItemsHostInsetProperty, inset); } if (remeasure) { // re-measure the scrolling panel ItemsControl scrollingItemsControl = GetScrollingItemsControl(child); Panel scrollingPanel = (scrollingItemsControl == null) ? null : scrollingItemsControl.ItemsHost; if (scrollingPanel != null) { VirtualizingStackPanel vsp = scrollingPanel as VirtualizingStackPanel; if (vsp != null) { vsp.AnchoredInvalidateMeasure(); } else { scrollingPanel.InvalidateMeasure(); } } } }