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 이벤트는 현재 셀 또는 선택한 셀에 따라 표시 할 항목을 결정할 수있는 기회를 제공합니다. 다른 답변 중 하나를 참조하십시오
헤럴드 Coppoolse

4
올바른 화면 좌표를 얻으려면 다음과 같은 컨텍스트 메뉴를 열어야합니다.m.Show(dataGridView1.PointToScreen(e.Location));
Olivier Jacot-Descombes

메뉴 항목에 기능을 추가하려면 어떻게합니까?
알파 가브리엘 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행당 다른 메뉴를 사용하려는 경우 표시 할 컨텍스트 메뉴를 지정할 수있는 위치를 사용하는 예 입니다.

이러한 맥락에서 MultiSelect이다 True하고 SelectionMode있다 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
포괄적 인 답변 및 접근성 고려 (및 3 년 이전 질문에 대한 답변)에 +1
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

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

확인 된 답변은 여러 화면에서 제대로 작동하지 않지만이 답변은 작동합니다.
Furkan Ekinci

45
  • 기본 제공 편집기를 사용하여 양식에 컨텍스트 메뉴를 넣고 이름을 지정하고 캡션을 설정합니다.
  • grid 속성을 사용하여 그리드에 연결 ContextMenuStrip
  • 그리드의 경우 처리 할 이벤트를 만듭니다. CellContextMenuStripNeeded
  • Event Args e에는 유용한 속성 e.ColumnIndexe.RowIndex있습니다.

나는 e.RowIndex그것이 당신이 요구하는 것이라고 믿습니다 .

제안 : 사용자가 이벤트 CellContextMenuStripNeeded를 실행 e.RowIndex하면 ID와 같은 그리드에서 데이터를 가져 오는 데 사용 합니다. 메뉴 이벤트의 태그 항목으로 ID를 저장합니다.

이제 사용자가 실제로 메뉴 항목을 클릭하면 Sender 속성을 사용하여 태그를 가져옵니다. ID가 포함 된 태그를 사용하여 필요한 작업을 수행하십시오.


5
충분히 찬성 할 수 없습니다. 다른 대답은 나에게 분명했지만 컨텍스트 메뉴에 대한 기본 제공 지원이 더 있다는 것을 알 수있었습니다 (DataGrid뿐만 아니라). 이것은 정답입니다.
Jonathan Wood

1
@ActualRandy, 사용자가 실제 상황에 맞는 메뉴를 클릭 할 때 태그를 어떻게 얻습니까? CellcontexMenustripNeeded 이벤트에서 다음과 같은 내용이 있습니다. contextMenuStrip1.Tag = e.RowIndex;
Edwin Ikechukwu Okonkwo

2
이 답변은 거의 있지만 상황에 맞는 메뉴를 그리드 속성 ContextMenuStrip에 연결하지 않는 것이 좋습니다. 대신 CellContextMenuStripNeeded이벤트 처리기 내부에서 다음을 수행합니다. if(e.RowIndex >= 0){e.ContextMenuStrip = yourContextMenuInstance;}이것은 유효한 행을 마우스 오른쪽 버튼으로 클릭 할 때만 메뉴가 표시됨을 의미합니다 (예 : 제목이나 빈 그리드 영역이 아님)
James S

이 매우 유용한 답변에 대한 의견과 마찬가지로 CellContextMenuStripNeededDGV가 데이터 소스에 바인딩되어 있거나 VirtualMode가 true로 설정된 경우에만 작동합니다. 다른 경우에는 CellMouseDown이벤트 에서 해당 태그를 설정해야합니다 . 안전한쪽에 있으려면 DataGridView.HitTestInfoMouseDown 이벤트 처리기에서를 수행 하여 셀에 있는지 확인하십시오.
LocEngineer 2017-10-20

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

상황에 맞는 메뉴의 위치에 대해 y는 DataGridView를 기준으로해야하는 문제를 발견했으며 사용하는 데 필요한 이벤트는 클릭 한 셀에 상대적인 위치를 제공합니다. 더 나은 해결책을 찾지 못했기 때문에이 함수를 커먼즈 클래스에서 구현 했으므로 필요한 곳에서 호출합니다.

꽤 테스트되었으며 잘 작동합니다. 도움이 되셨기를 바랍니다.

    /// <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.