views:

433

answers:

2

I'm writing a custom control derived from System.Windows.Forms.Control.

The control is using the Control.KeyDown event, to watch keystrokes: I should handle some keystrokes (for example <Ctrl>-K) as hotkeys, which cause me to launch a dialog box.

If I launch the dialog from my onKeyDown event handler, the dialog is displayed before I set KeyEventArgs.SuppressKeyPress to true and return (and so I'm failing to suppress the K keypress). Instead, I'd like to return from the onKeyDown event handler, and launch the dialog afterwards. To do this, after I return from the onKeyDown event handler I need to be invoked again somehow, with some kind of 'launch the dialog' event.

On Win32, I could generate this event by using the PostMessage API, to send a registered window message to myself: I would receive this message right after any previous message in my message queue, and use it as the signal to launch my dialog. Here however I can't use PostMessage function (nor the WndProc method) because I want to use strictly managed APIs (without needing SecurityPermissionFlag::UnmanagedCode).

So what would be the managed equivalent, for a thread (my UI thread) to schedule an asynchronous callback: perhaps a timer of some kind? Some kind of self-Invoke?

A: 

You can kick off the dialog from a method defined in your form, and start the method from a new thread (include System.Threading in your using statements). The code in your button click event will look something like this:

Thread thread = new Thread(new ThreadStart(LaunchDialog));
thread.Start();

where LaunchDialog is the method that launches the new dialog window.

MusiGenesis
I want the dialog to be modal: which disables its parent window, and gets all keystrokes until it's dismissed. I'm nervous about having multiple threads drive the windows of a single UI. I usually have onne UI thread, with any worker threads not touching the UI. Do you think your suggestion is safe?
ChrisW
No, my suggestion is not a good idea. I never use threads unless I have to, and it's extremely rare that I have to. I don't really understand why you can't just launch the modal dialog from the click event, have the user interact with it, and then close the dialog.
MusiGenesis
Maybe remove this answer if we're not happy with it?
Aardvark
If I launch it from the KeyDown event (before I set KeyEventArgs.SuppressKeyPress to true and return) then, while the dialog is displayed, the control receives the subsequent KeyPress event (which wasn't suppressed).
ChrisW
@Aardvark: that's not how SO works. Sometimes, even a wrong or not-ideal answer is useful to other people for other purposes.
MusiGenesis
+2  A: 

My first thought was "set the SurpressKeyPress property before opening the dialog", but that's not really answering your question. If you really need to return from the event handler before opening the dialog, take a look at the BeginInvoke method.

you could do something like this:

...
this.BeginInvoke(new InvokeDelegate(showDlg));
KeyEventArgs.SuppressKeyPress = true;
...


public void showDlg()
{
   // create and show dialog here
}
Jim Harte
Thank you. I had wrongly assumed that a Control's calling BeginInvoke on itself would result in a synchrnous call.
ChrisW