Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / WPF

WPF Virtualizing Grid Control

Rate me:
Please Sign up or sign in to vote.
5.00/5 (13 votes)
27 May 2014CPOL3 min read 41.6K   1.6K   27   12
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

Image 1

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

C#
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 DynamicGridContro<code>l<code><code>

C#
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.

C#
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:

C#
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:

C#
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 :-)

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.

XML
 <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)


Written By
Software Developer
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCan you use with a UserControl? Pin
blyon27-Oct-22 11:41
blyon27-Oct-22 11:41 
Questionselectable Item Pin
Scott-S14-Aug-20 7:10
Scott-S14-Aug-20 7:10 
QuestionHow to trigger refresh Pin
Benoit Dufresne17-Jun-16 7:44
Benoit Dufresne17-Jun-16 7:44 
QuestionHow could I make it update? Pin
Benoit Dufresne10-Jun-16 5:24
Benoit Dufresne10-Jun-16 5:24 
AnswerRe: How could I make it update? Pin
Benoit Dufresne10-Jun-16 7:02
Benoit Dufresne10-Jun-16 7:02 
I was almost there. Turns out it's normal that nobody subscribes to the PropertyChanged event by default... What was displayed in my grid were the ToString() values of DistanceGridItem.

Styling my grid:
HTML
<!-- ItemTemplate -->
<local:DynamicSpreadGridControl.DataItemTemplate>
    <DataTemplate DataType="local:SpreadGridItem">
        <!-- Cell template -->
        <Border Name="border" Background="AliceBlue" Margin="3" BorderBrush="DeepSkyBlue" BorderThickness="1">
            <Grid>
                <TextBlock Text="{Binding Dist}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Grid>
        </Border>

        <DataTemplate.Triggers>
            <!-- Hide cells with productionCount == 0 -->
            <DataTrigger Binding="{Binding Dist}" Value="0">
                <Setter TargetName="border" Property="Visibility" Value="Hidden" />
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
</local:DynamicSpreadGridControl.DataItemTemplate>

Taking care to explicitly name my content property was all I needed so that the GUI would automatically subscribe to my events! Cool | :cool:

Sometimes magic just needs a little help to happen I guess - thanks again for the great control.
QuestionHave to use .net 4.0 Pin
Dom Hicklin3-Dec-15 0:47
Dom Hicklin3-Dec-15 0:47 
SuggestionRe: Have to use .net 4.0 Pin
Dom Hicklin3-Dec-15 4:42
Dom Hicklin3-Dec-15 4:42 
QuestionThis is such an awesome control! Pin
Member 913610027-Sep-15 21:55
Member 913610027-Sep-15 21:55 
SuggestionRe: This is such an awesome control! Pin
thomai8727-Sep-15 23:11
professionalthomai8727-Sep-15 23:11 
GeneralRe: This is such an awesome control! Pin
Member 913610028-Sep-15 5:20
Member 913610028-Sep-15 5:20 
GeneralLooks like this is what I needed all along! Pin
Arsalan Ahmad28-May-14 7:22
Arsalan Ahmad28-May-14 7:22 
QuestionNice! Pin
Volynsky Alex27-May-14 19:59
professionalVolynsky Alex27-May-14 19:59 

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

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