Click here to Skip to main content
Click here to Skip to main content

Double-Click DataGrid in Silverlight

, 4 Oct 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Alishah Novin

United States United States
No Biography provided

Comments and Discussions

 
QuestionThe name initializeComponent() does not exist in the current context PinmemberMember 767644916-Aug-11 13:34 
AnswerRe: The name initializeComponent() does not exist in the current context Pinmembergkarlsson26-Oct-11 8:00 
Generalwant to download the source Pinmembermao_211090105517-Apr-11 16:16 
GeneralMy vote of 5 PinmemberFagnest20-Dec-10 2:05 
GeneralVery useful Pinmemberricas8-Dec-10 22:32 
GeneralMy vote of 5 Pinmemberricas8-Dec-10 22:30 
Useful and smart Smile | :) .
GeneralWorks well Pinmembermatthias_ludwig5-Nov-10 11:31 
GeneralMy vote of 3 Pinmembersantosh poojari4-Oct-10 23:47 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 4 Oct 2010
Article Copyright 2010 by Alishah Novin
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid