web-dev-qa-db-ja.com

WPFデータグリッドのコンテキストメニューの取得行をクリック

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>

私はクリックイベントハンドラを次のようにしています:

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

クリックする前にコンテキストメニューがあった行を取得するにはどうすればよいですか? senderオブジェクトはSystem.Windows.Controls.MenuItemDataGridRowではありません。コンテキストメニューがクリックされたDataGridRowを取得するにはどうすればよいですか。 DataGrid.ItemSourceコードビハインドファイル内。)

20
O.O.

したがって、サンプルコードに基づいて、DataGridをオブジェクトのObservableCollectionにバインドし、そのオブジェクトのプロパティSiteおよびSubjectをDataGridColumnsにバインドするとします。

基本的に、必要なことは、クリックされた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);
}
29
dsfgsho

通常、行を処理することはありません(処理する場合-理由をもう一度考えてください)-代わりにビューモデルを使用します。コンテキストメニューを開くと、アイテムが選択されているので、DataGrid.SelectedItemプロパティを介してアクセスできます。ただし、本当にDataGridRowが必要な場合-あなたのDataGrid.SelectedIndexがあり、SOで行を取得する方法について多くの回答があります Get row in datagrid)

7
morincer

上記のモリンサーのポイントを例で拡大すると、私はより簡単なアプローチになりました...

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

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

私の場合、

_importViewModel.SourceList 

行がバインドされているObservableCollectionです。したがって、ベストプラクティスに従って、選択したアイテムをコレクションから削除するだけで、バインディングがUIを処理します。

3
ebol2000

dsfgshoの答えは私にとってはうまくいきましたが、グリッド行を右クリックしても自動的には選択されません。つまり、フォーカスが別の場所にあり、右クリックしてコンテキストメニュー項目を選択すると、item.SelectedCells [0]で範囲外の例外が発生するか、行を選択して別の行を右クリックすると、行では、予期しない結果が生じる可能性があります。

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();
        }
    }
2
Dave Smash

Elemental PeteのUIHelperはおそらく以下のものからきています:

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

この記事には、UIHelper.csを含むZipがリストされています。それは私のコードではないので、ここにコピー/貼り付けしません。

0
Patrick Artner

dsfgsho からの受け入れられた回答は理にかなっていますが、明示的なClickイベントではなく標準のApplicationCommandsにCommandBindingを使用する場合、送信者はMenuItemではなくDataGrid自体であるため、少し異なります。

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>

分離コード:

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;
}
0
IronRod