DataGrid & Data Virtualization

Jun 15, 2009 at 8:48 AM

I'm trying to get Data Virtualization working based on this code http://www.codeproject.com/KB/WPF/WpfDataVirtualization.aspx (wish works perfect with ListView), but DataGrid craches whn I load the data.

Any suggestions or code samples I can look at. My data is a WebService that I get paged data from (from row # to row #).

Thanks!

Jun 18, 2009 at 8:55 PM

bump.

 

No info on how to use Virtualization with the Dataggird?

Jun 19, 2009 at 8:10 AM

My DataVirtualization is cruches only if I'm use async. Just use VirtualizingCollection instead of AsyncVirtualizingCollection and all will work fine. And if you do that, don't forget to set ScrollViewer.IsDeferredScrollingEnabled="True" like this:

 

<wpftk:DataGrid ScrollViewer.IsDeferredScrollingEnabled="True">
</wpftk:DataGrid>

 

 

Jun 19, 2009 at 9:35 AM
Edited Jun 19, 2009 at 11:58 AM

Thanks Settler,

How do I easiest "reload a page". I know my data has been change, but how do I update the contet in the grid?

 

And second, how can I support column sorting with Virtualization?

 

Jun 19, 2009 at 11:06 AM

Good questions :) Well. I'm modify Paul's DataWirtualization for supporting all that I'm need. Changing contains:

  • Refreshing support
  • Sorting support
  • IndexOf support for correct working DataGrid

I don't think a good idea pasting all new code right here. Is little large :) But i can give you some tricks:

1) For reload page:

 

public interface IRefreshableVirtualizingCollection
{
		void Refresh();
}

public class VirtualizingCollection<T> : IRefreshableVirtualizingCollection, IList<T>, IList, INotifyCollectionChanged
{
    ....................

                /// <summary>
		/// Refetch current page
		/// </summary>
		public virtual void Refresh()
		{
			LoadCount();
			if (Count == 0)
			{
				currentPageIndex = -1;
			}
			else
			{
				_pageTouchTimes.Clear();
				_pages.Clear();
				while (currentPageIndex * PageSize >= Count && currentPageIndex >= 0)
				{
					currentPageIndex--;
				}
				RequestPage(currentPageIndex);
			}
			FireCollectionReset();
		}
}

So. Here i'm add new interface IRefreshableVirtualizingCollection and move INotifyCollectionChanged and implementation of this from AsyncVirtualizingCollection to VirtualizingCollection.

2) For sorting data:

 

public class SortableDataGrid : DataGrid
	{
		protected override void OnSorting(DataGridSortingEventArgs e)
		{
			if (ItemsSource != null &&
			    ItemsSource is IRefreshableVirtualizingCollection &&
				((IRefreshableVirtualizingCollection)ItemsSource).ItemsProvider is IBasicSortableItemsProvider)
			{
				IRefreshableVirtualizingCollection itemsCollection = (IRefreshableVirtualizingCollection)ItemsSource;
				IBasicSortableItemsProvider itemsProvider = (IBasicSortableItemsProvider)itemsCollection.ItemsProvider;

				if (e.Column.SortDirection == ListSortDirection.Descending)
				{
					itemsProvider.SortDirection = null;
					itemsProvider.UntypedSortField = null;
				}
				else
				{
					if (e.Column.SortDirection == null)
					{
						itemsProvider.SortDirection = ListSortDirection.Ascending;
					}
					else if (e.Column.SortDirection == ListSortDirection.Ascending)
					{
						itemsProvider.SortDirection = ListSortDirection.Descending;
					}
					itemsProvider.UntypedSortField = e.Column.SortMemberPath;
				}
				e.Column.SortDirection = itemsProvider.SortDirection;
				itemsCollection.Refresh();
				e.Handled = true;
			}
			else
			{
				base.OnSorting(e);
			}
		}

		protected override void OnPreviewKeyDown(KeyEventArgs e)
		{
			if (e.Key == Key.A && 
				Keyboard.Modifiers == ModifierKeys.Control &&
				ItemsSource is IBasicVirtualizingCollection)
			{
				if(Items.Count > ((IBasicVirtualizingCollection)ItemsSource).PageSize)
				{
                    if (MessageBox.Show(ResStrings.CONFIRM_SELECTALL_MESSAGE, ResStrings.WARNING_TITLE, MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.No)
					{
						e.Handled = true;
						return;
					}
					SelectAll();
					e.Handled = true;
					return;
				}
			}
		}
	}

And here I'm create a simple SortableDataGrid that check ItemsSource and do sorting. So, My Items provider contain "ListSortDirection? SortDirection" field, and "string UntypedSortField", that used in FetchRange implementation. 

OnPreviewKeyDown method is warning user about possible large amount of data, because SelectAll() method is very slow...

 

 

Jun 19, 2009 at 12:16 PM

Thanks again!

In the "Refresh" from where to you take "currentPageIndex"  (from "pageIndex" in "ReguestPage" or ?)

Now I have to do "datagrid1.Items.Refresh()" after running your Refresh, to see the update.  And the method FireCollectionReset()  I like to see that... 

Do you use "public event NotifyCollectionChangedEventHandler CollectionChanged;" ?

if you want you can mail me: kennet[at]kennet.se

I get the the sorting later...

 

Jun 19, 2009 at 12:36 PM
Edited Jun 19, 2009 at 12:38 PM

currentPageIndex - is simply interger field. In "public T this[int index]":

int pageIndex = currentPageIndex = index / PageSize;

 

"... and move INotifyCollectionChanged and implementation of this from AsyncVirtualizingCollection to VirtualizingCollection."

I'm just pull down all code in region INotifyCollectionChanged to VirtualizingCollection.

And for sorting:

 

public interface IBasicVirtualizingCollection
	{
		/// <summary>
		/// Gets the items provider.
		/// </summary>
		/// <value>The items provider.</value>
		IBasicItemsProvider ItemsProvider{ get; }

		/// <summary>
		/// Gets the size of the page.
		/// </summary>
		/// <value>The size of the page.</value>
		int PageSize { get; }
	}
        /// <summary>
	/// Represents a basic provider of collection details.
	/// </summary>
	public interface IBasicItemsProvider
	{
		/// <summary>
		/// Fetches the total number of items available.
		/// </summary>
		/// <returns></returns>
		int FetchCount();
	}

 

         /// <summary>
	/// Represents a provider of collection details.
	/// </summary>
	/// <typeparam name="T">The type of items in the collection.</typeparam>
	public interface IItemsProvider<T> : IBasicItemsProvider
	{
		/// <summary>
		/// Fetches a range of items.
		/// </summary>
		/// <param name="startIndex">The start index.</param>
		/// <param name="count">The number of items to fetch.</param>
		/// <returns></returns>
		IList<T> FetchRange(int startIndex, int count);
	}

 

 

Untyped interface is need to me for not generic controls...

 

Jun 19, 2009 at 2:07 PM

Thanks Settler,

Refresh works just fine now, but sice I call .Refresh() on a new thread I hade to change "OnCollectionChanged", so I found this example: http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx

I try the sorting later...