Share on reddit
[`google_buzz` not found]
Bookmark this on Digg
Share on StumbleUpon

INTRODUCTION: Today a user asked a very good question on Stack Overflow about adding more data to a DataGrid as the user nears the bottom of the grid.

It was an easy enough problem to solve, so I whipped together a sample app and am writing this article to further document it.

First, you’re XAML (this time in WPF format) needs to be defined. Normal instinct would be to let the DataGrid show it’s scrollbar as necessary, but in this case you should suppress that behavior and use a ScrollViewer because the ScrollView allows you to capture a ScrollChange event and the DataGrid does not.

EXAMPLE 1

<Window x:Class="GridExpansion.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:GridExpansion"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ScrollViewer VerticalScrollBarVisibility="Visible" 
                      ScrollChanged="ScrollViewer_OnScrollChanged" 
                      PreviewMouseWheel="UIElement_OnPreviewMouseWheel">
            <DataGrid x:Name="dg" VerticalScrollBarVisibility="Disabled"></DataGrid>
        </ScrollViewer>
            <DataGrid x:Name="dg" VerticalScrollBarVisibility="Disabled"></DataGrid>
        </ScrollViewer>
    </Grid>
</Window>

Next, we need our code behind which contains the code to initialize our grid and add more data on demand.

EXAMPLE 2

using System;
using System.Data;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;

namespace GridExpansion
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            CreateTable();

            dg.AutoGenerateColumns = true;
            dg.ItemsSource = EmployeeDataTable.DefaultView;
        }

        private DataTable employeeDataTable;
        private bool _addingData;

        public DataTable EmployeeDataTable
        {
            get { return employeeDataTable; }
            set
            {
                employeeDataTable = value;
            }
        }

        private void CreateTable()
        {
            EmployeeDataTable = new DataTable("EmployeeDataTable");
            EmployeeDataTable.Columns.Add("Row", typeof(int));
            EmployeeDataTable.Columns.Add("0", typeof(string));
            EmployeeDataTable.Columns.Add("1", typeof(string));
            EmployeeDataTable.Columns.Add("2", typeof(string));
            EmployeeDataTable.Columns.Add("3", typeof(string));
            EmployeeDataTable.Columns.Add("4", typeof(string));
            EmployeeDataTable.Columns.Add("5", typeof(string));
            EmployeeDataTable.Columns.Add("6", typeof(string));
            EmployeeDataTable.Columns.Add("7", typeof(string));
            EmployeeDataTable.Columns.Add("8", typeof(string));
            EmployeeDataTable.Columns.Add("9", typeof(string));

            GetNewData();
            GetNewData();
            GetNewData();
        }

        private void GetNewData()
        {
            for (int i = 0; i < 20; i++)//Adding 20 DataRows
            {
                var theRow = employeeDataTable.NewRow();
                theRow[0] = employeeDataTable.Rows.Count;
                for (int j = 1; j < 11; j++)
                {
                    theRow[j] = j % 2 == 0 ? "a" : "b";
                }
                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
                {
                    employeeDataTable.Rows.Add(theRow);
                }));
            }
        }

        private void ScrollViewer_OnScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            var sv = sender as ScrollViewer;

            if (sv != null && !_addingData)
            {
                if (sv.ScrollableHeight - e.VerticalOffset == 0)
                {
                    _addingData = true;
                    GetNewData();
                    _addingData = false;
                }
            }
        }

        private void UIElement_OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            ScrollViewer scv = (ScrollViewer)sender;
            scv.ScrollToVerticalOffset(scv.VerticalOffset - e.Delta);
            e.Handled = true;
        }
    }
}

CONCLUSION: The magic is in the ScrollViewer_OnScrollChanged event handler. We simply check if the scrollable height == the vertical height of the event args and if they are the same then we add more data.