Click here to Skip to main content
Click here to Skip to main content

WPF Virtualizing Grid Control

, 27 May 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
A responsive two dimensional spreadsheet-like control

Introduction

Recently, I found myself searching the web for a highly flexible, virtualizing WPF grid control, to present a large set of data to the user in an time-table like manner, who should then be able to pick one item to proceed with.

I didn't find anything appropriate and after playing around with virtualizing stack panels which didn't satisfy my needs of flexibility and responsiveness, I decided to implement a reusable grid by myself.

Features

  • Highly responsive (long tasks are executed asynchronously, progress bar is shown while loading).
  • Highly customizable (Gridlines, Headers, Content may be styles by using DataTemplates).
  • Fast (1Mio. data items with integer column/header-information are loaded within 2 seconds).
  • Makes use of multiple CPU cores (using TPL).
  • Automatically transforms a 1-dimensional source array to a 2-dimensional grid (by using two source item's properties to calculate the x/y position).

Sample

The data source for the control shown above is a IEnumerable<SampleGridItem>. The SampleGridItem looks like this:

public class SampleGridItem
{
    public string ProductName { get; set; }
    public DateTime ProductionDate { get; set; }
    public int ProductionCount { get; set; } 
}

Using the control - Step by Step

1. Include the project you can download on this page

Download, unpack and add it as a project reference.

2. Create a new class and derive from DynamicGridControl<>.

Because WPF can't handle generic classes, you need to derive from DynamicGridControl

public class DynamicGridSampleControl : DynamicGridControl<SampleGridItem, string, DateTime>
{ 

The type parameters specify:

  1. TDataSource: The type of the data items (which are later bound to the DataSource property).
  2. TRow: The type of the data item's property which contains row information.
  3. TCol: The type of the data item's property which contains column information.
As you can see, row and column can be of any type.

3. Create a default constructor

To tell the control, which properties of the data source contain row / header information, you assign the first two delegates (columnSelector / rowSelector).

The third delegate controls what happens, when multiple data items relate to the same row / column - combination.

 public DynamicGridSampleControl()
    : base(

    item => item.ProductName,


    item => item.ProductionDate,


    items => new SampleGridItem()
    {
        ProductName = items.First().ProductName,
        ProductionDate = items.First().ProductionDate,
        ProductionCount = items.Sum(item => item.ProductionCount)
    })

4. Sorting?

If you want to sort the row / column headers, assign a RowComparer / ColumnComparer.

Since the row / column id generic and can be any CLR type, you need to add logic on how to compare the items. Most of the time, you can use a type's CompareTo() -method:

this.ColumnComparer = (a, b) => a.CompareTo(b);

5. Create a static constructor to create a default style

Tell WPF which style to use when creating your Grid:

static DynamicGridSampleControl()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(DynamicGridSampleControl), new FrameworkPropertyMetadata(typeof(DynamicGridSampleControl)));
}

6. Copy the default style from the sample project

Copy the default Style (DynamicGridSampleControlDefaultStyle.xaml) and adjust the namespaces.

Most of the time, there is no need to modify the control template, except for the following cases:

  • You want to alter the gridline's style (they already provide an IsOdd-Property if you want to make every second row/column pink for example).
  • You know what you're doing Smile | :)

7. Create your control and bind your data to the DataSource - Property

IMPORTANT: Wrap your control within a ScrollViewer which's CanContentScroll is set to True.
Otherwise you won't be able to scroll.

 <ScrollViewer CanContentScroll="True"
          HorizontalScrollBarVisibility="Auto"
          VerticalScrollBarVisibility="Auto">
    <local:DynamicGridSampleControl ItemWidth="100" ItemHeight="30" DataSource="{Binding Data}" />
</ScrollViewer> 

8. Style it !

You can:

  • style the cells by setting the DataItemTemplate / DataItemTemplateSelector.
  • style the headers by setting the HeaderTemplate / HeaderTemplateSelector.
  • style the wait-layer by setting the WaitLayerTemplate.

How it works

Simple.

If you assign the DataSource, a distinct column / row - table is build (asynchronously & TPL).
Then a two-dimensional itemsCache is build for fast lookup while scrolling (asynchronously).

To display the data, for each visible cell, a bindable ViewModel (DynamicGridDataItem, etc.) is created that wraps the current content of the cell in its Content property (much like ItemContainerGenerator's recycle behavior).

If you scroll or resize the window, the visible cells' contents are updated by simply accessing the itemsCache.

License

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

Share

About the Author

thomai87
Software Developer
Germany Germany
No Biography provided

Comments and Discussions

 
GeneralLooks like this is what I needed all along! PinmemberArsalan Ahmad28-May-14 7:22 
QuestionNice! PinprofessionalVolynsky Alex27-May-14 19:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.141022.2 | Last Updated 27 May 2014
Article Copyright 2014 by thomai87
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid