datagridview के लिए राइट क्लिक संदर्भ मेनू


116

मेरे पास .NET winform ऐप में एक डेटाग्रेडव्यू है। मैं एक पंक्ति पर राइट क्लिक करना चाहता हूं और एक मेनू पॉप अप करूंगा। फिर मैं कॉपी, वैलिडेट आदि जैसी चीजों का चयन करना चाहूंगा

मैं A कैसे बनाते हैं) B का मेनू पॉप अप होता है) कौन सी पंक्ति सही क्लिक की गई थी। मुझे पता है कि मैं सिलेक्टेडेक्स का उपयोग कर सकता हूं, लेकिन जो चुना गया है उसे बदले बिना राइट क्लिक करने में सक्षम होना चाहिए? अभी मैं चयनित इंडेक्स का उपयोग कर सकता हूं, लेकिन अगर जो चुना गया है उसे बदले बिना डेटा प्राप्त करने का एक तरीका है तो यह उपयोगी होगा।

जवाबों:


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
का उपयोग कर। स्ट्रिंग में (।) स्ट्रिंग। अनावश्यक रूप से है।
MS

19
यह तरीका पुराना है: एक डेटाग्रिड्यू में एक संपत्ति है: कॉन्टेक्स्टमेन्यू। जैसे ही ऑपरेटर राइट क्लिक करेगा संदर्भ मेनू खुल जाएगा। संबंधित प्रसंगमेनूओपनिंग इवेंट आपको यह तय करने का अवसर देता है कि वर्तमान सेल या चयनित कोशिकाओं के आधार पर क्या दिखाया जाए। अन्य उत्तरों में से एक देखें
हेराल्ड कोप्पुलसे

4
सही स्क्रीन निर्देशांक प्राप्त करने के लिए आपको संदर्भ मेनू को इस तरह से खोलना चाहिए:m.Show(dataGridView1.PointToScreen(e.Location));
ओलिवियर जैकोट-डेसॉम्बे नोव

मैं मीनू में फ़ंक्शन कैसे जोड़ूं?
अल्फा गैब्रियल वी। टिंबोल

89

हालांकि यह प्रश्न पुराना है, उत्तर उचित नहीं हैं। प्रसंग मेनू के DataGridView पर अपने स्वयं के कार्यक्रम हैं। पंक्ति संदर्भ मेनू और सेल संदर्भ मेनू के लिए एक घटना है।

जिस कारण से ये उत्तर उचित नहीं हैं, वे विभिन्न ऑपरेशन योजनाओं के लिए जिम्मेदार नहीं हैं। एक्सेसिबिलिटी विकल्प, रिमोट कनेक्शन या मेट्रो / मोनो / वेब / डब्ल्यूपीएफ पोर्टिंग काम नहीं कर सकती है और कीबोर्ड शॉर्टकट राइट फेल (शिफ्ट + एफ 10 या कॉन्टेक्स्ट मेन्यू की) नीचे आ जाएंगे।

सही माउस क्लिक पर सेल चयन मैन्युअल रूप से नियंत्रित किया जाना है। संदर्भ मेनू को दिखाने की आवश्यकता नहीं है क्योंकि यह UI द्वारा नियंत्रित किया जाता है।

यह Microsoft Excel द्वारा उपयोग किए गए दृष्टिकोण की पूरी तरह से नकल करता है। यदि कोई सेल चयनित श्रेणी का हिस्सा है, तो सेल चयन नहीं बदलता है और न ही करता है CurrentCell। यदि ऐसा नहीं है, तो पुरानी सीमा को हटा दिया जाता है और सेल का चयन कर लिया जाता है CurrentCell

यदि आप इस पर अस्पष्ट हैं, CurrentCellतो तीर कुंजियों को दबाते समय कीबोर्ड पर ध्यान केंद्रित किया गया है। Selectedक्या यह इसका हिस्सा है SelectedCells। संदर्भ मेनू यूआई द्वारा नियंत्रित के रूप में राइट क्लिक पर दिखाई देगा।

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
+1 व्यापक उत्तर के लिए और accesibility पर विचार करने के लिए (और एक 3 साल पुराने सवाल का जवाब देने के लिए)
gt

3
सहमत, यह स्वीकार किए गए की तुलना में बहुत बेहतर है (हालांकि उनमें से किसी के साथ वास्तव में कुछ भी गलत नहीं है) - और कीबोर्ड समर्थन सहित के लिए और भी कुछ यश, ऐसा बहुत से लोगों को लगता है कि अभी नहीं लगता है।
रिचर्ड मॉस

2
महान जवाब, सभी लचीलेपन देता है: क्लिक किए जाने वाले पर निर्भर करता है कि अलग-अलग संदर्भ मेनू। और ठीक EXCEL व्यवहार
हैराल्ड कोप्पुलसे

2
मैं इस पद्धति का प्रशंसक नहीं हूं क्योंकि मेरे सरल डेटाग्रिड्यू के साथ मैं एक डेटा स्रोत या वर्चुअलमोड का उपयोग नहीं करता हूं। The CellContextMenuStripNeeded event occurs only when the DataGridView control DataSource property is set or its VirtualMode property is true.
अरवो बोवेन

47

CellMouseDownपर घटना का प्रयोग करें DataGridView। इवेंट हैंडलर के तर्कों से आप यह निर्धारित कर सकते हैं कि किस सेल पर क्लिक किया गया था। का उपयोग करते हुएPointToClient()DataGridView पर विधि आप DataGridView को सूचक की सापेक्ष स्थिति निर्धारित कर सकते हैं, इसलिए आप मेनू को सही स्थान पर पॉप अप कर सकते हैं।

( DataGridViewCellMouseEventपैरामीटर केवल आपके देता है Xऔर Yसेल आप क्लिक किया है, जो प्रसंग मेनू पॉप अप करने के लिए उपयोग करने के लिए आसान के रूप में नहीं है के सापेक्ष।)

यह वह कोड है जिसका उपयोग मैंने माउस पोजीशन प्राप्त करने के लिए किया था, फिर 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

चेक किया गया उत्तर कई स्क्रीन पर सही ढंग से काम नहीं करता है लेकिन यह उत्तर काम करता है।
फुरकान एकिनसी

45
  • अपने प्रपत्र पर एक संदर्भ मेनू रखो, इसे नाम दें, कैप्शन सेट करें आदि अंतर्निहित संपादक का उपयोग कर
  • इसे ग्रिड संपत्ति का उपयोग करके अपने ग्रिड से लिंक करें ContextMenuStrip
  • अपने ग्रिड के लिए, हैंडल करने के लिए एक ईवेंट बनाएं CellContextMenuStripNeeded
  • घटना args ई उपयोगी गुण है e.ColumnIndex, e.RowIndex

मेरा मानना e.RowIndexहै कि आप जो पूछ रहे हैं।

सुझाव: जब उपयोगकर्ता आपकी घटना CellContextMenuStripNeededका कारण बनता है , तो e.RowIndexअपने ग्रिड से डेटा प्राप्त करने के लिए उपयोग करें, जैसे कि आईडी। मेनू इवेंट के टैग आइटम के रूप में आईडी स्टोर करें।

अब, जब उपयोगकर्ता वास्तव में आपके मेनू आइटम पर क्लिक करता है, तो टैग लाने के लिए प्रेषक संपत्ति का उपयोग करें। अपनी ज़रूरत के अनुसार कार्रवाई करने के लिए, अपनी आईडी युक्त टैग का उपयोग करें।


5
मैं इसे पर्याप्त नहीं बढ़ा सकता। अन्य उत्तर मेरे लिए स्पष्ट थे, लेकिन मैं बता सकता था कि संदर्भ मेनू के लिए अधिक अंतर्निहित समर्थन था (और सिर्फ डेटाग्रिड के लिए नहीं)। यह सही उत्तर है।
जोनाथन वुड

1
@ActualRandy, जब उपयोगकर्ता वास्तविक संदर्भ मेनू पर क्लिक करता है तो मुझे टैग कैसे मिलता है? CellcontexMenustripNeeded ईवेंट के तहत, मेरे पास कुछ इस तरह का संदर्भ है ।MenuStrip1.Tag = e.RowIndex;
एडविन इकेचुकवु ओकोंकोवू

2
यह उत्तर लगभग है, हालांकि मैं आपको सुझाव दूंगा कि संदर्भ मेनू को ग्रिड संपत्ति ContextMenuStrip से लिंक न करें। CellContextMenuStripNeededईवेंट हैंडलर के अंदर के बजाय if(e.RowIndex >= 0){e.ContextMenuStrip = yourContextMenuInstance;}इसका मतलब यह होगा कि मेनू केवल एक वैध पंक्ति पर राइट क्लिक करके दिखाया गया है, (यानी हेडिंग या खाली ग्रिड क्षेत्र पर नहीं)
जेम्स एस

इस बहुत ही उपयोगी उत्तर के लिए एक टिप्पणी के रूप में: CellContextMenuStripNeededकेवल तभी काम करता है जब आपका DGV किसी डेटा स्रोत से जुड़ा हो या यदि उसका VirtualMode सत्य पर सेट हो। अन्य मामलों में आपको CellMouseDownइवेंट में उस टैग को सेट करना होगा । वहां सुरक्षित स्थान पर रहने के लिए, DataGridView.HitTestInfoमाउसडाउन ईवेंट हैंडलर में यह देखने के लिए कि आप एक सेल में हैं।
LocEngineer

6

बस एक ContextMenu या ContextMenuStrip घटक को अपने फ़ॉर्म में खींचें और इसे नेत्रहीन रूप से डिज़ाइन करें, फिर इसे अपने इच्छित नियंत्रण के ContextMenu या ContextMenuStrip संपत्ति में असाइन करें।


4

उनके नक़्शे - कदम पर चलिए:

  1. एक संदर्भ मेनू बनाएं जैसे: नमूना संदर्भ मेनू

  2. इस मेनू को प्राप्त करने के लिए उपयोगकर्ता को पंक्ति पर राइट क्लिक करने की आवश्यकता है। हमें _MouseClick इवेंट और _CellMouseDown इवेंट को हैंडल करना होगा।

चयनित बायोडाटाएड वह चर है जिसमें चयनित पंक्ति जानकारी होती है।

यहाँ कोड है:

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);
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.