C# 获取 WPF 数据网格上下文菜单单击行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16822956/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 07:52:43  来源:igfitidea点击:

Getting WPF Data Grid Context Menu Click Row

c#wpfwpfdatagridwpf-4.0

提问by O.O.

I have a WPF DataGrid

我有一个 WPF DataGrid

<DataGrid AutoGenerateColumns="False"  Name="dataGrid1"  IsReadOnly="True" >
<DataGrid.Columns>
    <DataGridTextColumn Header="Site" Binding="{Binding Site}" Width="150" />
    <DataGridTextColumn Header="Subject" Binding="{Binding Subject}" Width="310" />
</DataGrid.Columns>
<DataGrid.ContextMenu>
    <ContextMenu>
        <MenuItem Header="Delete" Click="Context_Delete">
            <MenuItem.Icon>
                <Image Width="12" Height="12" Source="Images/Delete.png" />
            </MenuItem.Icon>
        </MenuItem>
    </ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>

I have the click event handler as:

我有点击事件处理程序:

private void Context_Delete(object sender, System.EventArgs e)  { }

How do I get the row on which the Context Menu was before the click? The senderobject is System.Windows.Controls.MenuItem, not the DataGridRow. How do I get the DataGridRowwhere the Context Menu was clicked. (I set the DataGrid.ItemSourcein the code behind file.)

如何获取单击前上下文菜单所在的行?该sender对象是System.Windows.Controls.MenuItem,不是DataGridRow。如何获取DataGridRow单击上下文菜单的位置。(我DataGrid.ItemSource在代码隐藏文件中设置了。)

采纳答案by dsfgsho

So based on your example code, I presume you bind your DataGrid to an ObservableCollection of objects of which you bind the properties Site and Subject to the DataGridColumns.

因此,根据您的示例代码,我假设您将 DataGrid 绑定到对象的 ObservableCollection,将属性 Site 和 Subject 绑定到 DataGridColumns。

Essentially, all you need to do is figure out what the item bound to the clicked DataGridRow is and remove that from your ObservableCollection. Here is some example code to get you started:

本质上,您需要做的就是弄清楚绑定到单击的 DataGridRow 的项目是什么,并将其从 ObservableCollection 中删除。以下是一些示例代码,可帮助您入门:

private void Context_Delete(object sender, RoutedEventArgs e)
{
    //Get the clicked MenuItem
    var menuItem = (MenuItem)sender;

    //Get the ContextMenu to which the menuItem belongs
    var contextMenu = (ContextMenu)menuItem.Parent;

    //Find the placementTarget
    var item = (DataGrid)contextMenu.PlacementTarget;

    //Get the underlying item, that you cast to your object that is bound
    //to the DataGrid (and has subject and state as property)
    var toDeleteFromBindedList = (YourObject)item.SelectedCells[0].Item;

    //Remove the toDeleteFromBindedList object from your ObservableCollection
    yourObservableCollection.Remove(toDeleteFromBindedList);
}

回答by morincer

Typically, you do not deal with rows (if you do - think again about the reasons) - instead you work with view model. When you open context menu, you get your item selected, so it can be accessed via the DataGrid.SelectedItem property. However, if you really need DataGridRow - you have your DataGrid.SelectedIndex and there is a lot of answers here on SO on how to get the row. like Get row in datagrid

通常,您不处理行(如果您这样做了 - 再想想原因) - 而是使用视图模型。当您打开上下文菜单时,您会选择您的项目,因此可以通过 DataGrid.SelectedItem 属性访问它。但是,如果你真的需要 DataGridRow - 你有你的 DataGrid.SelectedIndex 并且这里有很多关于如何获取行的答案。像在数据网格中获取行

回答by ebol2000

To expand morincer's point above with an example, I ended up with a simpler approach...

为了用一个例子来扩展morincer的观点,我最终采用了一种更简单的方法......

 private void MenuItem_OnClickRemoveSource(object sender, RoutedEventArgs e)
 {
     if (SourceDataGrid.SelectedItem == null) return;  //safety first

     _importViewModel.SourceList.Remove((SourceFileInfo)SourceDataGrid.SelectedItem);
 }

In my case, the

就我而言,

_importViewModel.SourceList 

is the ObservableCollection the rows are bound to. So per best practices, I simple remove the selected item from the collection and the binding takes care of the UI.

是行绑定到的 ObservableCollection。所以根据最佳实践,我简单地从集合中删除所选项目,绑定负责 UI。

回答by Dave Smash

