Programming

datagridview에 대한 오른쪽 클릭 컨텍스트 메뉴

procodes 2020. 8. 10. 20:45
반응형

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상대 값을 제공 하므로 컨텍스트 메뉴를 팝업하는 데 사용하기 쉽지 않습니다.)

This is the code I used to get the mouse position, then adjust for the position of the DataGridView:

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

The entire event handler looks like this:

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

  • Put a context menu on your form, name it, set captions etc. using the built-in editor
  • Link it to your grid using the grid property ContextMenuStrip
  • For your grid, create an event to handle CellContextMenuStripNeeded
  • The Event Args e has useful properties e.ColumnIndex, e.RowIndex.

I believe that e.RowIndex is what you are asking for.

Suggestion: when user causes your event CellContextMenuStripNeeded to fire, use e.RowIndex to get data from your grid, such as the ID. Store the ID as the menu event's tag item.

Now, when user actually clicks your menu item, use the Sender property to fetch the tag. Use the tag, containing your ID, to perform the action you need.


Simply drag a ContextMenu or ContextMenuStrip component into your form and visually design it, then assign it to the ContextMenu or ContextMenuStrip property of your desired control.


Follow the steps:

  1. Create a context menu like: Sample context menu

  2. User needs to right click on the row to get this menu. We need to handle the _MouseClick event and _CellMouseDown event.

selectedBiodataid is the variable that contains the selected row information.

Here is the code:

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

        }
    }
}

and the output would be:

Final output


For the position for the context menu, y found the problem that I needed a it to be relative to the DataGridView, and the event I needed to use gives the poistion relative to the cell clicked. I haven't found a better solution so I implemented this function in the commons class, so I call it from wherever I need.

It's quite tested and works well. I Hope you find it useful.

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

참고URL : https://stackoverflow.com/questions/1718389/right-click-context-menu-for-datagridview

반응형