Capture keyboard inputs without a textbox - c#

I have a application that reads data from health cards and parse them for basic info like D.O.B., Health Card #, and names. Right now, I have a textbox that takes input from the card swiper and it works great, but I feel there could be a better approach for this.
I want to have a keyboard listener in the background of the application that captures input from the card swiper and parse the data without the need of a textbox. I figure I'll need a loop function in the Form1_Load that actively listens for keyboard inputs, prepare a buffer for the input, and then when a carriage return is detected, go ahead and parse the buffered data. When the parsing is done, clear the buffer.
My problem is I'm relatively new to C# and I don't know what I should use for listening to keyboard inputs without a textbox. Could someone point me in the right direction?
Here's my code in case if anyone's interested: http://pastebin.com/q6AkghvN
Just a note, I followed the credit card swipe guide from
http://www.markhagan.me/Samples/CreditCardSwipeMagneticStripProcessing and modified it slightly for my usecase.
--- EDITED ---
Thanks Paul and everyone else for their help!
Here is my solution if anyone is interested:
private void frmMain_KeyPress(object sender, KeyPressEventArgs e)
{
lblStatus.Text = "Reading Card...";
lblStatus.ForeColor = Color.Blue;
if (e.KeyChar != (char)Keys.Enter)
{
buffer += e.KeyChar;
}
else
{
lblStatus.Text = "Parsing Card...";
if (buffer.Contains('^') && buffer.Contains(';') && buffer.Contains('='))
{
try
{
string[] cardData = buffer.Split(';');
string[] caretData = cardData[0].Split('^');
string[] nameData = caretData[1].Split('/');
string[] equalData = cardData[1].Split('=');
tBoxHealthCardNumber.Text = equalData[0];
tBoxDateOfBirth.Text = FormatBirthday(equalData[1]);
tBoxFirstName.Text = TrimName(nameData[1]);
tBoxLastName.Text = TrimName(nameData[0]);
tBoxDateTimeScanned.Text = DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm");
e.Handled = true;
}
catch (Exception)
{
throw;
}
}
else
{
lblStatus.Text = "Error Reading Card";
}
buffer = "";
lblStatus.Text = "Ready";
lblStatus.ForeColor = Color.Green;
}
}

If you add a key handler to the form you will not see the key presses when focus is on a control, e.g. a textbox. For the form to see the key presses even when there is a focused control, you must also enable the KeyPreview property.
You can then add a handler for KeyDown, KeyPress and/or KeyUp on the form as you desire to receive these events.
As you can read in the documentation to KeyPreview, if you set the Handled property to true, you can prevent the event from being subsequently sent to the focused control, i.e. you can hide certain key events from being seen by the focused control.

Related

SendKeys.Send() does not send or react as I expect

