Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Double-Click DataGrid in Silverlight

0.00/5 (No votes)
4 Oct 2010 1  
A simple double-clickeable datagrid in Silverlight

Introduction

This article focuses on a simple way of implementing Double-Click events on a DataGrid in Silverlight. Double-click support does not currently exist, and often one needs to enable it for DataGrids, whether it's to set a cell into edit mode, or to trigger some other event. This article details how it can be done in a simple manner.

Background

There are a few attempts out there to enable double-click support on a DataGrid, some either requiring additional dependencies, and others that are not as simple to implement. The method in this article derives a control by inheriting the DataGrid control and extending it to have double-click hooks.

Using the Code

Implementing the data-grid is very simple, and requires creating a new custom control that is not a UserControl, but a control derived from the DataGrid.

The XAML is very simple:

<Data:DataGrid x:Class="My.NameSpace.DoubleClickDataGrid"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:Data="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"/> 

The code-behind of this control is then:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace My.NameSpace
{
    public partial class DoubleClickDataGrid : DataGrid
    {
        public event RowClickedHandler RowClicked;
        public event RowDoubleClickedHandler RowDoubleClicked;

        public delegate void RowClickedHandler(object sender, DataGridRowClickedArgs e);
        public delegate void RowDoubleClickedHandler
            (object sender, DataGridRowClickedArgs e);
                
        private DataGridRow _LastDataGridRow = null;
        private DataGridColumn _LastDataGridColumn = null;
        private DataGridCell _LastDataGridCell = null;
        private object _LastObject = null;
        private DateTime _LastClick = DateTime.MinValue;

        private double _DoubleClickTime = 1500;
        public double DoubleClickTime
        {
            get
            {
                return _DoubleClickTime;
            }
            set
            {
                _DoubleClickTime = value;
            }
        }

        public DoubleClickDataGrid()
        {
            InitializeComponent();

            this.MouseLeftButtonUp += new MouseButtonEventHandler
                    (DoubleClickDataGridClick);
        }

        protected void OnRowClicked()
        {
            if (RowClicked != null)
            {
                RowClicked(this, new DataGridRowClickedArgs
          (_LastDataGridRow, _LastDataGridColumn, _LastDataGridCell, _LastObject));
            }
        }

        protected void OnRowDoubleClicked()
        {
            if (RowDoubleClicked != null)
            {
                RowDoubleClicked(this, new DataGridRowClickedArgs
          (_LastDataGridRow, _LastDataGridColumn, _LastDataGridCell, _LastObject));
            }
        }

        private void DoubleClickDataGridClick(object sender, MouseButtonEventArgs e)
        {
            DateTime clickTime = DateTime.Now;
            DataGridRow currentRowClicked;
            DataGridColumn currentColumnClicked;
            DataGridCell currentCellClicked;
            object currentObject;

            //If we've found at least the row,
            if (GetDataGridCellByPosition(e.GetPosition(null), out currentRowClicked, 
        out currentColumnClicked, out currentCellClicked, out currentObject))
            {
                //And the current row is the same as the last row, and is within 
                //the timespan, consider it a double-click
                bool isDoubleClick = (currentRowClicked == _LastDataGridRow && 
        clickTime.Subtract(_LastClick) <= TimeSpan.FromMilliseconds
            (_DoubleClickTime));

                _LastDataGridRow = currentRowClicked;
                _LastDataGridColumn = currentColumnClicked;
                _LastDataGridCell = currentCellClicked;
                _LastObject = currentObject;

                if (isDoubleClick)
                {
                    OnRowDoubleClicked();
                }
                else
                {
                    OnRowClicked();
                }
            }
            else
            {
                _LastDataGridRow = null;
                _LastDataGridCell = null;
                _LastDataGridColumn = null;
                _LastObject = null;
            }

            _LastClick = clickTime;
        }

        private bool GetDataGridCellByPosition(Point pt, out DataGridRow dataGridRow,  
    out DataGridColumn dataGridColumn, out DataGridCell dataGridCell, 
        out object dataGridObject)
        {
            var elements = VisualTreeHelper.FindElementsInHostCoordinates(pt, this);
            dataGridRow = null;
            dataGridCell = null;
            dataGridColumn = null;
            dataGridObject = null;

            if (null == elements || elements.Count() == 0)
            {
                return false;
            }

            var rowQuery = from gridRow in elements where gridRow 
            is DataGridRow select gridRow as DataGridRow;
            dataGridRow = rowQuery.FirstOrDefault();
            if (dataGridRow == null)
            {
                return false;
            }

            dataGridObject = dataGridRow.DataContext;

            var cellQuery = from gridCell in elements 
        where gridCell is DataGridCell select gridCell as DataGridCell;
            dataGridCell = cellQuery.FirstOrDefault();

            if (dataGridCell != null)
            {
                dataGridColumn = DataGridColumn.GetColumnContainingElement(dataGridCell);
            }

            //If we've got the row, return true - 
            //sometimes the Column, DataContext could be null
            return dataGridRow != null;
        }
    }

    public class DataGridRowClickedArgs
    {
        public DataGridRow DataGridRow { get; set; }
        public DataGridColumn DataGridColumn { get; set; }
        public DataGridCell DataGridCell { get; set; }
        public object DataGridRowItem { get; set; }

        public DataGridRowClickedArgs(DataGridRow dataGridRow, 
    DataGridColumn dataGridColumn, DataGridCell dataGridCell, object dataGridRowItem)
        {
            DataGridRow = dataGridRow;
            DataGridColumn = dataGridColumn;
            DataGridCell = dataGridCell;
            DataGridRowItem = dataGridRowItem;
        }
    }
} 

Points of Interest

The way it comes together is by analyzing where the click occurred, looking at what objects are at this position, and then returning them as part of the event.

One can implement this DoubleClickDataGrid in the same way you would use a DataGrid, but you can then tie into the RowClicked, and RowDoubleClicked events.

For example:

<UserControl x:Class="My.NameSpace.SampleControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:Data="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:Controls="clr-namespace:My.NameSpace">
    <Grid x:Name="LayoutRoot" Background="White">
        <Controls:DoubleClickDataGrid x:Name="MyDataGrid" DoubleClickTime="1500" 
        RowClicked="RowClicked" RowDoubleClicked="RowDoubleClick">
            <Data:DataGrid.Columns>
                <Data:DataGridTextColumn Header="MyColumn" CanUserResize="False" 
        CanUserReorder="False" />
            </Data:DataGrid.Columns>
        </Controls:DoubleClickDataGrid>
    </Grid>
</UserControl>

Within the DoubleClickDataGrid, you would then use the standard DataGrid elements. When the row is single-clicked or double-clicked, it will return the clicked row, cell, bound object, and column. You can change the click-delay by changing the DoubleClickTime.

References

This article modifies a modified method provided by Naveen, to obtain the position of the clicked row and data cell. For more, read:

History

  • 4th October, 2010: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here