windows-runtimesemantic-zoom

How to get the current item in view for Semantic Zoom control


I am using Semantic Zoom control to show the list of Contacts in my WinRT application. When I manually scroll the list, I want to get the current item which is in view (there will be more than one items in view, but I would like to get the closest one which is in view). So that I can save the scrolling position to set it as it is when user navigates back to this list of contact. Any suggestion? I tried getting the current Selected Index but it never got updated while scrolling through the list:

int lastViewedItem = ((ListViewBase)this.semanticZoom.ZoomedInView).SelectedIndex;

On navigating back

(ListViewBase)this.semanticZoom.ZoomedInView).SelectedIndex = lastViewedItem;

 ((ListViewBase)this.semanticZoom.ZoomedInView).ScrollIntoView(((ListViewBase)this.semanticZoom.ZoomedInView).SelectedItem);

Solution

  • I've created an extension to do just for any known ItemsControl that I knew here.

    using System;
    using System.Collections;
    using Windows.Foundation;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    /// <summary>
    /// Gets the first visible element.
    /// </summary>
    /// <param name="itemsControl">The ItemsControl.</param>
    /// <returns>The first visible item or null if not found.</returns>
    public static object GetFirstVisibleItem(this ItemsControl itemsControl)
    {
        var index = GetFirstVisibleIndex(itemsControl);
    
        if (index == -1)
        {
            return null;
        }
    
        var list = itemsControl.ItemsSource as IList;
    
        if (itemsControl.ItemsSource != null &&
            list != null &&
            list.Count > index)
        {
            return list[index];
        }
    
        if (itemsControl.Items != null &&
            itemsControl.Items.Count > index)
        {
            return itemsControl.Items[index];
        }
    
        throw new InvalidOperationException();
    }
    
    /// <summary>
    /// Gets the index of the first visible element.
    /// </summary>
    /// <param name="itemsControl">The ItemsControl.</param>
    /// <returns>The index of the first visible item or -1 if not found.</returns>
    public static int GetFirstVisibleIndex(this ItemsControl itemsControl)
    {
        // First checking if no items source or an empty one is used
        if (itemsControl.ItemsSource == null)
        {
            return -1;
        }
    
        var enumItemsSource = itemsControl.ItemsSource as IEnumerable;
    
        if (enumItemsSource != null && !enumItemsSource.GetEnumerator().MoveNext())
        {
            return -1;
        }
    
        // Check if a modern panel is used as an items panel
        var sourcePanel = itemsControl.ItemsPanelRoot;
    
        if (sourcePanel == null)
        {
            throw new InvalidOperationException("Can't get first visible index from an ItemsControl with no ItemsPanel.");
        }
    
        var isp = sourcePanel as ItemsStackPanel;
    
        if (isp != null)
        {
            return isp.FirstVisibleIndex;
        }
    
        var iwg = sourcePanel as ItemsWrapGrid;
    
        if (iwg != null)
        {
            return iwg.FirstVisibleIndex;
        }
    
        // Check containers for first one in view
        if (sourcePanel.Children.Count == 0)
        {
            return -1;
        }
    
        if (itemsControl.ActualWidth == 0)
        {
            throw new InvalidOperationException("Can't get first visible index from an ItemsControl that is not loaded or has zero size.");
        }
    
        for (int i = 0; i < sourcePanel.Children.Count; i++)
        {
            var container = (FrameworkElement)sourcePanel.Children[i];
            var bounds = container.TransformToVisual(itemsControl).TransformBounds(new Rect(0, 0, container.ActualWidth, container.ActualHeight));
    
            if (bounds.Left < itemsControl.ActualWidth &&
                bounds.Top < itemsControl.ActualHeight &&
                bounds.Right > 0 &&
                bounds.Bottom > 0)
            {
                return itemsControl.IndexFromContainer(container);
            }
        }
    
        throw new InvalidOperationException();
    }