How to make work standard hotkeys (Ctrl+C, Ctrl+Z) in DataGridView Edit Mode?

12,679

Solution 1

You can use the two events shown in the demo below to temporarily remove the shortcuts from your menu items.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;

public class Form1 : Form
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }

    public Form1()
    {
        var dgv = new DataGridView
        {
            Dock = DockStyle.Fill,
            DataSource = new List<DummyObject>
            {
                new DummyObject { Name = "One", Value = 1 },
                new DummyObject { Name = "Two", Value = 2 },
                new DummyObject { Name = "Three", Value = 3 },
            }
        };
        dgv.EditingControlShowing += (s, e) => e.Control.VisibleChanged += DgvEditingControlVisibleChanged;
        Controls.Add(dgv);
    }

    void DgvEditingControlVisibleChanged(object sender, EventArgs e)
    {
        Control control = sender as Control;
        if (control.Visible)
        {
            // The editing control has become visible.

            Trace.WriteLine(String.Format("Editing control showing {0}", control));
        }
        else
        {
            // The editing control has been removed.

            // Remove the event handler because the DGV can use multiple
            //  editing controls if it has different column types. 
            control.VisibleChanged -= DgvEditingControlVisibleChanged;
            Trace.WriteLine(String.Format("Editing control removed {0}", control));
        }
    }
}

public class DummyObject
{
    public string Name { get; set; }
    public int Value { get; set; }
}

Solution 2

If you add SendKeys.Send("^c"); to the click event of the menu item you don't need to remove the shortcut from the menuitem

Share:
12,679
sergo
Author by

sergo

Updated on June 17, 2022

Comments

  • sergo
    sergo almost 2 years

    I have simple .net application containing tabs and datargridviews on each tab. I've added main menu to the form and assigned hotkeys to menu items by using standard property:

    editMenuItem = new ToolStripMenuItem("Copy", null, new System.EventHandler(onCopyCut_Click));
    editMenuItem.ShortcutKeys = Keys.Control | Keys.C;
    

    The menu item shown above just copies cell content to clipboard. This works fine but in DGV's editing mode Ctrl+C and other standard hotkeys don't work anymore!

    I've set the Form.KeyPreview property to true, also tried to turn off the Handled property of my Form object but nothing happens:

        void FileOrginizerForm_KeyDown(object sender, KeyEventArgs e)
        {
                ...
            if (gridView.CurrentCell.IsInEditMode)
                e.Handled = false;
        }
    

    What I'm missing? I'm sure this should be something simple.

    I found some information on msdn help page's comments :

    One thing that you need to keep in mind when setting these properties is that if you have a textbox control in your form, the menu item's ShortcutKeys will intercept that key combination and the textbox will never receive it e.g. if you have a paste (ctrl + v) ShortcutKey, your textbox will never receive the paste command. According to Microsoft, this is by design. Their workaround is to temporarily clear the menu item's ShortCutKey property to permit the paste command (most likely during an event) and then reset it after the event is finished.

    SOLUTION:

    Instead of turning on and off the menu shortcuts I ended up by calling menu event handlers from the main Form's KeyDown event handler:

        void FileOrginizerForm_KeyDown(object sender, KeyEventArgs e)
        {
            if (!gridView.CurrentCell.IsInEditMode)
            {
                if (e.KeyData == (Keys.Control | Keys.Z))
                {
                    this.editToolStripMenuItem.DropDownItems["Undo"].PerformClick();
                }
                else if (e.KeyData == (Keys.Control | Keys.Y))
                {
                    this.editToolStripMenuItem.DropDownItems["Redo"].PerformClick();
                }
                else if (e.KeyData == (Keys.Control | Keys.X))
                {
                        this.editToolStripMenuItem.DropDownItems["Cut"].PerformClick();
                }
                else if (e.KeyData == (Keys.Control | Keys.C))
                {
                        this.editToolStripMenuItem.DropDownItems["Copy"].PerformClick();
                }
                else if (e.KeyData == (Keys.Control | Keys.V))
                {
                        this.editToolStripMenuItem.DropDownItems["Paste"].PerformClick();
                }
                else if (e.KeyData == (Keys.Control | Keys.A))
                {
    
    this.selectToolStripMenuItem.DropDownItems["Select All"].PerformClick();
                }
            }
        }
    
  • sergo
    sergo almost 13 years
    Thank you for this magic - dgv.EditingControlShowing += (s, e) => e.Control.VisibleChanged += DgvEditingControlVisibleChanged; But there's still lot of hassle. KeyDown event seems easier to me.
  • Tergiver
    Tergiver almost 13 years
    The easy way is how it's done in a "straight-api application" (i.e. sans framework). Normally a cut/copy/paste menu item will simply send WM_COMMAND (ID_CUT, ID_COPY, ID_PASTE) to the control with focus. It appears that the Windows Forms plumbing has made this more difficult than it should be. You might simply do it the way a straight-api app would and p/invoke SendMessage(this.ActiveControl.Handle, WM_COMMAND, ID_COPY, 0). I'm not familier enough with the WinForms plumbing to know if there is a better way.