dsfgsho's answer worked for me, but right clicking on a grid row does not automatically select it. This means that if your focus is elsewhere and you right-click and select a context menu item, you can get an out of range exception on item.SelectedCells[0], or if you have a row selected and right-click on a different row, you may get unexpected results.

dsfgsho 的答案对我有用,但右键单击网格行不会自动选择它。这意味着如果您的焦点在别处并且您右键单击并选择上下文菜单项,您可能会在 item.SelectedCells[0] 上获得超出范围的异常,或者如果您选择了一行并右键单击不同的行,您可能会得到意想不到的结果。

I dealt with this by handling "PreviewMouseRightButtonDown" on the Datagrid. Here I am explicitly selecting a row when it is right-clicked. I forget where my UIHelpers class came from (probably elsewhere on this site - I was using it to resolve drag & drop items), but this should point you in the right direction if you are running into this problem. This is an extension of the accepted answer:

我通过处理 Datagrid 上的“PreviewMouseRightButtonDown”来解决这个问题。在这里,我在右键单击时明确选择了一行。我忘记了我的 UIHelpers 类来自哪里(可能在本网站的其他地方 - 我用它来解决拖放项目),但是如果您遇到这个问题,这应该为您指明正确的方向。这是已接受答案的扩展:

// handle right mouse click to select the correct item for context menu usage
    private void myDataGrid_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
    {
        //find the clicked row
        DataGridRow row = UIHelpers.TryFindFromPoint<DataGridRow>((UIElement) sender, e.GetPosition(myDataGrid));
        if (row == null)
        {
            Debug.WriteLine("Row is null");
            return;
        }
        else
        {
            Debug.WriteLine("Grid Row Index is " + row.GetIndex().ToString());
            (sender as DataGrid).SelectedIndex = row.GetIndex();
        }
    }

回答by Patrick Artner

Elemental Pete's UIHelper probably stemmed from:

Elemental Pete 的 UIHelper 可能源于:

http://www.hardcodet.net/2009/03/moving-data-grid-rows-using-drag-and-drop

http://www.hardcodet.net/2009/03/moving-data-grid-rows-using-drag-and-drop

This Article lists a Zip that contains UIHelper.cs. It's not my Code so no copy/paste here.

本文列出了一个包含 UIHelper.cs 的 Zip。这不是我的代码,所以这里没有复制/粘贴。

回答by IronRod

The accepted answer from dsfgshomakes sense but when using CommandBinding for the standard ApplicationCommands rather than an explicit Click event it is a little different as the sender is not the MenuItem but the DataGrid itself.

dsfgsho接受的答案是有道理的,但是当将 CommandBinding 用于标准 ApplicationCommands 而不是显式 Click 事件时,它有点不同,因为发送者不是 MenuItem 而是 DataGrid 本身。

XAML:

XAML:

<DataGrid.CommandBindings>
    <CommandBinding Command="Cut" CanExecute="DataGrid_CanCut" Executed="DataGrid_Cut" />
    <CommandBinding Command="Copy" CanExecute="DataGrid_CanCopy" Executed="DataGrid_Copy" />
    <CommandBinding Command="Paste" CanExecute="DataGrid_CanPaste" Executed="DataGrid_Paste" />
    <CommandBinding Command="New" CanExecute="DataGrid_CanAddNew" Executed="DataGrid_AddNew" />
    <CommandBinding Command="Delete" CanExecute="DataGrid_CanDelete" Executed="DataGrid_Delete" />
</DataGrid.CommandBindings>
<DataGrid.ContextMenu>
    <ContextMenu>
        <MenuItem Command="Cut" />
        <MenuItem Command="Copy" />
        <MenuItem Command="Paste" />
        <MenuItem Command="New" />
        <MenuItem Command="Delete" />
        <Separator />
        <MenuItem Header="Test" Command="{Binding CustomContextCommand}" />
    </ContextMenu>
</DataGrid.ContextMenu>

Code Behind:

背后的代码:

private void DataGrid_Delete(object sender, ExecutedRoutedEventArgs e)
{
    // Test whether cleared, resolved, etc., and confirm deletion
    var datagrid = (DataGrid)sender;
    var trans = (DataClasses.BankTransaction)datagrid.SelectedCells[0].Item;

    // Take action here; e.g., remove it from the underlying collection, remove it
    // from the DB, etc.

    e.Handled = true;
}

private void DataGrid_CanDelete(object sender, CanExecuteRoutedEventArgs e)
{
     e.CanExecute = true;
     e.Handled = true;
}