Click here to Skip to main content
15,883,961 members
Articles / Programming Languages / XML

Double-Click DataGrid in Silverlight

Rate me:
Please Sign up or sign in to vote.
4.43/5 (6 votes)
4 Oct 2010CPOL1 min read 49.1K   8   8
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:

XML
<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:

C#
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:

XML
<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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionThe name initializeComponent() does not exist in the current context Pin
Member 767644916-Aug-11 12:34
Member 767644916-Aug-11 12:34 
AnswerRe: The name initializeComponent() does not exist in the current context Pin
gkarlsson26-Oct-11 7:00
gkarlsson26-Oct-11 7:00 
Generalwant to download the source Pin
mao_211090105517-Apr-11 15:16
mao_211090105517-Apr-11 15:16 
GeneralMy vote of 5 Pin
Fagnest20-Dec-10 1:05
Fagnest20-Dec-10 1:05 
GeneralVery useful Pin
rzvdaniel8-Dec-10 21:32
rzvdaniel8-Dec-10 21:32 
GeneralMy vote of 5 Pin
rzvdaniel8-Dec-10 21:30
rzvdaniel8-Dec-10 21:30 
GeneralWorks well Pin
matthias_ludwig5-Nov-10 10:31
matthias_ludwig5-Nov-10 10:31 
GeneralMy vote of 3 Pin
santosh poojari4-Oct-10 22:47
santosh poojari4-Oct-10 22:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.