Welcome to the Xceed Community | Help
Community Search  
More Search Options

Tech Side

How to Lookup on Data Virtualization

Hi! My name is Marc and I work in the technical support department here at Xceed. My main focus is to help those seeking help and give developers something exciting to read about. Since data virtualization is plenty to get excited about, let’s start there.

We all try to understand exactly what is going on in the “Data Virtualization” world and try to grasp its intentions; however, we sometimes need to sit back and take a look at what our possibilities are. What if we can go beyond that and try to cheat a little (as long as no one gets hurt)? Being from technical support and noticing that the same questions are often asked. How can something that seems so trivial be so complicated? Usually getting selected items is easy to achieve by simply accessing a property of a control. If you have tried to access the SelectedItems property from the DataGridControl when using data virtualization, you now know that an exception will be thrown stating that the operation is not supported. This happens when the number of SelectedItems exceeds the PageSize because these records aren’t loaded into memory yet, and the ones that were, well, they’re gone anyways since I scrolled down.

But not to worry, there are workarounds! This actually became a common question, especially when you want to allow deletion of multiple rows. So today I thought I would create this post in hopes of answering some of your questions.

Like I mentionned before, I will go ahead and explain how to delete multiple rows, although this workaround can apply to various other scenarios.

So to make things a little clearer, we will begin by creating a class called VirtualizationHelper. And from here on out, we will use this class to process any lookups that need to be done.

public class VirtualizationHelper<T>
       where T : class
{

    private static List<int> OrderSelectionRangesIndexes( IList<SelectionRange> selectedItemRanges )
    {
      List<int> indexes = new List<int>();
 

 

      foreachvar selectionRange in selectedItemRanges )
      {
        forint i = selectionRange.StartIndex; i <= selectionRange.EndIndex; i++ )
        {
          indexes.Add( i );
        }
      }
 

 

      return ( from i in indexes orderby i select i ).ToList<int>();
    }

 

    public VirtualizationHelper( DataGridControl dataGridControl )
    {
      iftypeof( T ) == typeofobject ) )
        throw new InvalidOperationException();

      m_dataGridControl = dataGridControl;
    }

    public void ProcessSelectionLookUp()
    {
      if( m_orderedSelectionIndexes != null )
        throw new InvalidOperationException();

      var indexes = VirtualizationHelper<T>.OrderSelectionRangesIndexes( m_dataGridControl.SelectedItemRanges );

      if( indexes.Count() == 0 )
        return;

      m_orderedSelectionIndexes = indexes;

      INotifyCollectionChanged notifyCollectionChanged = m_dataGridControl.Items as INotifyCollectionChanged;

      notifyCollectionChanged.CollectionChanged += ( sender, args ) =>
        {
          if( args.Action == NotifyCollectionChangedAction.Replace )
          {
            if( ( m_selectedIndexWaitingForRealizedItem != -1 ) && ( m_selectedIndexWaitingForRealizedItem == args.OldStartingIndex ) )
            {
              m_selectedIndexWaitingForRealizedItem = -1;
 

              m_dataGridControl.Dispatcher.BeginInvoke( new Action( () => this.ProcessSelectionLookUpCore() ) );
            }
          }
        };

      this.ProcessSelectionLookUpCore();
    }

    public event EventHandler<LookUpEventArgs<T>> SelectedItemLookUp;
    public event EventHandler SelectedItemLookUpCompleted;

    private void ProcessSelectionLookUpCore()
    {
      if( m_orderedSelectionIndexes == null )
        throw new InvalidOperationException();

      while( m_orderedSelectionIndexes.Count > 0 )
      {
        int globalIndex = m_orderedSelectionIndexes.Last();
        T item = m_dataGridControl.Items[ globalIndex ] as T;

        if( item == null )
        {
          // Item not yet realized.
          m_selectedIndexWaitingForRealizedItem = globalIndex;

          return;
        }

        // Item is realized
        this.OnSelectedItemLookUp( item, globalIndex );
        m_orderedSelectionIndexes.Remove( globalIndex );
      }

      this.OnSelectedItemLookUpCompleted();

      return;
    }

    private void OnSelectedItemLookUp( T item, int globalIndex )
    {
      ifthis.SelectedItemLookUp != null )
        this.SelectedItemLookUp( thisnew LookUpEventArgs<T>( item, globalIndex ) );
    }

    private void OnSelectedItemLookUpCompleted()
    {
      ifthis.SelectedItemLookUpCompleted != null )
        this.SelectedItemLookUpCompleted( this, EventArgs.Empty );

      // The whole lookup of all the selected items is completed.

      m_orderedSelectionIndexes = null;

      m_selectedIndexWaitingForRealizedItem = -1;

      var collectionView = m_dataGridControl.ItemsSource as ICollectionView;

      collectionView.Refresh();

      m_dataGridControl = null;
    }

    private List<int> m_orderedSelectionIndexes = null;
    private int m_selectedIndexWaitingForRealizedItem = -1;
    private DataGridControl m_dataGridControl;
  }

  public class LookUpEventArgs<T> : EventArgs
    where T : class
  {
    public LookUpEventArgs( T item, int globalIndex )
    {
      this.Item = item;
      this.GlobalIndex = globalIndex;
    }

    public T Item { getprivate set; }
    public int GlobalIndex { getprivate set; }
  }

