在WPF DataGrid中实现编辑和双击(MouseDoubleClick)。

Dav*_*ave 2 wpf events datagrid mvvm

我已经用DataGrid编码了WPF UserControl。我添加了使用“单击编辑”来编辑一列(RecordingName)的功能(请参阅:我的代码和http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing)。我还在处理整个DataGrid的MouseDoubleClick事件。

它的工作原理是……您可以肯定地编辑有问题的列(RecordingName),然后双击该列以外的任何地方,一切都很好。双击该列会出现问题。(对我来说)并不奇怪。您试图捕获双击,但同时也正在查看单击(通过PreviewMouseLeftButtonDown事件)。

我认为这是一个普遍的问题。有人可以建议我最好的处理方式吗?我绝对需要支持双击,但是也可以通过单击编辑来编辑RecordingName,这是很好的。

我还想通过右键单击RecordingName并选择重命名,然后使用F2选择它来支持编辑RecordingName。如果您转到Windows资源管理器,这就是您看到的行为。如果选择文件,然后在其上单击鼠标左键,则您处于编辑(重命名)模式。如果快速双击它,则文件将启动。如果右键单击或选择并单击F2,则可以对其进行重命名。

感谢您的帮助或想法。我粘贴了下面的代码。我确实尝试将其截断到最低限度。它仍然是很多代码。不管好坏,我都为控件本身使用了MVVM模型。

这是控件的xaml:

<UserControl x:Class="StackOverFlowExample.RecordingListControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
          xmlns:local="clr-namespace:StackOverFlowExample"
         mc:Ignorable="d" 

         d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
    <ResourceDictionary>

    <Style TargetType="{x:Type DataGridCell}">
        <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown"></EventSetter>
    </Style>
        <Style x:Key="CellViewStyle" TargetType="{x:Type Label}" BasedOn="{StaticResource {x:Type Label}}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={RelativeSource Self},
                                Path=(Validation.Errors)[0].ErrorContent}"/>
                <Setter Property="BorderBrush" Value="Red" />
                <Setter Property="BorderThickness" Value="1" />
            </Trigger>
        </Style.Triggers>
    </Style>



        <Style TargetType="{x:Type DataGrid}" >
        <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:RecordingListControl}}, Path=Foreground}" />

            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </Style.Triggers>

    </Style>

    <!-- style to apply to DataGridTextColumn in edit mode  -->
    <Style x:Key="CellEditStyle" TargetType="{x:Type TextBox}">
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="Padding" Value="0"/>
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={RelativeSource Self},
                                Path=(Validation.Errors)[0].ErrorContent}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
    <Style x:Key="CellNonEditStyle" TargetType="{x:Type TextBlock}">

        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={RelativeSource Self},
                                Path=(Validation.Errors)[0].ErrorContent}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
    </ResourceDictionary>
</UserControl.Resources>
<Grid >
    <Grid Name="LayoutRoot">
        <DataGrid Name="MainDataGrid" IsEnabled="{Binding Path=IsEnabled}" ItemsSource="{Binding Path=Recordings}" Margin="5"  SelectionChanged="ListBox_SelectionChanged" MouseDoubleClick="DataGrid_MouseDoubleClick" AutoGenerateColumns="False" >
            <DataGrid.Columns>

                <DataGridTextColumn Header="#"  IsReadOnly="True"  Binding="{Binding RecordingNumber}">
                    <DataGridTextColumn.HeaderStyle>
                        <Style TargetType="DataGridColumnHeader">

                            <Setter Property="HorizontalAlignment"
             Value="Center" />
                        </Style>
                    </DataGridTextColumn.HeaderStyle>
                    <DataGridTextColumn.ElementStyle>
                        <Style TargetType="{x:Type TextBlock}">

                            <Setter Property="HorizontalAlignment" Value="Center" />
                            <Setter Property="VerticalAlignment" Value="Center" />
                        </Style>
                    </DataGridTextColumn.ElementStyle>
                </DataGridTextColumn>

                <DataGridTemplateColumn SortMemberPath="RecordingName" Header="Recording Name" CanUserSort="True">
                    <DataGridTemplateColumn.HeaderStyle>
                        <Style TargetType="DataGridColumnHeader">
                            <Setter Property="HorizontalContentAlignment"
             Value="Center" />
                        </Style>
                    </DataGridTemplateColumn.HeaderStyle>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Label Content ="{Binding RecordingName, ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}" Foreground="Black"  Style="{StaticResource CellViewStyle}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                    <DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <TextBox Text="{Binding RecordingName, ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellEditingTemplate>
                </DataGridTemplateColumn>

                <DataGridTextColumn Header="Time" CanUserSort="False" IsReadOnly="True"  Binding="{Binding TotalTime, StringFormat=mm\\:ss}">
                    <DataGridTextColumn.HeaderStyle>
                        <Style TargetType="DataGridColumnHeader">
                            <Setter Property="HorizontalContentAlignment"
             Value="Center" />
                        </Style>
                    </DataGridTextColumn.HeaderStyle>
                    <DataGridTextColumn.ElementStyle>
                        <Style TargetType="{x:Type TextBlock}">
                            <Setter Property="HorizontalAlignment" Value="Center" />
                            <Setter Property="VerticalAlignment" Value="Center" />
                        </Style>
                    </DataGridTextColumn.ElementStyle>
                </DataGridTextColumn>
                <DataGridTextColumn Header="End Time" IsReadOnly="True" SortMemberPath="EndTime" Binding="{Binding EndTime,StringFormat={}\{0:dd/MM/yyyy HH:mm\}}">
                    <DataGridTextColumn.HeaderStyle>
                        <Style TargetType="DataGridColumnHeader">
                            <Setter Property="HorizontalContentAlignment"
             Value="Center" />
                        </Style>
                    </DataGridTextColumn.HeaderStyle>
                    <DataGridTextColumn.ElementStyle>
                        <Style TargetType="{x:Type TextBlock}">
                            <Setter Property="HorizontalAlignment" Value="Center" />
                            <Setter Property="VerticalAlignment" Value="Center" />
                        </Style>
                    </DataGridTextColumn.ElementStyle>
                </DataGridTextColumn>
            </DataGrid.Columns>
            <DataGrid.Resources>

            </DataGrid.Resources>
            <DataGrid.ContextMenu >
                <ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">

                    <MenuItem 
                    Header="Delete Recording" 
                    Command="{Binding Path=DataContext.DeleteRecordingCommand}"
                    CommandParameter="{Binding Path=SelectedItem}"/>
                </ContextMenu>
            </DataGrid.ContextMenu>
    </DataGrid>
    </Grid>

</Grid>
Run Code Online (Sandbox Code Playgroud)

这是背后的代码(依赖项属性需要此代码。我不知道其他方法)

using System;
using System.Windows;
 using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace StackOverFlowExample
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class RecordingListControl : UserControl
{
    public delegate void SelectionEventHandler(object sender, RecordingInfo info);

    public event SelectionEventHandler DoubleClickEvent;

    public RecordingListViewModel vm = new RecordingListViewModel();
    public RecordingListControl()
    {
        InitializeComponent();

       LayoutRoot.DataContext = vm;
    }

    #region Dependency property for SelectedItem
    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("SelectedItem", typeof(RecordingInfo), typeof(RecordingListControl));

    public RecordingInfo SelectedItem
    {
        get { return (RecordingInfo)GetValue(SelectedItemProperty); }
        set
        {
            SetValue(SelectedItemProperty, value);
        }
    }
    #endregion

    public static FrameworkPropertyMetadata md = new FrameworkPropertyMetadata(new PropertyChangedCallback(OnSomeCallback));

    public static readonly DependencyProperty SomeDependencyProperty =
   DependencyProperty.Register("SomeDependency", typeof(bool), typeof(RecordingListControl), md);


    public bool SomeDependency
    {  
    get { return (bool)GetValue(SomeDependencyProperty); }
        set { SetValue(SomeDependencyProperty, value); }
    }

    private static void OnSomeCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
       RecordingListControl ctrl = (RecordingListControl)d;

       ctrl.vm.PopulateGrid();

    }

    private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems != null && e.AddedItems.Count > 0)
            SelectedItem = e.AddedItems[0] as RecordingInfo;
        else
            SelectedItem = null;
    }

    private void DataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        if (MainDataGrid.SelectedItem == null || SelectedItem == null)
            return;

        if (DoubleClickEvent != null)
        {
            DoubleClickEvent(sender, SelectedItem);
        }
    }



    // from: http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing
    // SINGLE CLICK EDITING
    //
    private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        DataGridCell cell = sender as DataGridCell;
        if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
        {
            if (!cell.IsFocused)
            {
                cell.Focus();
            }
            DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
            if (dataGrid != null)
            {
                if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
                {
                    if (!cell.IsSelected)
                        cell.IsSelected = true;
                }
                else
                {
                    DataGridRow row = FindVisualParent<DataGridRow>(cell);
                    if (row != null && !row.IsSelected)
                    {
                        row.IsSelected = true;
                    }
                }
            }
        }
    }

    static T FindVisualParent<T>(UIElement element) where T : UIElement
    {
        UIElement parent = element;
        while (parent != null)
        {
            T correctlyTyped = parent as T;
            if (correctlyTyped != null)
            {
                return correctlyTyped;
            }

            parent = VisualTreeHelper.GetParent(parent) as UIElement;
        }
        return null;
    }

}
Run Code Online (Sandbox Code Playgroud)

}

