views:

182

answers:

3

Hi,

I have complex UserControl (grid, edit controls for grid, etc...) and I want handle CTRL+C keyboard shortcut, however I don't want disable a native functions by edit controls (textboxes, comboboxes, etc...). If the CTRL+C is not handled by other inner controls I want handle it by myself (copy whole row(s) from grid, etc...).

I tried override WndProc method in UserControl and check for WM_COPY and WM_COPYDATA, but it doesn't work. It works only on final target control (TextBox for example).

A: 

I didn't try it out and i think it heavy depends on all the child controls, which are within your UserControl. But normally a keystroke is given to the actual control that has the focus. If it doesn't handle that keystroke (setting e.Handled = true), it would be bubble up to its parent and if that doesn't handle it, it would go further till it reaches the form and finally the limbus.

So if your child controls are properly written and they can't handle the given keystroke (e.g. Control + C) it should be easy to add a handler into your UserControl to the KeyDown event and do whatever you like.

Update

After reading your comments, i still think that the way shown by Enrico and me should be the correct one. So i think the problem is that if one of your 3rd party controls has the focus it is not able to handle the copy shortcut, but it sets the e.Handled = true leading to no further informations of the parent controls about the shortcut.

So at first you should contact your control vendor and send him a bug report about this wrong behaviour.

Alternative there exists another hacky way:
In your form you can set the KeyPreview to true and intercept the incoming key. Now you could check if within the ActiveControl is something that handles the shortcut correctly (maybe a check against a Dictionary<Type, bool> or a HashSet<Type> lackingControls) and just leave the function or do whatever you want and setting the e.Handled = true by yourself.

Update 2

A little snippet to illustrate what i meant:

this.KeyPreview = true;

HashSet<Type> scrappyControls = new HashSet<Type>();
//ToDo: Add all controls that say it handles Ctrl-C
//      but doesn't it the right way.
scrappyControls.Add(typeof(TextBox));

this.KeyDown += (sender, e) =>
{
    if (e.KeyData == (Keys.Control | Keys.C))
    {
        if (scrappyControls.Contains(this.ActiveControl.GetType()))
        {
            //ToDo: Do copy to clipboard on yourself
            e.Handled = true;
        }
    }
};

The drawback of this functionality is, that it must be placed into your form, not into your self-written UserControl. But that way you will be informed, when a TextBox has the focus and Control + C is pressed within.

Oliver
I tried it, but it doesn't works. I found the Copy action is triggered by WM_COPY and/or WM_COPYDATA messages, it's littlebit different way than a keyboard events.
TcKs
No, the copying (which is usually caused by CTRL+C keys press) are internaly in windows implemented by processing WM_COPY and WM_COPYDATA. So the checking for "Handled = true" on "CTRL+C" keys is wrong. You can try it on simple TextBox, it doesn't works.
TcKs
A: 

What you are looking for is a way to bubble events raised by a child control up to its container so that you can handle those events at the User Control's level.

The automatic propagation of events across the control hierarchy is built into WPF and is called Routed Events. However this functionality is not available out of the box in Windows Forms, so you will have to implement your own solution.

Have a look at this SO question to get some inspiration.

Related resources:

Enrico Campidoglio
In fact, there is no Clipboard related events on System.Windows.Forms.Control or some other class or interface. The clipboard copy is implemented by overriding WndProc method on final control. However if you override WndProc method in parent control of final control (TextBox for example), the WndProc is not called for WM_COPY/WM_COPYDATA message (or is masked?). We are using 3rd party components and have not 100% control of creation of every control, so we can not override WndProc of every control which are used in UserControl.
TcKs
I see. But are those third-party control classes sealed?
Enrico Campidoglio
Unfortunately, the 3rd party controls are dynamicaly created and the creation is inside 3rd party grid which is obfuscated and there is no way, how to replace original control class by other (derived) classes.
TcKs
In that case I'm not sure there is a solution for your problem in Windows Forms. If the feature you are trying to implement is critical to your users, you could consider moving the application to WPF where this kind of functionality is possible through Routed Commands. You could still use those third-party controls from WPF through WinForms Interop.However, depending on the size of your application and your project constraints, that may not be a sustainable solution.
Enrico Campidoglio
+2  A: 

You can do this by overriding ProcessCmdKey(). Check if a text box has the focus. For example:

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
        if (keyData == (Keys.Control | Keys.C)) {
            var box = this.ActiveControl as TextBoxBase;
            if (box == null) {
                // Do your stuff
                MessageBox.Show("Copy!");
                return true;
            }
        }
        return base.ProcessCmdKey(ref msg, keyData);
    }
Hans Passant
+1, This looks like solution. I tried it in simple sample and it works.If I don't found a misfunctionality, I will accept this answer. Thanks!
TcKs