keyboard - WPF enqueue and replay key events -
i'm trying improve responsiveness of wpf business application when users "between" screens waiting new screen appear after server response, can still entering data. i'm able queue events (using previewkeydown event handler on background panel) i'm having difficulties throwing events dequeue @ new panel once it's loaded. in particular textboxes on new panel not picking text. i've tried raising same events (setting handled true when capturing them, setting handled false when raising them again) creating new keydown events, new previewkeydown events, doing processinput, doing raiseevent on panel, setting focus on right textbox , doing raiseevent on textbox, many things.
it seems should really simple, can't figure out.
here of things i've tried. consider queue of keyeventargs called eventq:
here's 1 thing doesn't work:
while (eventq.count > 0) { keyeventargs kea = eventq.dequeue(); tbone.focus(); // tbone text box kea.handled = false; this.raiseevent(kea); }
here's another:
while (eventq.count > 0) { keyeventargs kea = eventq.dequeue(); tbone.focus(); // tbone text box var key = kea.key; // key send var routedevent = keyboard.previewkeydownevent; // event send keyeventargs keanew = new keyeventargs( keyboard.primarydevice, presentationsource.fromvisual(this), 0, key) { routedevent = routedevent, handled = false }; inputmanager.current.processinput(keanew); }
and another:
while (eventq.count > 0) { keyeventargs kea = eventq.dequeue(); tbone.focus(); // tbone text box var key = kea.key; // key send var routedevent = keyboard.previewkeydownevent; // event send this.raiseevent( new keyeventargs( keyboard.primarydevice, presentationsource.fromvisual(this), 0, key) { routedevent = routedevent, handled = false } ); }
one strange thing i've noticed when using inputmanager method (#2) spaces appear. normal text keys not.
the same resources turned me when did research, think in answer pretty valid.
i looked on , have found way of doing it, using win32 api. had introduce threading , small delays, because reason key events not replayed in correct sequence without that. overall think solution easier though, , figured out how include modifier keys (by using get/setkeyboardstate function). uppercase working, , should keyboard shortcuts.
starting demo app, pressing keys 1 space 2 space 3 tab 4 space 5 space 6
, clicking button produces following:
xaml:
<usercontrol x:class="wpfapplication1.keyeventqueuedemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:ignorable="d" d:designheight="300" d:designwidth="300" > <stackpanel> <textbox x:name="tbone" margin="5,2" /> <textbox x:name="tbtwo" margin="5,2" /> <button x:name="btn" content="replay key events" margin="5,2" /> </stackpanel> </usercontrol>
code behind:
using system; using system.collections.generic; using system.runtime.interopservices; using system.threading; using system.threading.tasks; using system.windows; using system.windows.controls; using system.windows.input; using system.windows.interop; namespace wpfapplication1 { /// <summary> /// structure defines key input modifier keys /// </summary> public struct keyandstate { public int key; public byte[] keyboardstate; public keyandstate(int key, byte[] state) { key = key; keyboardstate = state; } } /// <summary> /// demo illustrate storing keyboard input , playing @ later stage /// </summary> public partial class keyeventqueuedemo : usercontrol { private const int wm_keydown = 0x0100; [dllimport("user32.dll")] static extern bool postmessage(intptr hwnd, uint32 msg, int wparam, int lparam); [dllimport("user32.dll")] static extern bool getkeyboardstate(byte[] lpkeystate); [dllimport("user32.dll")] static extern bool setkeyboardstate(byte[] lpkeystate); private intptr _handle; private bool _ismonitoring = true; private queue<keyandstate> _eventq = new queue<keyandstate>(); public keyeventqueuedemo() { initializecomponent(); this.focusable = true; this.loaded += keyeventqueuedemo_loaded; this.previewkeydown += keyeventqueuedemo_previewkeydown; this.btn.click += (s, e) => replaykeyevents(); } void keyeventqueuedemo_loaded(object sender, routedeventargs e) { this.focus(); // necessary detect previewkeydown event setfocusable(false); // demo purpose only, controls not focus @ tab key // getting window handle hwndsource source = (hwndsource)hwndsource.fromvisual(this); _handle = source.handle; } /// <summary> /// key , keyboard state (modifier keys), store them in queue /// </summary> void keyeventqueuedemo_previewkeydown(object sender, keyeventargs e) { if (_ismonitoring) { int key = keyinterop.virtualkeyfromkey(e.key); byte[] state = new byte[256]; getkeyboardstate(state); _eventq.enqueue(new keyandstate(key, state)); } } /// <summary> /// replay key events queue /// </summary> private void replaykeyevents() { _ismonitoring = false; // no longer add queue setfocusable(true); // allow controls take focus (demo purpose only) movefocus(new traversalrequest(focusnavigationdirection.next)); // set focus first control // thread dequeueing, because sequence of inputs not preserved // unless small delay between them introduced. effect // produces should acceptable ui. task.run(() => { while (_eventq.count > 0) { keyandstate keyandstate = _eventq.dequeue(); application.current.dispatcher.begininvoke((action)(() => { setkeyboardstate(keyandstate.keyboardstate); // set stored keyboard state postmessage(_handle, wm_keydown, keyandstate.key, 0); })); system.threading.thread.sleep(5); // might need adjustment } }); } /// <summary> /// prevent controls getting focus , taking input until requested /// </summary> private void setfocusable(bool isfocusable) { tbone.focusable = isfocusable; tbtwo.focusable = isfocusable; btn.focusable = isfocusable; } } }
Comments
Post a Comment