ViewModel:

public class RecordingInfo
{
    public string RecordingName { get; set; }
    public int RecordingNumber { get; set; }
    public TimeSpan? TotalTime { get; set; }
    public DateTime? EndTime { get; set; }
}
public class RecordingListViewModel : ViewModelBase
{
    private ObservableCollection<RecordingInfo> _recordings = null;
    private string _patientId;
    private int _sessionNumber;
    BackgroundWorker _workerThread = new BackgroundWorker();


    public RecordingListViewModel()
    {
        _workerThread.DoWork += new DoWorkEventHandler(workerThread_DoWork);
        _workerThread.RunWorkerCompleted += new
            RunWorkerCompletedEventHandler(workerThread_RunWorkerCompleted);

    }
    public ObservableCollection<RecordingInfo> Recordings
    {
        get
        {
            return _recordings;
        }
    }

    bool _isEnabled = false;
    public bool IsEnabled
    {
        get
        {
            return _isEnabled;
        }
        private set
        {
            if (value != _isEnabled)
            {
                _isEnabled = value;
                OnPropertyChanged("IsEnabled");
            }
        }

    }

    public void PopulateGrid()
    {
        _workerThread.RunWorkerAsync();  // this is overkill in this demo project...
    }


    private void workerThread_DoWork(object sender, DoWorkEventArgs e)
    {
        _recordings = new ObservableCollection<RecordingInfo>();
        RecordingInfo info1 = new RecordingInfo() { TotalTime = new TimeSpan(100), EndTime = DateTime.Now, RecordingName = "recording 1", RecordingNumber = 1 };
        _recordings.Add(info1);

        RecordingInfo info2= new RecordingInfo() { TotalTime = new TimeSpan(10000), EndTime = new DateTime(1999,2,2), RecordingName = "recording 2", RecordingNumber = 2 };
        _recordings.Add(info2);

        RecordingInfo info3 = new RecordingInfo() { TotalTime = new TimeSpan(7000), EndTime = new DateTime(2008, 2, 2), RecordingName = "recording 3", RecordingNumber = 3};
        _recordings.Add(info3);
    }

    private void workerThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        OnPropertyChanged("Recordings");
        IsEnabled = true;
    }


    public void SaveRecording(RecordingInfo info)
    {

    }

    private RecordingInfo _selectedItem = null;
    public RecordingInfo SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            if (value == _selectedItem)
                return;
            // verify that selected item is actully in our collection of recordings!
            if (!_recordings.Contains(value))
                throw new ApplicationException("Selected item not in collection");

            _selectedItem = value;

            OnPropertyChanged("SelectedItem");

            // selection changed - do something special
        }
    }

    private ICommand _deleteRecordingCmd = null;
    public ICommand DeleteRecordingCommand
    {
        get
        {
            if (_deleteRecordingCmd == null)
            {
                _deleteRecordingCmd = new RelayCommand(param => DeleteRecordingCommandImplementation(param));
            }
            return _deleteRecordingCmd;
        }
    }

    /// <summary>
    /// I used ideas from this post to get Delete working:
    /// http://stackoverflow.com/questions/19447795/command-bind-to-contextmenu-which-on-listboxitem-in-listbox-dont-work
    /// </summary>
    /// <param name="note"></param>
    private void DeleteRecordingCommandImplementation(object recording)
    {

            if (_recordings != null && _recordings.Count > 0 && recording is RecordingInfo)
            {
                if (_recordings.Contains(recording as RecordingInfo))
                {
                    _recordings.Remove(recording as RecordingInfo);

                }
                OnPropertyChanged("Recordings");
            }

    }


}
Run Code Online (Sandbox Code Playgroud)

还有MainWindow xaml和后面的代码:

<Window x:Class="StackOverFlowExample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:StackOverFlowExample"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <local:RecordingListControl x:Name="_ctrl"></local:RecordingListControl>
</Grid>
Run Code Online (Sandbox Code Playgroud)

 public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        _ctrl.SomeDependency = true;
        _ctrl.DoubleClickEvent += _ctrl_DoubleClickEvent;
    }

    void _ctrl_DoubleClickEvent(object sender, RecordingInfo info)
    {
        MessageBox.Show("You double clicked me!");
    }
}
Run Code Online (Sandbox Code Playgroud)

Gop*_*ope 6

我不确定您真正想要实现什么。单击是否仅更改标题,而单击仅对DataGrid条目执行操作?

如果是这种情况,您可以轻松地区分两者,因此无论如何您都不会处理错误的情况(取决于您选择的处理程序):

鼠标按下:

    OnMouseDownDown(object sender, MouseButtonEventArgs e)
    {
        UIElement uiElement = (UIElement) sender;
        Point hitPoint = e.GetPosition(uiElement);

        HitTestResult hitTestResult = VisualTreeHelper.HitTest(uiElement, hitPoint);
        if (hitTestResult == null)
        {
            return;
        }

         Visual DataGridHeader= hitTestResult.VisualHit.FindParent<DataGridHeader>();
    }
Run Code Online (Sandbox Code Playgroud)

单击:使用VisualTreeHelper来获得[查找的任何类型]的第一个父项。eventArgs.OriginalSource

    OnHandleMouseClick(object sender, RoutedEventArgs e)
    {
      var target = e.OriginalSource.FindVisualParent<DataGridHeader>();
    }
Run Code Online (Sandbox Code Playgroud)

如果您只想区分单击和双击(可以与点击测试结合使用,您仍然可以使用MouseDown并查看ClickCount中的)EventArgs。此计数可处理给定时间范围内的单击和可视矩形,以区分单击和双击。

希望这可以帮助。

编辑 - - - - - - - - - - - - - - - - - - - - - - - - - ----->

您发表评论后,我建立了一个小样本。您可以在这里下载:http : //1drv.ms/1kgnCeQ

当我在StackOverflow上发现另一个问题时,我一直在寻找一种利用DispatcherTimer的解决方案。尽管我提出了正确的想法,但您可以在此处找到所用代码的源:WPF:单击按钮+双击问题

那应该是您要求的。如果您需要有关F2或ContextMenu的帮助,只需注释。:)

祝一切顺利

编辑2 ------------------------------------------------- ---->

我创建了一个新示例来处理您的所有要求:http : //1drv.ms/1kgnCeQ 它包含一个简单的ContextMenu,以及除以前的要求之外的F2编辑。就ExceptionHandling等而言,它还很原始,但是它显示了如何处理所有情况。那应该做。我还在DoubleClick部分添加了dataGrid.CancelEdit来重置编辑模式。:)

这是示例中的代码,使其他人更容易阅读此答案:

MainWindow.xaml:

<Window x:Class="IDevign.DataGrids.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dataGrids="clr-namespace:IDevign.DataGrids"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid x:Name="dataGrid" AutoGenerateColumns="False" MouseDoubleClick="Button_MouseDoubleClick" PreviewKeyDown="DataGrid_OnPreviewKeyDown">
            <DataGrid.Resources>
                <Style TargetType="{x:Type DataGridCell}">
                    <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown"></EventSetter>
                </Style>
            </DataGrid.Resources>
            <DataGrid.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Edit" Click="MenuItem_OnClick"></MenuItem>
                </ContextMenu>
            </DataGrid.ContextMenu>
            <DataGrid.Columns>
                <DataGridTextColumn Header="Recording Name" Binding="{Binding Name}"/>
                <DataGridTextColumn Header="Duration"  Binding="{Binding Duration}" IsReadOnly="True"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)

