Data Virtualization in WPF DataGrid (SfDataGrid)

2 Aug 202317 minutes to read

WPF DataGrid (SfDataGrid) provides support to handle the large amount of data through built-in virtualization features. With Data virtualization, SfDataGrid.View process the data in on-demand for better performance while loading large amount of data. Below are the different virtualization concepts available,

Concept Usage
Data Virtualization Use to load large amount of data in less time.
Incremental Loading Use to load subset of data from the services or servers in less time while loading and scrolling. On-demand request also supported.
Paging Use to load large amount of data in less time with the help of SfDataPager.
On-demand paging Use to load data in on-demand. You can load data only for current page from server.

Data Virtualization

You can load large amount of data in less time by setting SfDataGrid.EnableDataVirtualization property to true.

<syncfusion:SfDataGrid x:Name="dataGrid"
                       AutoGenerateColumns="True"
                       ItemsSource="{Binding EmployeeDetails}"
                       EnableDataVirtualization="True">
this.datagrid.EnableDataVirtualization = true;

Working with GridVirtualizingCollectionView

You can load the large amount of data in less time in another way using GridVirtualizingCollectionView which is derived from VirtualizingCollectionView to SfDataGrid.ItemsSource.

In the below code, ViewModel defined with GridVirtualizingCollectionView by passing complete records collection and bound to SfDataGrid.

public class ViewModel
{
    private GridVirtualizingCollectionView _gridVirtualizingItemsSource;

    public GridVirtualizingCollectionView GridVirtualizingItemsSource
    {
        get { return _gridVirtualizingItemsSource; }
        set { _gridVirtualizingItemsSource = value; }
    }

    public ViewModel()
    {
        var _orders = this.GenerateOrders();                        
        GridVirtualizingItemsSource = new GridVirtualizingCollectionView(_orders);
    }
}
<syncfusion:SfDataGrid x:Name="dataGrid" 
                       ColumnSizer="Star"
                       ItemsSource="{Binding GridVirtualizingItemsSource}" />

Limitations

  1. Data update using LiveDataUpdateMode is not supported.
  2. Details view is not supported.
  3. AllowFrozenGroupHeaders is not supported.
  4. DataTable collection is not supported.

Incremental Loading

DataGrid supports to load the data incrementally using ISupportIncrementalLoading interface.
ISupportIncrementalLoading interface has LoadMoreItemsAsync method which helps to load the data incrementally. LoadMoreItemsAsync called in on-demand while scrolling based on HasMoreItems property.

If HasMoreItems is false, SfDataGrid stops calling LoadMoreItemsAsync. SfDataGrid have IncrementalList which is derived from ISupportIncrementalLoading. You can use IncrementalList or create collection derived from ISupportIncrementalLoading and bind it SfDataGrid.ItemsSource.

In the below code, IncrementalList is initialized by passing Action to its constructor for loading items incrementally.

<Window.DataContext>
    <local:ViewModel />
</Window.DataContext>

<syncfusion:SfDataGrid x:Name="dataGrid" 
                       AllowFiltering="True"
                       AutoGenerateColumns="True"    
                       ItemsSource="{Binding IncrementalItemsSource}"/>
Public class ViewModel
{

    public ViewModel()
    {
        IncrementalItemsSource = new IncrementalList<OrderInfo>(LoadMoreItems) { MaxItemCount = 1000 };
    }

    private IncrementalList<OrderInfo> _incrementalItemsSource;

    public IncrementalList<OrderInfo> IncrementalItemsSource
    {
        get { return _incrementalItemsSource; }
        set { _incrementalItemsSource = value; }
    }

    /// <summary>
    /// Method to load items which assigned to the action of IncrementalList
    /// </summary>
    /// <param name="count"></param>
    /// <param name="baseIndex"></param>

    void LoadMoreItems(uint count, int baseIndex)
    {
        var _orders = GenerateOrders();
        var list = _orders.Skip(baseIndex).Take(50).ToList();
        IncrementalItemsSource.LoadItems(list);
    }
}

You can download the sample from here.

Displaying animation when fetching data from services

You can display animations when fetching data from service for LoadMoreItemsAsync method call, using BackgroundWorker.

In the below code snippet data fetched from service using BackgroundWorker and SfBusyIndicator displayed over SfDataGrid based on IsBusy property in ViewModel, until BackgroundWorker completes its action.

<Window.DataContext>
    <local:ViewModel />
</Window.DataContext>
<Window.Resources>
    <local:BoolToVisiblityConverter x:Key="converter" />
