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

Tech Side

How To Add Excel Like Selection of DataCells In Xceed DataGridControl

Recently, we had a question here at technical support asking if we had "Excel Like Selection of DataCells".

Well... we don't! When SelectionUnit is set to Cell on DataGridControl it is possible to make your selection on a cell-by-cell basis and to select multiple cells at once by Shift Clicking but still, that is not what some clients expect, especially if they are used to an Excel-like selection. That is, clicking on one cell, holding down the mouse key and as you move the mouse around, the cells in the rectangle formed by the clicked cell and the current location of the mouse should dynamically become selected and the content should scroll as you move your mouse to the edges of the DataGridControl .

But of course, nothing is impossible! This behavior can be achieved but it requires a little extra work.

This post will show how to customize the DataGridControl in order to add this functionality. The demo application is provided below.

First thing we need to do is to create a custom DataGridControl, in this post I will name my class ExelSelectionDataGrid  which inherits from DataGridControl.

We will keep a reference to the first clicked cell and the cell that lies under the mouse as the mouse moves. We will then dynamically calculate and add the ranges of cells that lie in the rectangle bounded by the first clicked cell and the cell beneath the mouse to the SelectedCellRanges collection of the datagrid instance.

For that, we would need to override the following:

OnPreviewMouseLeftButtonDown In order to get the keep track of the first clicked cell

OnPreviewMouseMove where we get the cell that is currently under the mouse, find the ranges between the first cell and the current cell and update the selected ranges, and finally scroll the DataGridControl if we are close to any of the edges if possible. 

OnPreviewMouseLeftButtonUp only to set our selection flag to false.

Getting the cell under the mouse can easily be acheived by using the VisualTreeHelperClass. This is how I used it:

private DataCell getDataCellUnderMouse(MouseEventArgs e)

{

     HitTestResult result = VisualTreeHelper.HitTest(this, e.MouseDevice.GetPosition(this));

     DependencyObject obj = result.VisualHit;

     var ctype = typeof(DataCell);

     if (obj != null)

     {

          var otype = obj.GetType();

         while (obj != null && (obj as DataCell) == null)

         {

         obj = VisualTreeHelper.GetParent(obj);

         }

     }

    return obj as DataCell;

}

 Manually scrolling the datagrid would require access to the scroll viewer, this can be reached through the following:

ScrollViewer  sv = (ScrollViewer)this.Template.FindName("PART_ScrollViewer", this);

So this is the entire class: 

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Xceed.Wpf.DataGrid;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Input;

using System.Diagnostics;

using System.Windows.Media;

using Xceed.Wpf.DataGrid.Views;

 

namespace ExcelLikeSelectionDataGrid

{

 

    class ExelSelectionDataGrid : DataGridControl

    {

        public ExelSelectionDataGrid()

        {

            this.Loaded += new RoutedEventHandler(CustomDataGridControl_Loaded);

        }

 

        void CustomDataGridControl_Loaded(object sender, RoutedEventArgs e)

        {

            sv = (ScrollViewer)this.Template.FindName("PART_ScrollViewer", this);

        }

        ScrollViewer sv;

 

 

        int firstItemIndex;

        int secondItemIndex;

        int firstColumnIndex;

        int secondColumnIndex;

 

        protected override void OnPreviewMouseMove(MouseEventArgs e)

        {

            base.OnPreviewMouseMove(e);

 

            if (isSelecting)

            {

                DataCell cellUnderMouse = getDataCellUnderMouse(e);

                if (cellUnderMouse != null)

                {

                    int columnIndex = this.Columns[cellUnderMouse.FieldName].VisiblePosition;

                    int itemIndex = this.Items.IndexOf(cellUnderMouse.DataContext);

                    if ((columnIndex != secondColumnIndex || itemIndex != secondItemIndex) && columnIndex >= 0 && itemIndex >= 0)

                    {

                        secondColumnIndex = columnIndex;

                        secondItemIndex = itemIndex;

                        UpdateSelection();

                    }

                }

                Point mousePosition = Mouse.GetPosition(this);

             

                if (mousePosition.Y > (this.ActualHeight - 10))

                {

                    double calculatedVerticalOffset = sv.VerticalOffset + 3 * (this.View as TableflowView).ContainerHeight;

                    sv.ScrollToVerticalOffset(calculatedVerticalOffset);

                }

                if (mousePosition.X > (this.ActualWidth - 10))

                {

                    double calculatedHorizontalOffset = sv.HorizontalOffset + 3 * 50.0;

                    sv.ScrollToHorizontalOffset(calculatedHorizontalOffset);

                }

 

                if (mousePosition.Y < (10))

                {

                    double calculatedVerticalOffset = sv.VerticalOffset - 3 * (this.View as TableflowView).ContainerHeight;

                    sv.ScrollToVerticalOffset(calculatedVerticalOffset);

                }

                if (mousePosition.X < 10)

                {

                    double calculatedHorizontalOffset = sv.HorizontalOffset - 3 * 50.0;

                    sv.ScrollToHorizontalOffset(calculatedHorizontalOffset);

                }

            }

        }

        protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)

        {

            base.OnPreviewMouseRightButtonDown(e);

            DataCell clickedCell = getDataCellUnderMouse(e);

            if (clickedCell != null)

            {

                isSelecting = true;

                firstColumnIndex = this.Columns[clickedCell.FieldName].VisiblePosition;

                firstItemIndex = this.Items.IndexOf(clickedCell.DataContext);

            }

        }

       

        protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)

        {

            isSelecting = false;

        }

 

 

        private void UpdateSelection()

        {

            if (!(Keyboard.Modifiers == ModifierKeys.Shift))

                this.SelectedCellRanges.Clear();

 

            this.SelectedCellRanges.Add(new SelectionCellRange(firstItemIndex, firstColumnIndex, secondItemIndex, secondColumnIndex));

        }

        bool isSelecting = false;

      

 

     

 

        private DataCell getDataCellUnderMouse(MouseEventArgs e)

        {

            HitTestResult result = VisualTreeHelper.HitTest(this, e.MouseDevice.GetPosition(this));

            DependencyObject obj = result.VisualHit;

 

            var ctype = typeof(DataCell);

            if (obj != null)

            {

                var otype = obj.GetType();

 

                while (obj != null && (obj as DataCell) == null)

                {

                    obj = VisualTreeHelper.GetParent(obj);

                }

            }

            return obj as DataCell;

        }

    }

} 

I will not go through the code details here. It should be straightforward to understand once you look at it. You can download a sample application here.

Please, add your comments and suggestions below. Let me know if you find a better way to acheive this or if you find any bugs in my code ;)

 

Published September 27, 2011 8:45 PM by Michel [Xceed]
Anonymous comments are disabled
Contact | Site Map | Reviews | Legal Terms of Use | Trademarks | Privacy Statement Copyright 2011 Xceed Software Inc.