private void ReconfigureViewportPropagation(bool isInternal = false, IFrameworkElement_EffectiveViewport?child = null) { if (IsLoaded && IsEffectiveViewportEnabled) { if (_parentViewportUpdatesSubscription == null) { TRACE_EFFECTIVE_VIEWPORT("Enabling effective viewport propagation."); var parent = this.GetVisualTreeParent(); if (parent is IFrameworkElement_EffectiveViewport parentFwElt) { _parentViewportUpdatesSubscription = parentFwElt.RequestViewportUpdates(isInternal, this); } else { global::System.Diagnostics.Debug.Assert(IsVisualTreeRoot); // We are the root of the visual tree, we update the effective viewport // in order to initialize the _parentViewport of children. PropagateEffectiveViewportChange(isInitial: true, isInternal: isInternal); } } else if (child != null) { TRACE_EFFECTIVE_VIEWPORT("New child requested viewport propagation which has already been enabled, forwarding current viewport to it."); // We are already subscribed, the parent won't send any update (and our _parentViewport is expected to be up-to-date). // But if this "reconfigure" was made for a new child (child != null), we have to initialize its own _parentViewport. var parentViewport = GetParentViewport(); var viewport = GetEffectiveViewport(parentViewport); child.OnParentViewportChanged(isInitial: true, isInternal: true, this, viewport); } } else { #if CHECK_LAYOUTED if (!IsLoaded) { _isLayouted = false; } #endif if (_parentViewportUpdatesSubscription != null) { TRACE_EFFECTIVE_VIEWPORT("Disabling effective viewport propagation."); _parentViewportUpdatesSubscription.Dispose(); _parentViewportUpdatesSubscription = null; _parentViewport = ViewportInfo.Empty; } } }
private ViewportInfo GetEffectiveViewport(ViewportInfo parentViewport) { global::System.Diagnostics.Debug.Assert(parentViewport.Reference == this || parentViewport.Reference == null); if (IsVisualTreeRoot) { var slot = LayoutInformation.GetLayoutSlot(this); return(new ViewportInfo(this, slot, Rect.Infinite)); } if (IsScrollPort) { var viewport = parentViewport.Clip; // Pseudo intersect: // A normal intersect would return `Empty` as soon as the `viewport` and the `scrollport` does not overlap, // but on UWP the viewport is `Empty` only if the `viewport` is below the `scrollport` (for vertical scroll). // It means for an item in a list which is not yet visible on load (while vertical offset is 0), its EffVP will be empty. // Then when you scroll, its effVP will be something like [slot.Width,10 @ slot.X,slot.Y] when its first top 10px becomes visible. // Then if you continue to scroll and the item goes above the 'scrollport', its effVP will be [slot.Width,slot.Height @ slot.X,slot.Y] // while a normal intersect would be `Empty` (which is logic as the item is no longer visible at all, // but it make sense to not request to not go back to empty so element won't unload its content when scrolling up)! // However, nested scroll host has to do a real intersect. if (viewport.Right <= 0 || viewport.Bottom <= 0) { return(new ViewportInfo(this, Rect.Empty)); } // The visible window of the SCP // TODO: We should constrains the clip only on axis on which we can scroll var scrollport = new Rect( new Point(ScrollOffsets.X, ScrollOffsets.Y), LayoutInformation.GetLayoutSlot(this).Size); if (viewport.IsInfinite) { viewport = scrollport; } else { viewport.Intersect(scrollport); } return(new ViewportInfo(this, viewport)); } return(parentViewport); }
void IFrameworkElement_EffectiveViewport.OnParentViewportChanged( bool isInitial, // Update is intended to initiate the _parentViewport and should be "public" only if not due to a new event listener while tree is live (e.g. load) bool isInternal, // Indicates that this update is due to a new event handler IFrameworkElement_EffectiveViewport parent, // We propagate the parent to avoid costly lookup ViewportInfo viewport) // The viewport of the parent, expressed in parent's coordinates { if (!IsEffectiveViewportEnabled) { // We do not keep the _parentViewport up-to-date if not needed. // It's expected to the root parent to update its children when propagation activated. return; } if (!isInitial && viewport == _parentViewport) { return; } _parentViewport = viewport; PropagateEffectiveViewportChange(isInitial, isInternal); }