datagridview için sağ tıklama bağlam menüsü


117

.NET winform uygulamasında bir veri görünümüm var. Bir satırı sağ tıklayıp bir menünün açılmasını istiyorum. Sonra kopyala, doğrula vb. Gibi şeyler seçmek istiyorum.

A) bir menü açılır B) hangi satırın sağ tıklandığını nasıl bulurum. SelectedIndex'i kullanabileceğimi biliyorum ama seçileni değiştirmeden sağ tıklayabilmeliyim? şu anda seçili dizini kullanabilirim, ancak verileri seçili olanı değiştirmeden almanın bir yolu varsa, bu yararlı olacaktır.

Yanıtlar:


143

Farenin şu anda üzerinde gezindiği satır numarasını izlemek için CellMouseEnter ve CellMouseLeave'i kullanabilirsiniz.

Ardından, geçerli satır için özelleştirilmiş açılır menüyü görüntülemek için bir ContextMenu nesnesi kullanın.

İşte ne demek istediğimin hızlı ve kirli bir örneği ...

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
Doğru! ve sizin için bir not, var r = dataGridView1.HitTest (eX, eY); r.RowIndex, fareyi veya currentMouseOverRow'u kullanarak ÇOK DAHA İYİ çalışır

3
string.Format içinde .ToString () kullanmak gereksizdir.
MS

19
Bu yöntem eskidir: bir datagridview şu özelliğe sahiptir: ContextMenu. Operatör sağ tıklar tıklamaz bağlam menüsü açılacaktır. Karşılık gelen ContextMenuOpening olayı, geçerli hücreye veya seçili hücrelere bağlı olarak neyin gösterileceğine karar verme fırsatı sunar. Diğer cevaplardan birini görün
Harald Coppoolse

4
Doğru ekran düzenleyicilerini elde etmek için içerik menüsünü şu şekilde açmalısınız:m.Show(dataGridView1.PointToScreen(e.Location));
Olivier Jacot-Descombes

menü öğelerine nasıl işlev eklerim?
Alpha Gabriel V.Timbol

89

Bu soru eski olsa da cevaplar uygun değil. Bağlam menülerinin DataGridView üzerinde kendi olayları vardır. Satır bağlam menüsü ve hücre bağlam menüsü için bir olay var.

Bu cevapların uygun olmamasının nedeni, farklı işlem şemalarını hesaba katmamalarıdır. Erişilebilirlik seçenekleri, uzak bağlantılar veya Metro / Mono / Web / WPF bağlantı noktası çalışmayabilir ve klavye kısayolları aşağı doğru başarısız olur (Shift + F10 veya Bağlam Menüsü tuşu).

Sağ fare tıklamasıyla hücre seçimi manuel olarak yapılmalıdır. Bağlam menüsünün gösterilmesi, kullanıcı arabirimi tarafından işlendiği için işlenmesine gerek yoktur.

Bu, Microsoft Excel tarafından kullanılan yaklaşımı tamamen taklit eder. Hücre seçilen aralığın parçasıysa, hücre seçimi değişmez ve değişmez CurrentCell. Değilse, eski aralık temizlenir ve hücre seçilir ve olur CurrentCell.

Bu konuda net CurrentCelldeğilseniz, ok tuşlarına bastığınızda klavyenin odaklandığı yerdir. Selectedparçası olup olmadığıdır SelectedCells. Bağlam menüsü, kullanıcı arabirimi tarafından işlendiği şekliyle sağ tıklamada gösterilecektir.

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

Klavye kısayolları bağlam menüsünü varsayılan olarak göstermez, bu yüzden onları eklememiz gerekir.

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

Bu kodu statik olarak çalışacak şekilde yeniden çalıştım, böylece bunları kopyalayıp herhangi bir etkinliğe yapıştırabilirsiniz.

Anahtar, CellContextMenuStripNeededsize bağlam menüsünü vereceği için kullanmaktır .

CellContextMenuStripNeededHer satırda farklı menülerin olmasını istiyorsanız hangi bağlam menüsünün gösterileceğini belirleyebileceğiniz bir örnek aşağıda verilmiştir .

Bu bağlamda MultiSelectolduğunu Trueve SelectionModebir FullRowSelect. Bu sadece örnek içindir ve bir sınırlama değildir.

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
Kapsamlı yanıt ve erişilebilirliği düşünmek için +1 (ve 3 yıllık bir soruyu yanıtlamak için)
gt

3
Kabul edildi, bu kabul edilenden çok daha iyi (hiçbirinde gerçekten yanlış bir şey olmamasına rağmen) - ve klavye desteğini dahil etmek için daha da fazla övgü, pek çok insanın düşünmediği bir şey.
Richard Moss

2
Harika yanıt, tüm esnekliği sağlar: tıklanan şeye bağlı olarak farklı bağlam menüleri. Ve tam olarak EXCEL davranışı
Harald Coppoolse

