在Silverlight应用程序中,经常需要为DataGrid控件添加双击支持,无论是为了设置单元格的编辑模式,还是为了触发其他事件。本文将详细介绍如何以简单的方式实现这一功能。
目前,有一些尝试在DataGrid上实现双击支持的方法,但它们要么需要额外的依赖,要么实现起来并不简单。本文介绍的方法是通过继承DataGrid控件并扩展它以具有双击钩子的方式来实现的。
实现DataGrid非常简单,需要创建一个新的自定义控件,这个控件不是UserControl,而是从DataGrid派生的控件。
XAML代码如下:
<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" />
然后是这个控件的代码后台:
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;
}
}
}
通过分析点击发生的位置,查看该位置的对象,然后将它们作为事件的一部分返回。
可以像使用DataGrid一样使用这个DoubleClickDataGrid,但可以绑定到RowClicked和RowDoubleClicked事件。例如:
<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>
在DoubleClickDataGrid中,将使用标准的DataGrid元素。当行被单击或双击时,它将返回点击的行、单元格、绑定对象和列。可以通过更改DoubleClickTime来改变点击延迟。