MainWindow.xaml.cs:

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private static DispatcherTimer myClickWaitTimer = new DispatcherTimer(
            new TimeSpan(0, 0, 0, 1),
            DispatcherPriority.Background,
            mouseWaitTimer_Tick,
            Dispatcher.CurrentDispatcher);

        public MainWindow()
        {
            InitializeComponent();
            Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
        {
            ObservableCollection<Song> songs = new ObservableCollection<Song>
            {
                new Song {Duration = 3.11, Name = "Best song ever"},
                new Song {Duration = 3.33, Name = "2nd best song ever"},
                new Song {Duration = 3.02, Name = "3rd best song ever"}
            };

            dataGrid.ItemsSource = songs;
        }

        private void Button_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            // Stop the timer from ticking.
            myClickWaitTimer.Stop();
            dataGrid.CancelEdit();
            MessageBox.Show("DoubleClicked");
            e.Handled = true;
        }

        private static void mouseWaitTimer_Tick(object sender, EventArgs e)
        {
            myClickWaitTimer.Stop();

        }

        /// <summary>
        /// Handles the LeftButton Click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            myClickWaitTimer.Start();

            DataGridCell cell = sender as DataGridCell;
            if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
            {
                if (!cell.IsFocused)
                {
                    cell.Focus();
                }
                DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
                if (dataGrid != null)
                {
                    if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
                    {
                        if (!cell.IsSelected)
                            cell.IsSelected = true;
                    }
                    else
                    {
                        DataGridRow row = FindVisualParent<DataGridRow>(cell);
                        if (row != null && !row.IsSelected)
                        {
                            row.IsSelected = true;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// KeyDown Handler for F2 Key
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DataGrid_OnPreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (dataGrid.SelectedItem == null || e.Key != Key.F2)
            {
                return;
            }

            DataGridRow selectedRow = dataGrid.ItemContainerGenerator.ContainerFromItem(dataGrid.SelectedItem) as DataGridRow;
            if (selectedRow == null)
            {
                return;
            }

            DataGridCell cell = GetCell(dataGrid, selectedRow, 0);
            if (cell != null)
            {
                cell.Focus();
                dataGrid.BeginEdit();
                e.Handled = true;
            }

        }

        /// <summary>
        /// ContextMenu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuItem_OnClick(object sender, RoutedEventArgs e)
        {
            DataGridRow selectedRow = dataGrid.ItemContainerGenerator.ContainerFromItem(dataGrid.SelectedItem) as DataGridRow;
            if (selectedRow == null)
            {
                return;
            }

            DataGridCell cell = GetCell(dataGrid, selectedRow, 0);
            if (cell != null)
            {
                cell.Focus();
                dataGrid.BeginEdit();
                e.Handled = true;
            }
        }

        /// <summary>
        /// Helper Method to get a cell by columnIndex
        /// </summary>
        /// <param name="dataGrid"></param>
        /// <param name="rowContainer"></param>
        /// <param name="column"></param>
        /// <returns></returns>
        public static DataGridCell GetCell(DataGrid dataGrid, DataGridRow rowContainer, int column)
        {
            if (rowContainer != null)
            {
                DataGridCellsPresenter presenter = FindVisualChild<DataGridCellsPresenter>(rowContainer);
                if (presenter == null)
                {
                    /* if the row has been virtualized away, call its ApplyTemplate() method
                     * to build its visual tree in order for the DataGridCellsPresenter
                     * and the DataGridCells to be created */
                    rowContainer.ApplyTemplate();
                    presenter = FindVisualChild<DataGridCellsPresenter>(rowContainer);
                }
                if (presenter != null)
                {
                    DataGridCell cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
                    if (cell == null)
                    {
                        /* bring the column into view
                         * in case it has been virtualized away */
                        dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]);
                        cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
                    }
                    return cell;
                }
            }
            return null;
        }

        #region VisualTreeHelper Methods 

        private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                if (child != null && child is T)
                    return (T) child;
                else
                {
                    T childOfChild = FindVisualChild<T>(child);
                    if (childOfChild != null)
                        return childOfChild;


     }
        }
        return null;
    }

    private static T FindVisualParent<T>(UIElement element) where T : UIElement
    {
        UIElement parent = element;
        while (parent != null)
        {
            T correctlyTyped = parent as T;
            if (correctlyTyped != null)
            {
                return correctlyTyped;
            }

            parent = VisualTreeHelper.GetParent(parent) as UIElement;
        }
        return null;
    }

    #endregion
}
Run Code Online (Sandbox Code Playgroud)

最后一首歌课:

<Window x:Class="IDevign.DataGrids.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dataGrids="clr-namespace:IDevign.DataGrids"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid x:Name="dataGrid" AutoGenerateColumns="False" MouseDoubleClick="Button_MouseDoubleClick" PreviewKeyDown="DataGrid_OnPreviewKeyDown">
            <DataGrid.Resources>
                <Style TargetType="{x:Type DataGridCell}">
                    <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown"></EventSetter>
                </Style>
            </DataGrid.Resources>
            <DataGrid.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Edit" Click="MenuItem_OnClick"></MenuItem>
                </ContextMenu>
            </DataGrid.ContextMenu>
            <DataGrid.Columns>
                <DataGridTextColumn Header="Recording Name" Binding="{Binding Name}"/>
                <DataGridTextColumn Header="Duration"  Binding="{Binding Duration}" IsReadOnly="True"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)