datagridviewの右クリックコンテキストメニュー


116

.NET winformアプリにdatagridviewがあります。行を右クリックしてメニューをポップアップしたいのですが。次に、コピー、検証などを選択します

A)メニューポップアップB)右クリックされた行を見つける方法 selectedIndexを使用できることはわかっていますが、選択内容を変更せずに右クリックできるはずですか?現時点では選択したインデックスを使用できますが、選択したものを変更せずにデータを取得する方法がある場合は、それが便利です。

回答:


143

CellMouseEnterおよびCellMouseLeaveを使用して、マウスが現在ホバーしている行番号を追跡できます。

次に、ContextMenuオブジェクトを使用して、現在の行に合わせてカスタマイズされたポップアップメニューを表示します。

ここに私が意味することの迅速で汚い例があります...

private void dataGridView1_MouseClick(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Right)
    {
        ContextMenu m = new ContextMenu();
        m.MenuItems.Add(new MenuItem("Cut"));
        m.MenuItems.Add(new MenuItem("Copy"));
        m.MenuItems.Add(new MenuItem("Paste"));

        int currentMouseOverRow = dataGridView1.HitTest(e.X,e.Y).RowIndex;

        if (currentMouseOverRow >= 0)
        {
            m.MenuItems.Add(new MenuItem(string.Format("Do something to row {0}", currentMouseOverRow.ToString())));
        }

        m.Show(dataGridView1, new Point(e.X, e.Y));

    }
}

6
正しい!そしてあなたのためのメモ、var r = dataGridView1.HitTest(eX、eY); r.RowIndexは、マウスまたはcurrentMouseOverRowを使用して、より良い方法で機能します

3
string.Formatで.ToString()を使用する必要はありません。
MS

19
このメソッドは古いです。datagridviewにはプロパティContextMenuがあります。オペレーターが右クリックするとすぐに、コンテキストメニューが開きます。対応するContextMenuOpeningイベントを使用すると、現在のセルまたは選択したセルに応じて、何を表示するかを決定できます。他の回答の1つを見る
Harald Coppoolse

4
:右画面coordiantesを取得するためには、このようなコンテキストメニュー開く必要がありますm.Show(dataGridView1.PointToScreen(e.Location));
オリヴィエJacot-Descombes

メニューアイテムに関数を追加するにはどうすればよいですか?
Alpha Gabriel V. Timbol

89

この質問は古いですが、答えは適切ではありません。コンテキストメニューには、DataGridViewに独自のイベントがあります。行のコンテキストメニューとセルのコンテキストメニューのイベントがあります。

これらの回答が適切でない理由は、それらが異なる操作スキームを考慮していないためです。アクセシビリティオプション、リモート接続、またはMetro / Mono / Web / WPFポーティングが機能せず、キーボードショートカットが正しく機能しなくなる(Shift + F10またはコンテキストメニューキー)。

マウスの右クリックでのセル選択は手動で処理する必要があります。コンテキストメニューの表示はUIによって処理されるため、処理する必要はありません。

これは、Microsoft Excelで使用されているアプローチを完全に模倣しています。セルが選択範囲の一部である場合、セルの選択は変更されず、どちらも変更されませんCurrentCell。そうでない場合は、古い範囲がクリアされ、セルが選択されてになりCurrentCellます。

これがよくわからない場合はCurrentCell、矢印キーを押したときにキーボードにフォーカスがある場所です。Selectedの一部であるかどうかですSelectedCells。右クリックでコンテキストメニューが表示され、UIで処理されます。

private void dgvAccount_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
    if (e.ColumnIndex != -1 && e.RowIndex != -1 && e.Button == System.Windows.Forms.MouseButtons.Right)
    {
        DataGridViewCell c = (sender as DataGridView)[e.ColumnIndex, e.RowIndex];
        if (!c.Selected)
        {
            c.DataGridView.ClearSelection();
            c.DataGridView.CurrentCell = c;
            c.Selected = true;
        }
    }
}

キーボードショートカットはデフォルトではコンテキストメニューを表示しないため、追加する必要があります。