I have a problem using Sendkeys.Send in my C# application and I really cannot understand why. When using it then it does not send what I expect to the active application. I am using it together with the global hotkey manager, https://github.com/thomaslevesque/NHotkey
I have created this simple PoC that, for my part at least, will be able to reproduce my problem. Just launch Wordpad and press the hotkey, ALT + O:
using System.Windows.Forms;
using System.Diagnostics;
using NHotkey.WindowsForms;
namespace WindowsFormsApp5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Convert string to keys
string hotkey = "Alt + O";
KeysConverter cvt;
Keys key;
cvt = new KeysConverter();
key = (Keys)cvt.ConvertFrom(hotkey);
// Setup the hotkey
HotkeyManager.Current.AddOrReplace("MyID", key, HotkeyAction);
// Copy some text to the clipboard that I want to paste to the active application
Clipboard.SetText("My String");
}
private void HotkeyAction(object sender, NHotkey.HotkeyEventArgs e)
{
Debug.WriteLine("Pressed the hotkey");
SendKeys.Send("^v");
// SendKeys.Send("Test string");
e.Handled = true;
}
}
}
When I do this in Wordpad, then instead of pasting the clipboard (^v equals CTRL + V) then it tries to "Paste Special":
Even if I do the most simple thing and then just put some text in SendKeys.Send, then it seems to be messing with the menus in Wordpad? SendKeys.SendWait is not any different.
I have been trying to figure this out for quite some time now but I simply do not understand why it does that. Basically, I need to paste the clipboard on a hotkey though it doesn't need to be with this exact method so if anyone knows another way of doing it then I would appreciate some hints.
MY IMPLEMENTED SOLUTION
Based on the accepted answer then I did change my implementation slightly as I could not get it working with just a timer. I may have missed something(?) but this is working.
In basic then I change focus to my application as soon as the hotkey is detected, to avoid conflict with modifier keys (ALT etc) in the active application. I then create an invisible form and when I detect a KeyUp event, then I check for modifier keys and if none is pressed down then I enable a timer and immediately switch focus back to the originating application. After 50ms the clipboard will be pasted to the active application.
Something like this:
// Somewhere else in code but nice to know
// IntPtr activeApp = GetForegroundWindow(); // get HWnd for active application
// SetForegroundWindow(this.Handle); // switch to my application
private System.Timers.Timer timerPasteOnHotkey = new System.Timers.Timer();
// Main
public PasteOnHotkey()
{
InitializeComponent();
// Define the timer
timerPasteOnHotkey.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timerPasteOnHotkey.Interval = 50;
timerPasteOnHotkey.Enabled = false;
// Make the form invisble
this.Size = new System.Drawing.Size(0, 0);
this.Opacity = 0.0;
}
private void PasteOnHotkey_KeyUp(object sender, KeyEventArgs e)
{
// Check if modifier keys are pressed
bool isShift = e.Shift;
bool isAlt = e.Alt;
bool isControl = e.Control;
// Proceed if no modifier keys are pressed down
if (!isShift && !isAlt && !isControl)
{
Hide();
// Start the timer and change focus to the active application
timerPasteOnHotkey.Enabled = true;
SetForegroundWindow(activeApp);
}
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
timerPasteOnHotkey.Enabled = false;
SendKeys.SendWait("^v"); // send "CTRL + v" (paste from clipboard)
}
When you use SendKeys.Send in a response to a key press then the keys you send may be combined with the physical keys you’re holding at that moment. In this case you’re holding Alt, so Wordpad assumes you pressed Alt-Ctrl-V instead of just Ctrl-V. Also Alt opens menu, so sending other keys may relate to hotkeys there.
Adding a delay will remove this issue, and usually when sending key presses it would be done as not relating to other key presses so it won’t be a problem.

Execute specific blocks of code based on which key on the keyboard is pressed

I am very new to the programming world and recently dove into c#. I don't want to waste your time so I'll get right to it. I wanted to create a program just to test my knowledge, and thought I could attempt to execute specific blocks of code based on which key on the keyboard is pressed by the user. I tried doing this by creating an event handler that contained if statements, but then realized I didn't know how to have the event handler active in the program.
For example, and as you can see in the below snippet, after the WriteLine in Line 5 lets say I wanted to raise the EventKeyPress event so that it waits for user input and reads the key they have pressed and reacts accordingly, how would I do that?
Again, I'm almost a complete beginner and have searched around for explanations about event handlers for hours and still can't wrap my head around what I am supposed to do or if I am even using the event handler correctly. Thanks in advance!
static void Main();
{
if (search == "Ball")
{
Console.WriteLine("Press enter to exit or backspace to return to the search bar")
// RIGHT HERE
}
else
{
Console.WriteLine("Sorry, I don't recognize {0}", search);
}
void EventKeyPress(object sender, KeyPressEventArgs e)
{
for (int i = 0; i < 1;)
{
if (e.KeyChar == (char)Keys.Enter)
{
// exit app
}
else if (e.KeyChar == (char)Keys.Back)
{
// go back to search
}
else
{
i = 0; // error
}
}
}
}
So, you're asking for something that involves Threading which is not a beginner thing to accomplish at all. The best way to do this for a beginner is to ask for a prompt, then accept as an input. For example.
Console.WriteLine("Hello, what's your name?");
string nameStr = Console.ReadLine();
Console.WriteLine($"Hello, {nameStr}");
You can then use your variable and apply it to an if/while or whatever kind of conditional.
if (nameStr == "Matt"){
//Do This Code.
}
Once you have that code, add a sequential method that will ask the user to return to the main menu or whatever you want it to do.
Main.ReturnMenu(); //Or whatever you want to use.

Windows Forms Textbox is uneditable by accident

