Click here to Skip to main content
15,887,436 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
Hello
I am having ListView inside the Scrollviwer in wpf, Listbox caontain many items. I want to scroll the Listbox page by page on click of page up/ page down key press.

What I have tried:

C#
<pre>if (e.Key == Key.PageDown && selector.SelectedIndex == -1)
            {
                itemToSelect = selector.ItemContainerGenerator.ContainerFromIndex(0) as ListViewItem;
              
            }

its srolling but not selecting the next page item.

How to do this?
Posted
Updated 19-Nov-17 18:06pm
v2
Comments
Graeme_Grant 17-Nov-17 3:28am    
You need to figure out the last visible ListBoxItem, how many ListBoxItems are visible, then find the next last ListBoxItem and call scrollintoview on it. How you code it depends on if this is a MVVM project or not.
Member 11859517 17-Nov-17 3:44am    
Thanks Graeme_Grant for reply
no its not mvvm project.
how can I find last visible ListboxItem, can you have reference code.
Graeme_Grant 17-Nov-17 4:01am    
AS per the steps above. The key is to check the ListBoxItem's IsVisible Property (System.Windows)[^].
Member 11859517 17-Nov-17 6:46am    
sorry its ListView not the Listbox.
Graeme_Grant 17-Nov-17 7:22am    
I'm not actually convinced that you read what I wrote or looked at the link that I provided. IsVisible works for items in both ListBox and ListView. Did you try? If you did, then update your question with the code and describe where you are stuck.

I have a better idea for a simple solution - see below.

1 solution

Here is a simple non-MVVM solution that will work with a VirtualizingStackPanel - tested with 100,000 items. We work directly with the ListView's ScrollViewer. Will work even when the ListView is resized.

1. Xaml
XML
<Window
    x:Class="PagingVirtualListView.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Paging Virtualized ListView"
    Height="300" Width="500" WindowStartupLocation="CenterScreen">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <ListView x:Name="PeopleList" ItemsSource="{Binding Persons}" >
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Avatar" Width="100">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Image Source="{Binding AvatarUrl}" Margin="10" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Name" Width="200" DisplayMemberBinding="{Binding Name}" />
                    <GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
                </GridView>
            </ListView.View>
        </ListView>

        <StackPanel Grid.Column="1" Margin="10" VerticalAlignment="Center">
            <Button Content="PGUP" Click="OnPgUpClick" Padding="10 5" Margin="10"/>
            <Button Content="PGDn" Click="OnPgDnClick" Padding="10 5" Margin="10"/>
        </StackPanel>
    </Grid>

</Window>

2. The code-behind:
C#
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        PeopleList.Loaded += PeopleList_Loaded;

        DataContext = this;
        Mock();
    }

    ScrollViewer lvScrollViewer;

    public ObservableCollection<Person> Persons { get; set; }
        = new ObservableCollection<Person>();

    private Random rand = new Random();

    private void Mock()
    {
        for (int i = 0; i < 100000; i++)
        {
            Persons.Add(new Person
            {
                AvatarUrl = "http://www.freepngimg.com/download/happy_person/2-2-happy-person-free-download-png.png",
                Name = $"Person {i}",
                Age = rand.Next(20, 60)
            });
        }
    }

    private void PeopleList_Loaded(object sender, RoutedEventArgs e)
    {
        PeopleList.Loaded -= PeopleList_Loaded;

        // remember the ListView's Scrollviewer
        lvScrollViewer = PeopleList.GetVisualChild<ScrollViewer>();
    }

    private void OnPgUpClick(object sender, RoutedEventArgs e)
    {
        lvScrollViewer.ScrollToVerticalOffset(
            Math.Max(0, lvScrollViewer.VerticalOffset - lvScrollViewer.ViewportHeight));
    }
    private void OnPgDnClick(object sender, RoutedEventArgs e)
    {
        lvScrollViewer.ScrollToVerticalOffset(
            Math.Min(lvScrollViewer.VerticalOffset + lvScrollViewer.ViewportHeight, Persons.Count - 1));
    }
}

public static class HelperExtension
{
    public static T GetVisualChild<T>(this Visual referenceVisual) where T : Visual
    {
        Visual child = null;
        for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceVisual); i++)
        {
            child = VisualTreeHelper.GetChild(referenceVisual, i) as Visual;
            if (child != null && (child.GetType() == typeof(T)))
                break;
            else if (child != null)
            {
                child = GetVisualChild<T>(child);
                if (child != null && (child.GetType() == typeof(T)))
                    break;
            }
        }
        return child as T;
    }
}

public class Person
{
    public string AvatarUrl { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

Enjoy.
 
Share this answer
 
Comments
Member 11859517 19-Nov-17 23:55pm    
Thanks Graeme_Grant for your great help.
its scrolling page by page, no doubt, but I need select the 1st item of 2nd page or last Item of 1st page. did you get me?
Graeme_Grant 20-Nov-17 0:05am    
The above code does what you asked in your question.

However, for the new requirements, the hardest part (of changing the viewport) is done above for you.

For the new requirements, that part is pretty straight forward. You can set your pagesize manually or you can use the size of the lvScrollViewer.ViewportHeight (viewable area) to calculate the number of pages and the first item of each page and pass that value to lvScrollViewer.ScrollToVerticalOffset method.

But my recommendation is don't do that, it will disorient the user and they'll miss important data. The current method they won't.

The proper method of paging data is to only fill the ListView with the page and have a navigation bar at the bottom with forward/back & page number navigation buttons.
Member 11859517 20-Nov-17 1:23am    
Thanks,
still i am not able to select the item, how can i find the index based on vertical ofset or ViewportHeight
.
Graeme_Grant 20-Nov-17 2:25am    
That is a completely different problem for a new question. You need to use the ListView.SelectedItem property and not lvScrollViewer.ScrollToVerticalOffset. One sets the selection, the other changes the visible items.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900