private void dgvAccount_KeyDown(object sender, KeyEventArgs e)
{
    if ((e.KeyCode == Keys.F10 && e.Shift) || e.KeyCode == Keys.Apps)
    {
        e.SuppressKeyPress = true;
        DataGridViewCell currentCell = (sender as DataGridView).CurrentCell;
        if (currentCell != null)
        {
            ContextMenuStrip cms = currentCell.ContextMenuStrip;
            if (cms != null)
            {
                Rectangle r = currentCell.DataGridView.GetCellDisplayRectangle(currentCell.ColumnIndex, currentCell.RowIndex, false);
                Point p = new Point(r.X + r.Width, r.Y + r.Height);
                cms.Show(currentCell.DataGridView, p);
            }
        }
    }
}

このコードを静的に機能するように作り直したので、それらをコピーして任意のイベントに貼り付けることができます。

キーは使用することです CellContextMenuStripNeededコンテキストメニューが表示さ、。

これは使用例です CellContextMenuStripNeeded行ごとに異なるメニューを表示する場合に表示するコンテキストメニューを指定できる示します。

このコンテキストでMultiSelectTrueSelectionModeですFullRowSelect。これは単なる例であり、制限ではありません。

private void dgvAccount_CellContextMenuStripNeeded(object sender, DataGridViewCellContextMenuStripNeededEventArgs e)
{
    DataGridView dgv = (DataGridView)sender;

    if (e.RowIndex == -1 || e.ColumnIndex == -1)
        return;
    bool isPayment = true;
    bool isCharge = true;
    foreach (DataGridViewRow row in dgv.SelectedRows)
    {
        if ((string)row.Cells["P/C"].Value == "C")
            isPayment = false;
        else if ((string)row.Cells["P/C"].Value == "P")
            isCharge = false;
    }
    if (isPayment)
        e.ContextMenuStrip = cmsAccountPayment;
    else if (isCharge)
        e.ContextMenuStrip = cmsAccountCharge;
}

private void cmsAccountPayment_Opening(object sender, CancelEventArgs e)
{
    int itemCount = dgvAccount.SelectedRows.Count;
    string voidPaymentText = "&Void Payment"; // to be localized
    if (itemCount > 1)
        voidPaymentText = "&Void Payments"; // to be localized
    if (tsmiVoidPayment.Text != voidPaymentText) // avoid possible flicker
        tsmiVoidPayment.Text = voidPaymentText;
}

private void cmsAccountCharge_Opening(object sender, CancelEventArgs e)
{
    int itemCount = dgvAccount.SelectedRows.Count;
    string deleteChargeText = "&Delete Charge"; //to be localized
    if (itemCount > 1)
        deleteChargeText = "&Delete Charge"; //to be localized
    if (tsmiDeleteCharge.Text != deleteChargeText) // avoid possible flicker
        tsmiDeleteCharge.Text = deleteChargeText;
}

private void tsmiVoidPayment_Click(object sender, EventArgs e)
{
    int paymentCount = dgvAccount.SelectedRows.Count;
    if (paymentCount == 0)
        return;

    bool voidPayments = false;
    string confirmText = "Are you sure you would like to void this payment?"; // to be localized
    if (paymentCount > 1)
        confirmText = "Are you sure you would like to void these payments?"; // to be localized
    voidPayments = (MessageBox.Show(
                    confirmText,
                    "Confirm", // to be localized
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button2
                   ) == DialogResult.Yes);
    if (voidPayments)
    {
        // SQLTransaction Start
        foreach (DataGridViewRow row in dgvAccount.SelectedRows)
        {
            //do Work    
        }
    }
}