I have an application which asks the user a few simple questions. The user is supposed to input the answer by typing it into a TextBox. When I render the Windows Form though, the TextBox is greyed out, blends in with the background, and is uneditable.
Here's my code:
public string waitForText(Point Locution)
{
TextBox WriteAnswerHere = new TextBox();
WriteAnswerHere.Location = Locution;
WriteAnswerHere.ReadOnly = false;
WriteAnswerHere.Focus();
this.Controls.Add(WriteAnswerHere);
int waiting = 1;
while (waiting == 1)
{
if (Control.ModifierKeys == Keys.Enter)
{
waiting = 0;
}
}
string HowYouAre = WriteAnswerHere.Text;
this.Controls.Remove(WriteAnswerHere);
return HowYouAre;
}
The input is supposed to be given to the application when the Enter key is pressed, hence the (Control.ModifierKeys == Keys.Enter); Any suggestions on what I am doing wrong?
You shouldn't use a while loop to detect specific key events. Your while loop is holding up the form. I suggest you check out these articles on Events and Event handlers for Windows forms.
http://msdn.microsoft.com/en-us/library/dacysss4.aspx

I'm trying to catch the input of the swiped card but I'm stuck using C# Windows Forms

I am using a test card and this is the output after I swiped the card and it is ok
But when I'm trying to get the data of the swiped through prompting it to messagebox this will be the output
How can I fix this? I am expecting the output same as the first image, and it will also be the message of the messagebox
Here is my code:
private void CreditCardProcessor_Load(object sender, EventArgs e)
{
KeyPreview = true;
KeyPress += CreditCardProcessor_KeyPress;
}
private bool inputToLabel = true;
private void CreditCardProcessor_KeyPress(object sender, KeyPressEventArgs e)
{
if (inputToLabel)
{
label13.Text = label13.Text + e.KeyChar;
e.Handled = true;
}
else
{
e.Handled = false;
}
MessageBox.Show(label13.Text);
}
In short I want to run a function after swiping the card, and use its data to be use in my function. :)
You'll need to be more specific with your question. From the looks of things your card scanner is operating through the keyboard buffer. (Many card scanners operate this way) This means that every character of the strip is received as a character which is why you can capture this OnKeyPress.
If you're wondering why you're only seeing one character at a time it is exactly because you're raising a message box with each character received. If you want to know when you can call a function with the whole card info using that code what you'll need is something like:
private bool inputToLabel = true;
private StringBuilder cardData = new StringBuilder();
private void CreditCardProcessor_KeyPress(object sender, KeyPressEventArgs e)
{
if (!inputToLabel)
return;
if (e.KeyChar == '\r')
{
MessageBox.Show(cardData.ToString()); // Call your method here.
}
else
{
cardData.Append(e.KeyChar);
//label13.Text = label13.Text + e.KeyChar;
}
e.Handled = true;
}
Caveat: This is assuming that the card reader library is configured to terminate a card read with carriage return. (\r) You'll need to read up, or experiment with it for settings as to whether it can/does send a terminating character to know when the card read is complete. Failing that you can watch the output string for patterns. (I.e. when the captured string ends with "??") Though this is less optimal.

How to prevent tab item selection change event?

I am working on a wpf .My requirement is to change selection of tab according to user confirmation it means every time when user changes tab a message box opens and confirm with user whether he wants to change the tab or not.
But problem with me is when I press no first time it works fine .but after that on second time it asks two times for user confirmation
can anyone help me to solve this ?
private void tabcontrol_SelectionChanged(object sender,SelectionChangedEventArgs e)
{
try
{
if (handleSelection && e.OriginalSource == tbUserProfileMainControl)
{
//Ask user for change
if (isUserAllowedToChanged)
{
int currentIndex = (tabcontrol.SelectedIndex);
GeneralDeclaration.currentSelectedTabIndex = currentIndex;
LoadUserControl(GeneralDeclaration.currentSelectedTabIndex);
}
else
{
//e.Handled = true;
handleSelection = false;
tbUserProfileMainControl.SelectedIndex = Math.Abs(tbUserProfileMainControl.SelectedIndex - 1);
}
}
handleSelection = true;
}
catch (Exception ex)
{
//
}
}
It sounds like you're adding handlers during the click event itself. This causes your subsequent click to perform the action one more time (3rd click 3 times, 4th click 4 times, etc).
Check how you bind the event to the handler and check where you are defining the handler itself. You're doing something twice that should only be done once.
This is my estimation based on your findings, without code, I'm just taking a wild stab in the dark.

Categories