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 DataGrid
s, 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 (GetDataGridCellByPosition(e.GetPosition(null), out currentRowClicked,
out currentColumnClicked, out currentCellClicked, out currentObject))
{
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);
}
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