I override ProcessCmdKey in my application and can get any single keypress with modifiers (eg. Alt+Ctrl+X). What I want to do is mimic the short cut handling of say ReSharper where the user holds down the control key and then R, M to open the refactor dialog
I have found plenty of references to capture key plus modifier combinations but not much for the sequence. There is this Capture multiple key downs in C# but it uses the KeyDown Event.
There are also key mining examples such as this http://www.codeproject.com/KB/system/simple_key_log.aspx that capture everything and use native calls.
Am I able to extend my ProcessCmdKey to handle the key sequences or do I need to look elsewhere? Since I have a large number of shortcuts captured in ProcessCmdKey I would rather not have to start again if possible
Thanks
In order to achieve the functionality you want you simply need to keep track of the sequence of KeyPress events.
You can create a class to keep track of the last key combination that was pressed in ProcessCmdKey. If that particular combination does not match a mapped command but it is the first element of a sequence you can store it in your class. Then the next time ProcessCmdKey is activated check your new KeyPressTracker class to determine if a sequence has been started. If it has then check if the newly pressed key combination is the second element of one you specify. Please see the pseudocode example below:
Step 1: ProcessCmdKey is activated. The key combination is Ctrl+R, this does not match a command that you want to process but it is the first element of a sequence that you want to use (Ctrl+R+M).
Step 2: Store this key-press in a new class you created to keep track of the last key-press.
KeyPressTracker.Store(KeyCode, Modifiers);
Step 3: ProcessCmdKey is activated a second time. This time, the key combination is Ctrl+M which is not a key-press we're looking for but is the second element of a sequence. We check the last stored keypress using the new KeyPressTracker class. This will allow you to match a "sequence" such as Ctrl+R and Ctrl+M.
var lastKeyPress = KeyPressTracker.GetLastKeyPress();
if (lastKeyPress == "Ctrl+R" && currentKeyPress == "Ctrl+M")
{
// Show Refactor dialog
}
Related
In short, trying to do what the following two lines of C# code do without using System.Windows.Forms, because it is .NET Core and not a WinForms project.
var data = new System.Windows.Forms.DataObject(
System.Windows.Forms.DataFormats.FileDrop, new string[] { #"C:\test.txt"});
dummyControl.DoDragDrop(data, System.Windows.Forms.DragDropEffects.Copy);
But it does not work. What have I done wrong? My procedure was,
When mouse is down on a UI control, call SetCapture to capture the event.
The IDropSource's QueryContinueDrag keep returning S_OK until the mouse button is up, and then DRAGDROP_S_DROP.
My "DataObject" implementing IDataObject has EnumFormatEtc. In which I return only one FORMATETC.
new FORMATETC()
{
cfFormat = CF_HDROP,
ptd = IntPtr.Zero,
dwAspect = DVASPECT.DVASPECT_ICON,
lindex = -1,
tymed = TYMED.TYMED_FILE
}
In QueryGetData, if the format's tymed is TYMED_FILE, return S_OK meaning I am dragging a file. Otherwise return DV_E_TYMED meaning I don't have that type of data.
In GetData, if the format's tymed is TYMED_FILE, I set a file like this
medium = new STGMEDIUM();
medium.tymed = TYMED.TYMED_FILE;
medium.unionmember = Marshal.StringToHGlobalUni(#"C:\test.txt");
medium.pUnkForRelease = IntPtr.Zero;
I tried to drag-and-drop to Notepad. Problems are,
When DoDragDrop is not called on the mouse down event, mouse is captured and mouse up event is received. But when DoDragDrop is called, QueryContinueDrag is endlessly called and I don't get the mouse up event, even after I released the mouse. I tried to call DoDragDrop in another thread, but that did not work.
I get callbacks like QueryGetData(TYMED_HGLOBAL) or QueryGetData(TYMED_HGLOBAL, TYMED_ISTREAM, TYMED_GDI, TYMED_MFPICT, TYMED_ENHMF), but not for TYMED_FILE. Why isn't Notepad requesting that?
The drag and drop seems unnecessarily complex, but for now, I am only interested in dragging a file, so I did not want to implement parts other than that. What in the above procedure or assumptions are wrong?
When DoDragDrop is not called on the mouse down event, mouse is captured and mouse up event is received. But when DoDragDrop is called, QueryContinueDrag is endlessly called and I don't get the mouse up event, even after I released the mouse.
You don't get a mouse up event because DoDragDrop() blocks your UI message loop until the drag operation is completed. So you need to use the input flags given to your QueryContinueDrag() implementation to decide whether to continue dragging, perform the drop, or abort the operation.
If you start the drag on a left mouse down, return S_OK if the grfKeyState parameter includes the MK_LBUTTON flag, and return DRAGDROP_S_DROP if the MK_LBUTTON flag is cleared. Return DRAGDROP_S_CANCEL if the fEscapePressed parameter is true. This is documented on MSDN:
IDropSource::QueryContinueDrag method
Parameters
fEscapePressed
Indicates whether the Esc key has been pressed since the previous call to QueryContinueDrag or to DoDragDrop if this is the first call to QueryContinueDrag. A TRUE value indicates the end user has pressed the escape key; a FALSE value indicates it has not been pressed.
grfKeyState
The current state of the keyboard modifier keys on the keyboard. Possible values can be a combination of any of the flags MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON.
Return Value
This method can return the following values.
S_OK
The drag operation should continue. This result occurs if no errors are detected, the mouse button starting the drag-and-drop operation has not been released, and the Esc key has not been detected.
DRAGDROP_S_DROP
The drop operation should occur completing the drag operation. This result occurs if grfKeyState indicates that the key that started the drag-and-drop operation has been released.
DRAGDROP_S_CANCEL
The drag operation should be canceled with no drop operation occurring. This result occurs if fEscapePressed is TRUE, indicating the Esc key has been pressed.
Remarks
The DoDragDrop function calls QueryContinueDrag whenever it detects a change in the keyboard or mouse button state during a drag-and-drop operation. QueryContinueDrag must determine whether the drag-and-drop operation should be continued, canceled, or completed based on the contents of the parameters grfKeyState and fEscapePressed.
I get callbacks like QueryGetData(TYMED_HGLOBAL) or QueryGetData(TYMED_HGLOBAL, TYMED_ISTREAM, TYMED_GDI, TYMED_MFPICT, TYMED_ENHMF), but not for TYMED_FILE. Why isn't Notepad requesting that?
You can't use TYMED_FILE for CF_HDROP, you must use TYMED_HGLOBAL. And the content of the allocated HGLOBAL must be a DROPFILES struct followed by a double-null-terminated list of file paths. This is documented on MSDN:
Shell Clipboard Formats
CF_HDROP
This clipboard format is used when transferring the locations of a group of existing files. Unlike the other Shell formats, it is predefined, so there is no need to call RegisterClipboardFormat. The data consists of an STGMEDIUM structure that contains a global memory object. The structure's hGlobal member points to a DROPFILES structure as its hGlobal member.
The pFiles member of the DROPFILES structure contains an offset to a double null-terminated character array that contains the file names. If you are extracting a CF_HDROP format from a data object, you can use DragQueryFile to extract individual file names from the global memory object. If you are creating a CF_HDROP format to place in a data object, you will need to construct the file name array.
The file name array consists of a series of strings, each containing one file's fully qualified path, including the terminating NULL character. An additional null character is appended to the final string to terminate the array. For example, if the files c:\temp1.txt and c:\temp2.txt are being transferred, the character array looks like this:
c:\temp1.txt'\0'c:\temp2.txt'\0''\0'
Note
In this example, '\0' is used to represent the null character, not the literal characters that should be included.
If the object was copied to the clipboard as part of a drag-and-drop operation, the pt member of the DROPFILES structure contains the coordinates of the point where the object was dropped. You can use DragQueryPoint to extract the cursor coordinates.
If this format is present in a data object, an OLE drag loop simulates WM_DROPFILES functionality with non-OLE drop targets. This is important if your application is the source of a drag-and-drop operation on a Windows 3.1 system.
I am creating a Calculator in Windows Store Application. I have successfully created the app in the store.
Now there is a problem in my app, after getting the result from performing any operation whenever I press on any numeric value, that value got append in the existing value.
In the following snapshot: I have added two numbers (1,1):
Now I am entering another value to perform some other option, but the new value got append in the existing value. I am entering 1 here:
What is the code for removing the existing value, if any numeric values pressed?
you could declare a bool value which is false and when you have your calculation done you switch it to true. Then you write a method that checks if the calculation is done or not and if it's done you simply clear the (i guess you use a textblock / box?) output. That would be my way in this situation - maybe there is a better solution for you. I hope it helps you to get a clearer way in mind.
As the author of the Windows Calculator that shipped from Windows 3.0 through Windows Vista, I agree with user3645029's response. You need to work out the input model for the app, so you understand clearly when you begin entering a new number and when you append to the one showing. I suspect that your app logic isn't making this distinction.
Let me be more specific:
If the key pressed is a number and the last key pressed was a number, then you add that new digit, which effectively means multiplying the current value by 10 and then adding the new key.
If the key pressed is a number and the last key pressed was an operator, =, or similar keys, then you're starting a new number input and your current value should be reset to 0 first.
In short, writing a calculator app requires an internal state machines that understands how to proceed from one input to the next. From what you describe, it sounds like you're missing the logic for the = key. Generally speaking, hand-held calculators with an = sign effectively clear the current value if you start entering a new number after =. Only if you press an operator does that value persist, and in that case you're also starting a new current value and keeping the "2" in your case as the first operand.
I am using a barcode scanner and we were looking for a way to determine if input was from a keyboard or the scanner. The input will be used in a WPF POS application. We thought we had come up with a good method by using the scanners ability to prefix reads with custom characters. The first character ideally would be non printable so we chose NULL or '\0'. We used the following code to register an event handler
EventManager.RegisterClassHandler(typeof(System.Windows.Controls.Control), System.Windows.Controls.Control.KeyUpEvent, new RoutedEventHandler(KeyUpEvent));
internal static void KeyUpEvent (object sender, RoutedEventArgs e)
{
KeyEventArgs keyEvent = e as KeyEventArgs;
if (keyEvent != null)
{
keyEvent.Key.ToString();
}
}
This however seems to not get the first NULL character and instead moves to the next char which is the length of the data.
I have also tested with a console app and just done a Console.ReadKey(). This returns me the null char as the first thing read so I know the scanner is definitely sending the correct data.
So is there any way in WPF to obtain all the data read?
Edit:
I tried using TextCompositionEventHandler but to no avail, i still only get the printable characters coming through.
KeyEventArgs.Key is not a character, it's an enum indicating which key was pressed. There is no NULL keyboard key so there is no point trying to check for the ASCII NULL (0x00) character. Moreover, most non-printable characters have no equivalent key and require the user to use a combination of keys to type them.
If you want to detect scanner codes using prefixes, try chekcing the UIElement.TextInput TextBoxBase.TextChanged events.
A better idea may be to use the scanner's SDK (if available) to send data directly to your application. More expensive models usually have an SDK to communicate with applications directly instead of emulating the keyboard.
I might be wrong, but I don't think NULL and \0 is the same in this context. Have a look at this post. They suggest using TextInput instead of KeyUp.
How to get KeyChar in WPF application
I am having trouble with Keyboard.GetState() and .IsKeysDown(). They simply won't work.
If you have Synergy, then it might be one of the problems.
I believe it clears the keyboard input cache or something along the lines, so when you poll the hardware again Windows doesn't have any keystrokes to identify.
So when debugging in XNA, disable Synergy.
It sounds like you may be experiencing a couple of issues. Since other new developers are likely to find this question when looking for help with Keyboard input I will try to cover both.
1) Keyboard.GetState() returns a snapshot of which keys are currently being held down. KeyboardState.IsKeyDown() does not identify keystrokes. It just tells you which keys are currently being pressed. Due to the way this works, it is best if you store the keyboard state before attempting to use it.
2) In your Update loop you will want to store the current keyboard state:
KeyboardState newKeyboardState = Keyboard.GetState();
Now you can check if a key press has occurred. A key press means that a key used to be down, and is now up:
if(previousKeyboardState.IsKeyDown(Keys.A && !newKeyboardState.IsKeyDown(Keys.A)) {
Console.Out.WriteLine("Keystroke: A");
}
Finally, save the current state so you can check against it later:
previousKeyboardState = newKeyboardState;
I have been searching around for an answer to this but I can't seem to find anything. Does anyone know if you can determine the letter casing in Keys?
For example:
if (System.Windows.Forms.Keys.A.ToString() == "A")
{
// Upper or Lower?
}
Thanks.
There is no casing, it represents a physical key on your keyboard. Do you see an 'a' and an 'A' on your keyboard?
You can check and see if a Shift key is depressed.
System.Windows.Forms.Keys.A represents the physical key A on your keyboard. It does not have a case. Thus, your question does not make sense.
If you want to check whether the user holds the Shift key on the keybord, there's also System.Windows.Forms.Keys.Shift.
There is no simple mapping between keys and characters. Keyboard layouts can work differently. One example are dead keys. And once you get to IMEs it gets even more complicated. Do not try to duplicate a keyboard layout manually in your application.
If you want to get what character a user entered, handle WM_CHAR, not WM_KEY_DOWN/UP. It's exposed as Control.KeyPress event in winforms.