Now we can send in our DataGridControl and get the SelectedItems we need. Even though this does defeat the whole purpose of data virtualization, there are those who may want to do something with those records that are not in memory yet. Now back to business… So we have this class, but how do we use it? The next part is pretty simple since the helper class does all the work for you, but you still have to call the appropriate methods.

In your code behind, handle the DeletingSelectedItems event and use the helper class from there.

void dataGridControl1_DeletingSelectedItems( object sender, CancelRoutedEventArgs e )
{
   e.Cancel = true;

   this.DeleteSelectedItems();

   var collectionView = dataGridControl1.ItemsSource as ICollectionView;

   collectionView.Refresh();

   dataGridControl1.IsEnabled = true;
}

private void DeleteSelectedItems()
{
   dataGridControl1.IsEnabled = false;

   var helper = new VirtualizationHelper<Record>( dataGridControl1 );

   helper.SelectedItemLookUp += ( sender, args ) =>
   {
      myBusinessObjectCollection.Remove( args.Item.Index );
   };

   helper.SelectedItemLookUpCompleted += ( sender, args ) =>
   {
      dataGridControl1.SelectedItemRanges.Clear();
        dataGridControl1.IsEnabled = true;
   };
 

   helper.ProcessSelectionLookUp();
}

So once we have deleted whatever we had to, we need to refresh the CollectionView so that the DataGridControl can reflect the changes that have been made.

One issue to keep in mind is that the above solution deletes when a user selects DataRows from top to bottom. If you are looking to implement a delete capability, please remember that you need to handle selection as well as selecting DataRows from bottom to top (user clicks on the 100th record and holds the Shift key and clicks on the 25th record). If it is not handled, the DataGrid won’t delete those items. It is important to think of everything that the user can do.

I will conclude this post by saying "thank you!" to all the readers and for your support! Marc - out.

Published July 11, 2011 3:37 PM by Marc [Xceed]

Attachment(s): DataVirtualizationLookupSample.zip

Comments

 

Richard said:

This code doesn't work with the Silverlight / WPF ListBox control - the Xceed.Silverlight.ListBox.SelectionRange class doesn't have a StartIndex or EndIndex property.

How do we retrieve the selected items?

September 27, 2011 3:49 PM
 

Richard said:

Sorry - just found it myself:

private void ListBox_SelectionChanged(object sender, EventArgs e)

{

   var list = (Xceed.Silverlight.ListBox.ListBox)sender;

   list.BeginGetSelectedItems(ar =>

   {

       IEnumerable<object> items = list.EndGetSelectedItems(ar);

       // Use selection here...

   }, null);

}

September 27, 2011 4:00 PM
 

Marc [Xceed] said:

Hey guys,

I guess I should have put it in the blog at the beginning (maybe I was just so excited to post my first entry), but this topic pertains to the Xceed DataGrid for WPF. What I will be doing shortly is posting a sample here that should give you a more clear understanding of how it works.

September 28, 2011 11:00 PM
 

Marc [Xceed] said:

Hey guys,

This is to let you know that I have added a sample application that shows how I use the above class called 'VirtualizationHelper' in order to fetch the rows that are not in memory. For the sake of the Blog, I have used a delete feature to the DataGrid, but you can manipulate the code to your liking and to whichever fits your scenario.

If you have any questions, please feel free to email support@xceed.com. Our technical support team is able to answer your questions a lot faster than on the Blog post

October 3, 2011 11:18 AM
 

Kevin said:

That's a great and clear coded explanation on this topic.

<a href="http://www.citizenshipper.com/hot-shot-loads.php">delivery jobs</a>

November 30, 2011 6:47 AM
 

Kevin said:

That's a great and clear coded explanation on this topic.

November 30, 2011 6:48 AM
Anonymous comments are disabled
Contact | Site Map | Reviews | Legal Terms of Use | Trademarks | Privacy Statement Copyright 2011 Xceed Software Inc.