How to create an event fired from a control in DataTemplate in GridView is visible to user when container ScrollViewer scrolls?

Sky Youngjin Kim 116 Reputation points
2021-02-26T01:09:31.823+00:00

Scenario:
1000 image records are searched and bound to GridView. with
I like to load Images only when the Image control is visible to user.

How to fire a event when a control in DataTemplate in GridView is visible currently to user as user scrolls ScrollViewer container?

Restriction: I do not want to loop through 1000 records each time when user scroll. - I found many solutions on web asking looping through entire controls and checking if it's HitTest OK or if each element is in ViewPort.

If a control is visible and fires an event. It will be perfect! I can handle the event and load the image.

Is there a way to complete this?

<ScrollViewer x:Name="scroller" 
              ViewChanged="scroller_ViewChanged"
              Visibility="{x:Bind Mode=OneWay, Path=ovm.scroller_Visibility}" Grid.Row="1" Margin="0, 0, 10, 0" 
              VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled" BorderBrush="{ThemeResource SystemControlBackgroundAccentBrush}" BorderThickness="0" >
    <Grid>
        <GridView x:Name="gridView" SelectionMode="Extended" Margin="0, 0, 0, 0"
SelectionChanged="gridView_SelectionChanged" IsItemClickEnabled="True" ItemClick="gridView_ItemClick" >
            <GridView.ItemTemplate>
                <DataTemplate x:DataType="modelLocal:OgFileInfo">
                    <Grid>
                        <Grid x:Name="scrollerItem">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="auto"></ColumnDefinition>
                                <ColumnDefinition ></ColumnDefinition>
                                <ColumnDefinition Width="30"></ColumnDefinition>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition></RowDefinition>
                            </Grid.RowDefinitions>
                            <Image Grid.Column="0" Width="{x:Bind ThumbnailWidth, Mode=OneWay}" Height="{x:Bind ThumbnailHeight, Mode=OneWay}" 
                                   Source="{x:Bind Thumbnail, Mode=OneWay}" Stretch="Uniform" Tag="{x:Bind Id, Mode=OneWay}"
                                   PointerEntered="gridView_ThumbnailImg_PointerEntered"
                                   PointerExited="gridView_ThumbnailImg_PointerExited"
                                   DoubleTapped="gridView_ThumbnailImg_DoubleTapped"
                                   ></Image>
                        </Grid>
                    </Grid>
                </DataTemplate>
            </GridView.ItemTemplate>
            <GridView.ItemsPanel>
                <ItemsPanelTemplate>
                    <ItemsWrapGrid Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </GridView.ItemsPanel>
        </GridView>
    </Grid>
</ScrollViewer>
Universal Windows Platform (UWP)
0 comments No comments
{count} votes

Accepted answer
  1. Sky Youngjin Kim 116 Reputation points
    2021-03-02T05:43:40.103+00:00

    F.Y.I.
    After long research, I confirmed there is NO support on this, but found very effective algorithm to solve this issue.

    All items' size are identical in GridView.
    Based on this, I could calculate and identify which items are visible in the area with Vertical and HorizontalOffset, and the current Viewport size and item size.
    This worked and tested with thousands of records. Algorithm is just N(1), without any looping.

    I hope this helps others.

    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. AryaDing-MSFT 2,841 Reputation points
    2021-02-26T10:14:09.35+00:00

    Hi,

    Welcome to Microsoft Q&A!

    First, when the Scrollviewer slides, you need to find the item that is currently visible in the Gridview. Then you could trigger events for them.

    You could check the following steps to do this.

    1.Get the Scrollviewer object through FindVisualChild<T>.

    2.Use MyGridViewItem.TransformToVisual(MyGridViewScrollViewer).TransformPoint(new Point(0, 0)) to get a Point object, which represents the top left corner of the item.

    3.Take the ScrollViewer.HorizontalOffset Property ,which is your current lower bound for the current viewable region.

    4.Take the HorizontalOffset plus the ScrollViewer.ViewportWidthProperty, which is the upper bound.

    5.Judge location. If your Point.X is greater than your lower bound and less than your upper bound, then the item is visible. (The Scrollview in the example is a Horizontal Scrollviewer. If you're using a Vertical ScrollViewer, use the ViewportHeight.)


    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

  2. Sky Youngjin Kim 116 Reputation points
    2021-02-26T16:49:58.81+00:00

    AryaDing,
    Thank you so much for your comment.
    Let me ask few things I don't understand from your answers:

    a. MyGridViewItem (in your step 2)
    => what is this? Is this Id of 'DataTemplate' or "OgFileInfo" object bound to 'DataTemplate'?

    b. "1. Get the Scrollviewer object through FindVisualChild<T>"
    => Why do I have to do this because 'ScrollViewer' is container of GridView and already available with x:Name?
    <ScrollViewer x:Name="scroller"><Grid><GridView>...

    The Scrollviewer found by 'FindVisualChild' is different from the container ScrollViewer?

    c. (MyGridViewScrollViewer) (in your step 2)
    => Is this the scrollViewer found by FindVisualChild ?

    d. GridView_ViewChanging event fires when scrolls, do I have to implement your logic by going through all items in GridView to check its visibility using your logic?

    e. You mentioned only HorizontalOffset and Width. What about vertical? VerticalOffset and Width?
    when there are hundreds items, do I have to check both horizontal and vertical bound?

    0 comments No comments