[c#] datagridview에 대한 오른쪽 클릭 컨텍스트 메뉴

.NET winform 앱에 datagridview가 있습니다. 행을 마우스 오른쪽 버튼으로 클릭하고 메뉴 팝업을 표시하고 싶습니다. 그런 다음 복사, 유효성 검사 등과 같은 것을 선택하고 싶습니다.

A) 메뉴 팝업 B) 마우스 오른쪽 버튼으로 클릭 한 행을 찾으려면 어떻게합니까? selectedIndex를 사용할 수 있다는 것을 알고 있지만 선택한 항목을 변경하지 않고 마우스 오른쪽 버튼을 클릭 할 수 있어야합니까? 지금은 선택한 인덱스를 사용할 수 있지만 선택한 항목을 변경하지 않고 데이터를 가져올 수있는 방법이 있다면 유용 할 것입니다.



답변

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));

    }
}


답변

이 질문은 오래되었지만 대답은 적절하지 않습니다. 상황에 맞는 메뉴에는 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    
        }
    }
}


답변

에서 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);
        }
    }
}


답변

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

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

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

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


답변

ContextMenu 또는 ContextMenuStrip 구성 요소를 폼으로 끌어서 시각적으로 디자인 한 다음 원하는 컨트롤의 ContextMenu 또는 ContextMenuStrip 속성에 할당하기 만하면됩니다.


답변

다음 단계를 따르십시오.

  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)
        {

        }
    }
}

출력은 다음과 같습니다.

최종 출력


답변

상황에 맞는 메뉴의 위치에 대해 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);
    }