Search code examples
xamluwpwinui-3

How do you know which items are realized (non-virtualized) in an ItemsRepeater?


I have an ItemsRepeater. I know I can get all of the elements with ItemsRepeater.ItemsSourceView, but how can I figure out which ones are realized (and not virtualized) (e.g., for focusing the right-most realized item)?

Is the only way to iterate through the list with ItemsRepeater.TryGetElement and see what returns non-null?


Solution

  • You can't do this statically (i.e. ask "at the given moment, which elements are realized?") beyond the TryGetElement approach mentioned in the question.

    Alternative: book-keeping

    Have you tried the ItemsRepeater's Lifecycle Events?

    • ElementPrepared occurs each time an element is made ready for use. This occurs for both a newly created element as well as an element that already exists and is being re-used from the recycle queue.

    • ElementClearing occurs immediately each time an element has been sent to the recycle queue, such as when it falls outside the range of realized items.

    Check this sample code:

    public class Person
    {
        public string Id { get; set; }
    }
    
    
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
    
            for (int i = 0; i < 10_000; i++)
            {
                People.Add(new Person { Id = $"{i + 1}" });
            }
        }
    
        public ObservableCollection<Person> People { get; } = new();
    
        public ObservableCollection<Person> LoadedItems { get; } = new();
    
        private void ItemsRepeaterControl_ElementPrepared(ItemsRepeater sender, ItemsRepeaterElementPreparedEventArgs args)
        {
            if (sender.ItemsSourceView.GetAt(args.Index) is not Person item ||
                args.Element is not FrameworkElement frameworkElement)
            {
                return;
            }
    
            frameworkElement.DataContext = item;
            LoadedItems.Add(item);
        }
    
        private void ItemsRepeaterControl_ElementClearing(ItemsRepeater sender, ItemsRepeaterElementClearingEventArgs args)
        {
            if (args.Element is not FrameworkElement frameworkElement ||
                frameworkElement.DataContext is not Person item)
            {
                return;
            }
    
            LoadedItems.Remove(item);
        }
    }
    
    <Grid ColumnDefinitions="*,*">
    
        <Grid.Resources>
            <DataTemplate
                x:Key="PersonItemTemplate"
                x:DataType="local:Person">
                <Grid>
                    <TextBlock Text="{x:Bind Id, Mode=OneWay}" />
                </Grid>
            </DataTemplate>
        </Grid.Resources>
    
        <Grid
            Grid.Column="0"
            RowDefinitions="Auto,*">
            <TextBlock Grid.Row="0">
                <Run Text="Items Count: " />
                <Run Text="{x:Bind People.Count, Mode=OneWay}" />
            </TextBlock>
            <ScrollView Grid.Row="1">
                <ItemsRepeater
                    ElementClearing="ItemsRepeaterControl_ElementClearing"
                    ElementPrepared="ItemsRepeaterControl_ElementPrepared"
                    ItemsSource="{x:Bind People, Mode=OneWay}">
                    <ItemsRepeater.ItemTemplate>
                        <DataTemplate x:DataType="local:Person">
                            <Grid>
                                <TextBlock Text="{x:Bind Id, Mode=OneWay}" />
                            </Grid>
                        </DataTemplate>
                    </ItemsRepeater.ItemTemplate>
                </ItemsRepeater>
            </ScrollView>
        </Grid>
    
        <Grid
            Grid.Column="1"
            RowDefinitions="Auto,*">
            <TextBlock Grid.Row="0">
                <Run Text="Loaded Count: " />
                <Run Text="{x:Bind LoadedItems.Count, Mode=OneWay}" />
            </TextBlock>
            <ScrollView Grid.Row="1">
                <ListView ItemsSource="{x:Bind LoadedItems, Mode=OneWay}">
                    <ListView.ItemTemplate>
                        <DataTemplate x:DataType="local:Person">
                            <Grid>
                                <TextBlock Text="{x:Bind Id, Mode=OneWay}" />
                            </Grid>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </ScrollView>
        </Grid>
    </Grid>