2
Bu yöntemin hayranı değilim çünkü basit DataGridView ile bir veri kaynağı veya sanal mod kullanmıyorum. The CellContextMenuStripNeeded event occurs only when the DataGridView control DataSource property is set or its VirtualMode property is true.
Arvo Bowen

47

Kullanım CellMouseDownetkinliği DataGridView. Olay işleyici bağımsız değişkenlerinden hangi hücrenin tıklandığını belirleyebilirsiniz. PointToClient()DataGridView'daki yöntemi kullanarak, işaretçinin DataGridView'a göre konumunu belirleyebilir, böylece menüyü doğru konumda açabilirsiniz.

( DataGridViewCellMouseEventParametre sadece verir Xve Ybağlam menüsünü açmak için kullanımı kolay olarak değil tıkladığınız hücreye göre.)

Bu, farenin konumunu almak için kullandığım koddur, ardından DataGridView'in konumunu ayarlar:

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

Tüm olay işleyicisi şuna benzer:

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];Hücreye daha basit bir çağrı için de kullanabilirsiniz .
Qsiris

Kontrol edilen cevap birden fazla ekranda doğru çalışmıyor, ancak bu cevap çalışıyor.
Furkan Ekinci

45
  • Yerleşik düzenleyiciyi kullanarak formunuza bir bağlam menüsü ekleyin, adlandırın, başlıkları ayarlayın vb
  • Izgara özelliğini kullanarak onu ızgaranıza bağlayın ContextMenuStrip
  • Izgaranız için işlenecek bir olay oluşturun CellContextMenuStripNeeded
  • Olay Args e kullanışlı özelliklere sahiptir e.ColumnIndex, e.RowIndex.

İstediğin e.RowIndexşeyin bu olduğuna inanıyorum .

Öneri: Kullanıcı, etkinliğinizin tetiklenmesine neden olduğunda , kimlik gibi verileri şebekenizden almak için CellContextMenuStripNeededkullanın e.RowIndex. Kimliği menü etkinliğinin etiket öğesi olarak kaydedin.

Şimdi, kullanıcı gerçekten menü öğenizi tıkladığında, etiketi getirmek için Gönderen özelliğini kullanın. İhtiyacınız olan eylemi gerçekleştirmek için kimliğinizi içeren etiketi kullanın.


5
Buna yeterince olumlu oy veremiyorum. Diğer cevaplar benim için açıktı, ancak bağlam menüleri için daha fazla yerleşik destek olduğunu söyleyebilirim (ve sadece DataGrid için değil). Bu doğru cevap.
Jonathan Wood

1
@ActualRandy, kullanıcı gerçek içerik menüsünü tıkladığında etiketi nasıl alırım? CellcontexMenustripNeeded olayı altında, so contextMenuStrip1.Tag = e.RowIndex gibi bir şeye sahibim;
Edwin Ikechukwu Okonkwo

2
Bu cevap neredeyse orada, ancak bağlam menüsünü ContextMenuStrip ızgara özelliğine BAĞLAMAMANIZI öneririm. Bunun yerine CellContextMenuStripNeededolay işleyicisinin içinde yap if(e.RowIndex >= 0){e.ContextMenuStrip = yourContextMenuInstance;}Bu, menünün yalnızca geçerli bir satıra sağ tıklandığında gösterileceği anlamına gelir (yani başlık veya boş ızgara alanında değil)
James S

Bu çok yararlı yanıta bir yorum olarak: CellContextMenuStripNeededyalnızca DGV'niz bir veri kaynağına bağlıysa veya VirtualMode değeri true olarak ayarlanmışsa çalışır. Diğer durumlarda, CellMouseDownetkinlikte bu etiketi ayarlamanız gerekecektir . Orada güvenli tarafta DataGridView.HitTestInfoolmak için, bir hücrede olduğunuzu kontrol etmek için MouseDown olay işleyicisinde bir gerçekleştirin .
LocEngineer

6

Bir ContextMenu veya ContextMenuStrip bileşenini formunuza sürükleyin ve görsel olarak tasarlayın, ardından bunu istediğiniz kontrolün ContextMenu veya ContextMenuStrip özelliğine atayın.


4

Adımları takip et:

  1. Şunun gibi bir bağlam menüsü oluşturun: Örnek içerik menüsü

  2. Kullanıcının bu menüyü almak için satırı sağ tıklaması gerekir. _MouseClick olayını ve _CellMouseDown olayını işlememiz gerekiyor.

selectedBiodataid, seçilen satır bilgilerini içeren değişkendir.

İşte kod:

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

        }
    }
}

ve çıktı:

Nihai çıktı


3

Bağlam menüsünün konumu için y, DataGridView'e göre ihtiyacım olan sorunu buldu ve kullanmam gereken olay, tıklanan hücreye göre konumu veriyor. Daha iyi bir çözüm bulamadım, bu yüzden bu işlevi commons sınıfında uyguladım, bu yüzden onu ihtiyacım olan her yerden arıyorum.

Oldukça test edilmiş ve iyi çalışıyor. Umarım yararlı bulursunuz.

    /// <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);
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.