private void tsmiDeleteCharge_Click(object sender, EventArgs e)
{
    int chargeCount = dgvAccount.SelectedRows.Count;
    if (chargeCount == 0)
        return;

    bool deleteCharges = false;
    string confirmText = "Are you sure you would like to delete this charge?"; // to be localized
    if (chargeCount > 1)
        confirmText = "Are you sure you would like to delete these charges?"; // to be localized
    deleteCharges = (MessageBox.Show(
                    confirmText,
                    "Confirm", // to be localized
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button2
                   ) == DialogResult.Yes);
    if (deleteCharges)
    {
        // SQLTransaction Start
        foreach (DataGridViewRow row in dgvAccount.SelectedRows)
        {
            //do Work    
        }
    }
}

5
+1は包括的回答およびアクセス可能性の検討(および3年前の質問への回答)
gt

3
確かに、これは受け入れられたものよりもはるかに優れています(ただし、実際には何も問題はありません)。さらに、キーボードサポートを含めることに対する賛辞も多く、多くの人は考えていないようです。
Richard Moss

2
すばらしい答えです。すべての柔軟性が得られます。何がクリックされたかによって異なるコンテキストメニューが表示されます。そしてまさにEXCELの動作
Harald Coppoolse 2014

2
私の単純なDataGridViewではデータソースや仮想モードを使用しないため、私はこのメソッドのファンではありません。 The CellContextMenuStripNeeded event occurs only when the DataGridView control DataSource property is set or its VirtualMode property is true.
Arvo Bowen 2016

47

CellMouseDownイベントを使用しDataGridViewます。イベントハンドラーの引数から、クリックされたセルを特定できます。を使用してPointToClient()DataGridViewメソッドをと、DataGridViewへのポインターの相対位置を特定できるため、メニューを正しい場所にポップアップできます。

(このDataGridViewCellMouseEventパラメーターはXYコンテキストメニューをポップアップ表示するために使いやすいようではありませんあなたがクリックされたセル、と比較して。)

これは、マウスの位置を取得し、DataGridViewの位置を調整するために使用したコードです。

var relativeMousePosition = DataGridView1.PointToClient(Cursor.Position);
this.ContextMenuStrip1.Show(DataGridView1, relativeMousePosition);

イベントハンドラ全体は次のようになります。

private void DataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
    // Ignore if a column or row header is clicked
    if (e.RowIndex != -1 && e.ColumnIndex != -1)
    {
        if (e.Button == MouseButtons.Right)
        {
            DataGridViewCell clickedCell = (sender as DataGridView).Rows[e.RowIndex].Cells[e.ColumnIndex];

            // Here you can do whatever you want with the cell
            this.DataGridView1.CurrentCell = clickedCell;  // Select the clicked cell, for instance

            // Get mouse position relative to the vehicles grid
            var relativeMousePosition = DataGridView1.PointToClient(Cursor.Position);

            // Show the context menu
            this.ContextMenuStrip1.Show(DataGridView1, relativeMousePosition);
        }
    }
}

1
(sender as DataGridView)[e.ColumnIndex, e.RowIndex];セルへのより単純な呼び出しにも使用できます。
Qsiris 2012

チェックした回答が複数の画面で正しく機能しませんが、この回答は機能します。
Furkan Ekinci

45
  • 組み込みのエディタを使用して、フォームにコンテキストメニューを配置し、名前を付け、キャプションを設定するなど
  • gridプロパティを使用してグリッドにリンクします ContextMenuStrip
  • グリッドで、処理するイベントを作成します CellContextMenuStripNeeded
  • イベント引数eには、便利なプロパティがe.ColumnIndexありe.RowIndexます。

それe.RowIndexがあなたが求めていることだと思います。

提案:ユーザーがイベントCellContextMenuStripNeededを発生させたときに、を使用e.RowIndexしてグリッドからデータ(IDなど)を取得します。IDをメニューイベントのタグ項目として保存します。

ここで、ユーザーが実際にメニュー項目をクリックしたときに、Senderプロパティを使用してタグをフェッチします。IDを含むタグを使用して、必要なアクションを実行します。


5
私はこれを十分に賛成できない。他の答えは私には明らかでしたが、コンテキストメニュー(およびDataGridだけでなく)の組み込みサポートがより多くあることがわかりました。これが正解です。
ジョナサンウッド

