Windows DataGridView – Improving the Data Display (Refresh) Performance
The DataGridView is used everywhere – both in ASP.net apps as well as WinForms apps. A quick google search will reveal the scale of users affected by the slow refresh rate on the DataGridView control. How the data is displayed is dictated by a property called the Display Mode. The Display Mode that most datagridview users opt for is the Bound mode.
An object-bound datagridview
Object binding refers to the ability to bind a control to a custom data type (your domain objects). In pre-object binding days, one would bind a control directly to the raw data (e.g. an int CustomerID and a string CustomerName retrieved from the datasource). This was a cumbersome approach since most of the data could logically be grouped into custom data types (e.g. Customer). The User Interface was mainly interested in displaying the domain types (e.g. Customer) and less interested in native data types such as ints and strings. This led to the development of object-binding as an alternative to older raw data-binding.
A typical datagridview application that uses object binding will involve the following steps:
- Fetching Data (usually using ADO.NET and a DataReader/DataTable/DataSet combination).
- Storing the fetched data in a local collection (typically an IList<T> where T represents your custom domain object e.g. IList<Customer>).
- Converting the IList<Customer> to a BindingList<Customer>
- Binding the BindingList to the datagridview
For a detailed walkthrough of custom object binding to a datagridview, see this MSDN magazine article.
Bound Display Mode
For object bound DGVs (datagridviews), the ‘Bound’ mode is the most commonly used display mode. In this mode, the datagridview control is automatically bound to the source of the data – and any changes to the source of the data cause refreshes of the various UI elements of the grid (columns, rows etc.). This offers a degree of convenience that is appealing, at least for small volumes of data. This Bound mode kicks into effect whenever you set the Datasource property on the datagridview as shown in the snippet below.
- // Define your BindingList
- BindingList<object> bindableDataSource = new BindingList<object>();
- // Add some data to the bindingList
- BindingList<object> list = CreateData();
- foreach (object item in list)
- {
- bindableDataSource.Add(item);
- }
- // This actually 'binds' the gridview to the BindingList. Any changes to the BindingList from here on affect the gridview.
- // For e.g. – The 'bound' gridview would refresh its layout every time a new item is added to this BindingList
- dataGridView1.DataSource = bindableDataSource;
The Problem with Bound display mode
The first indications of problems with the bound mode are visible as soon as you start adding additional items to the source list (while the grid is bound to it). Note that it is possible to add additional items without binding the grid – and then performing the binding step at the very end – as shown in the snippet above. However, even with this approach, at some point, you may need to add/delete items from the source list – causing unnecessary refreshes. More serious problems with the DataGridView start manifesting themselves as the size of the bound collection grows. For larger sets of data, this problem grows to unacceptable response times.
Enter Virtual Mode
By default, most datagridview developers use a Display mode called Bound. In this display mode, the data in the grid is automatically bound to the data in the datasource. This means that the grid view control handles all aspects of fetching the data as well as displaying the data. While this offers convenience, it is the main reason for slower display performance on the DGV. Fortunately, there is another display mode available to address this problem. This mode is known as the virtual display mode. In this display mode, instead of being bound to the entire datasource, the grid is essentially bound to a small subset (a cached portion) of the datasource. This small subset is the set of data should match the data that is visible on the grid (the exact amount of data is under the programmer’s control). Whenever a user wants to see more data (scrolls the grid), that data is fetched from the datasource, placed into cache – and returned to the grid from the cache. This way, only a small amount of data is ever bound to the grid – and even that only through a memory based cache. This alleviates a lot of issues with the Windows DataGridView – including slow UI refreshes, locked UIs etc.
Quick Conversion from bound to virtual (full source code at the end of this post)
In your source code, if you are using the bound display mode (happens by default if you set the ‘datasource property) you will be setting the datasource property on your gridview somewhere – e.g.
- this.dataGridView1.DataSource = _customers;
Instead of setting this datasource property, what you will need to do is initialize the grid to use virtual mode. This is shown in the snippet below (full source code included at the end of this post).
- private void InitializeGrid()
- {
- // Enable virtual mode.
- this.dataGridView1.VirtualMode = true;
- // Connect the virtual-mode events to event handlers.
- this.dataGridView1.CellValueNeeded += new
- DataGridViewCellValueEventHandler(dataGridView1_CellValueNeeded);
The actual dataGridView1_CellValueNeeded event is where the event to request more data is handled. The included source code below provides a quick implementation of the virtual display mode – which should work for simple use cases. For a full walkthrough on a virtual mode implementation, see MSDN.
What if the Virtual Mode is still sluggish?
If the performance of virtual mode is still sluggish for your data (if you have several thousands of rows of data), chances are you need to look elsewhere. There are several 3rd party grid controls on the market – including Infragistics, Syncfusion and Telerik. One such grid that I have worked with extensively is Syncfusion’s Grid Control. On my project, we were handling several hundred MBs of data in the grid – with refreshes occurring in real-time or near real-time (i.e. no sluggishness). A performance comparison is detailed in an earlier post.
In addition to the speed, you also get a host of features that I like to call Grid on Steroids features. These include nested grids (which make it possible to efficiently display hierarchical data), multiple column sorts (which isn’t available out of the box in Windows DataGridView – but can be custom built), pivot tables and more.
Summary
If you are witnessing slow rendering (sluggish rendering) of your datagridview in your WinForms (or ASP.NET ) application, chances are you are using automatic binding (bound display mode). Chances are that you will experience a significant benefit from trying the virtual display mode built into the gridview. This display mode binds to a smaller, cached subset of the full data, allowing quicker UI refreshes. The sample attached here shows a grid using the bound mode and another grid using virtual mode (both with identical 10000 rows of customer data). Even with this simple sample of a few 100 KBs of data, a noticeable difference can be seen (by visual inspection) when one tries to scroll down the grid to view more rows. The refresh rate of the virtual mode grid clearly wins out. If you find the virtual mode also insufficient for your application’s volume of data, you might need something like the syncfusion grid.
Source Code
Download Full Solution
About the Author
Anuj Varma is a Microsoft .NET architect specializing in high-performance applications. His specific expertise in the .NET framework architecture as well as 3rd party controls (such as Syncfusion) built around the framework, makes him a sought after performance expert for .NET applications. Most recently, he has worked on an ASP.NET revamp of dell.com as well as a WinForms app used to map Ocean floors (Petrel). Both these applications were built with performance (UI performance as well as Data Access Layer performance) as a key driver. In addition to WinForms and ASP.NET apps, Anuj works hands-on in the WCF and Azure arena to bring performance to existing SOA apps. Some of Anuj’s personal websites include anujvarma.com, AspDotnetArchitect.com and RecruitersToAvoid.com.
very helpful article, specially for performance. dapfor .net grid also provide powerful performance control. http://www.dapfor.com/en/net-suite/net-grid/features/performance
I actually maanegd to sort this out, and this is how I did it for an un-bound datagridview in a win forms project VB2008Express. Private Sub DataGridView1_SortCompare(ByVal sender As Object, ByVal e As DataGridViewSortCompareEventArgs) Handles DataGridView1.SortCompare If e.Column.Index = 0 Then Dim DateValue1, DateValue2 As Date If Not Date.TryParse(e.CellValue1.ToString, DateValue1) Then Return If Not Date.TryParse(e.CellValue2.ToString, DateValue2) Then Return If DateValue1 = DateValue2 Then e.SortResult = 0 ElseIf DateValue2 DateValue1 Then e.SortResult = -1 Else e.SortResult = 1 End If End If e.Handled = True End SubI then call the routine from within one of my subs that loads data like so: sort routine before loading comboboxes Dim newcolumn As DataGridViewColumn = DataGridView1.Columns(0) Dim direction As ListSortDirection direction = ListSortDirection.Ascending DataGridView1.Sort(newcolumn, direction)I am posting it here in the hope that like me, if some-one needs it as desperately as I did, they can have access to it. Also, if this code is not as efficient as can be, I am hoping someone can refine it for our good use!thanks again for providing the forum and the spark!