</Window.Resources>
<Grid>
    <syncfusion:SfDataGrid x:Name="dataGrid" 
                           AllowFiltering="True"
                           AutoGenerateColumns="True"    
                           ItemsSource="{Binding IncrementalItemsSource}"/>  
    <Border Height="60"
            VerticalAlignment="Bottom"
            Background="Black"
            BorderBrush="Black"
            BorderThickness="1"
            Opacity="50"
            Visibility="{Binding IsBusy,
                                 Mode=TwoWay,
                                 Converter={StaticResource converter}}">
        <StackPanel HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Orientation="Horizontal">
            <TextBlock Margin="5"
                       VerticalAlignment="Center"
                       FontSize="16"
                       Foreground="White"
                       Text="Fetching Data..." />
            <syncfusion:SfBusyIndicator Margin="5"
                                        VerticalAlignment="Center"
                                        Foreground="Gray"
                                        AnimationType="Gear" />
        </StackPanel>
    </Border>
</Grid>
public class ViewModel : INotifyPropertyChanged
{
    NorthwindEntities northwindEntity;

    public ViewModel()
    {
        string uri="http://services.odata.org/Northwind/Northwind.svc/";
        incrementalItemsSource = new IncrementalList<Order>(LoadMoreItems) { MaxItemCount = 1000}; 
        northwindEntity = new NorthwindEntities(new Uri(uri));
        IsBusy = false;
    }

    private bool isBusy;
    /// <summary>
    /// Gets or Sets whether to show the busy indicator.
    /// </summary>

    public bool IsBusy
    {
        get { return isBusy; }
        set { isBusy = value; RaisePropertyChanged("IsBusy"); }
    }

    /// <summary>
    /// Loads the item while SfDataGrid loading and scrolling.
    /// </summary>
    /// <param name="count">Specifies the fetch count to load data</param>
    /// <param name="baseIndex">Specifies the index to load data</param>

    void LoadMoreItems(uint count, int baseIndex)
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += (o, ae) =>
        {
            DataServiceQuery<Order> query = northwindEntity.Orders.Expand("Customer");
            query = query.Skip<Order>(baseIndex).Take<Order>(50) as DataServiceQuery<Order>;
            IAsyncResult ar = query.BeginExecute(null, null);
            var items = query.EndExecute(ar);
            var list = items.ToList();
            Application.Current.Dispatcher.Invoke(new Action(() => { IncrementalItemsSource.LoadItems(list); }));
        };

        worker.RunWorkerCompleted += (o, ae) =>
        {
            IsBusy = false; 
        };
            
        IsBusy = true;
        worker.RunWorkerAsync();
    }
}

WPF DataGrid displays Incremental Data Loading

You can download the sample from here.

LoadMore using ISupportIncrementalLoading

You can fetch the data in some user action instead of scrolling using IncrementalList.LoadItems method.

In the below code, data fetched when you click the Load Items button.

<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>

<Grid x:Name="Root_Grid">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <syncfusion:SfDataGrid x:Name="dataGrid"                                                                                                                                                                                           
                           AutoGenerateColumns="True"    
                           ItemsSource="{Binding IncrementalItemsSource}"/>
        <StackPanel Grid.Row="1" Orientation="Vertical">
            <Button x:Name="loadByButton" Content="Load Items" 
                            Command="{Binding DataContext.LoadItems , ElementName=dataGrid}"/>
        </StackPanel>
    </Grid>
</Window>
public class ViewModel 
{
    NorthwindEntities northwindEntity;

    private IncrementalList<Order> _incrementalItemsSource;

    public IncrementalList<Order> IncrementalItemsSource
    {
        get { return _incrementalItemsSource; }
        set { _incrementalItemsSource = value; }
    }

    private BaseCommand loadItems;

    public BaseCommand LoadItems
    {
        get
        {
            if (loadItems == null)

                loadItems = new BaseCommand(OnLoadItemsClicked, OnCanLoad);
            return loadItems;
        }
    }

    private static bool OnCanLoad(object obj)
    {
        return true;
    }

    private void OnLoadItemsClicked(object obj)
    {            
        LoadMoreItems(5, this.IncrementalItemsSource.Count);
    }


    public ViewModel()
    {
        string uri="http://services.odata.org/Northwind/Northwind.svc/";
        _incrementalItemsSource = new IncrementalList<Order>(LoadMoreItems) { MaxItemCount = 50 }; 
        northwindEntity = new NorthwindEntities(new Uri(uri));
    }

    /// <summary>
    /// Method to load items which assigned to the action of IncrementalList
    /// </summary>
    /// <param name="count"></param>
    /// <param name="baseIndex"></param>

    void LoadMoreItems(uint count, int baseIndex)
    {                        
        DataServiceQuery<Order> query = northwindEntity.Orders.Expand("Customer");
        query = query.Skip<Order>(baseIndex).Take<Order>(50) as DataServiceQuery<Order>;
        IAsyncResult ar = query.BeginExecute(null, null);
        var items = query.EndExecute(ar);
        var list = items.ToList();
        IncrementalItemsSource.LoadItems(list);            
    }
}

You can download the sample from here.

Limitations

  1. Deleting is not supported. You can code to delete row in application level.
  2. Summary is not calculated based on LiveDataUpdateMode.

Paging

SfDataGrid supports to load paged data source using SfDataPager. You can use the paging in SfDataGrid by go through the Paging section.