1
@ActualRandy、ユーザーが実際のコンテキストメニューをクリックしたときにタグを取得するにはどうすればよいですか?CellcontexMenustripNeededイベントの下で、contextMenuStrip1.Tag = e.RowIndex;
Edwin Ikechukwu Okonkwo 2014

2
この答えはほとんどありますが、コンテキストメニューをグリッドプロパティContextMenuStripにリンクしないことをお勧めします。代わりに、CellContextMenuStripNeededイベントハンドラ内で行います。if(e.RowIndex >= 0){e.ContextMenuStrip = yourContextMenuInstance;}これは、有効な行を右クリックしたときにのみメニューが表示されることを意味します(つまり、見出しや空のグリッド領域ではありません)
James S

この非常に役立つ回答へのコメントとCellContextMenuStripNeeded同様に、DGVがデータソースにバインドされている場合、またはそのVirtualModeがtrueに設定されている場合にのみ機能します。他の場合では、CellMouseDownイベントでそのタグを設定する必要があります。そこで安全をDataGridView.HitTestInfo確保するには、MouseDownイベントハンドラーでを実行して、セル上にいることを確認します。
LocEngineer 2017年

6

単にContextMenuまたはContextMenuStripコンポーネントをフォームにドラッグして視覚的にデザインし、それを目的のコントロールのContextMenuまたはContextMenuStripプロパティに割り当てます。


4

手順に従ってください:

  1. 次のようなコンテキストメニューを作成します。 コンテキストメニューの例

  2. このメニューを表示するには、行を右クリックする必要があります。_MouseClickイベントと_CellMouseDownイベントを処理する必要があります。

selectedBiodataidは、選択された行情報を含む変数です。

これがコードです:

private void dgrdResults_MouseClick(object sender, MouseEventArgs e)
{   
    if (e.Button == System.Windows.Forms.MouseButtons.Right)
    {                      
        contextMenuStrip1.Show(Cursor.Position.X, Cursor.Position.Y);
    }   
}

private void dgrdResults_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
    //handle the row selection on right click
    if (e.Button == MouseButtons.Right)
    {
        try
        {
            dgrdResults.CurrentCell = dgrdResults.Rows[e.RowIndex].Cells[e.ColumnIndex];
            // Can leave these here - doesn't hurt
            dgrdResults.Rows[e.RowIndex].Selected = true;
            dgrdResults.Focus();

            selectedBiodataId = Convert.ToInt32(dgrdResults.Rows[e.RowIndex].Cells[1].Value);
        }
        catch (Exception)
        {

        }
    }
}

出力は次のようになります。

最終出力


3

コンテキストメニューの位置については、DataGridViewに関連する必要があるという問題が見つかりました。使用する必要があるイベントは、クリックされたセルに関連する位置を示します。より良い解決策が見つからなかったので、この関数をcommonsクラスに実装したので、必要な場所から呼び出します。

十分にテストされており、うまく動作します。お役に立てれば幸いです。

    /// <summary>
    /// When DataGridView_CellMouseClick ocurs, it gives the position relative to the cell clicked, but for context menus you need the position relative to the DataGridView
    /// </summary>
    /// <param name="dgv">DataGridView that produces the event</param>
    /// <param name="e">Event arguments produced</param>
    /// <returns>The Location of the click, relative to the DataGridView</returns>
    public static Point PositionRelativeToDataGridViewFromDataGridViewCellMouseEventArgs(DataGridView dgv, DataGridViewCellMouseEventArgs e)
    {
        int x = e.X;
        int y = e.Y;
        if (dgv.RowHeadersVisible)
            x += dgv.RowHeadersWidth;
        if (dgv.ColumnHeadersVisible)
            y += dgv.ColumnHeadersHeight;
        for (int j = 0; j < e.ColumnIndex; j++)
            if (dgv.Columns[j].Visible)
                x += dgv.Columns[j].Width;
        for (int i = 0; i < e.RowIndex; i++)
            if (dgv.Rows[i].Visible)
                y += dgv.Rows[i].Height;
        return new Point(x, y);
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.