I've some code written in C# that allows the user to scan a barcode, and the label.Text changes to the input of the barcode. I used a label instead of textbox to prevent the user from fat-fingering a wrong barcode -basically disabling any keyboard input.
Everything works nice, except when the barcode being scanned has alphabetic characters in it. If the barcode starts with an alphabetic character and ends with an alphabetic character, then the barcode doesn't scan at all.
If the barcode starts with an alphabetic character, but ends with numeric characters, the program picks up only the trailing numeric characters.
Here's the code:
char cforKeyDown = '\0';
int _lastKeystroke = DateTime.Now.Millisecond;
List<char> _barcode = new List<char>();
bool UseKeyboard = false;
private void Form1_Load(object sender, EventArgs e)
{
this.ActiveControl = label1;
this.KeyDown += new KeyEventHandler(Form1_KeyDown);
this.KeyUp += new KeyEventHandler(Form1_KeyUp);
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
// if keyboard input is allowed to read
if (UseKeyboard && e.KeyData != Keys.Enter)
{
MessageBox.Show(e.KeyData.ToString());
}
/* check if keydown and keyup is not different
* and keydown event is not fired again before the keyup event fired for the same key
* and keydown is not null
* Barcode never fired keydown event more than 1 time before the same key fired keyup event
* Barcode generally finishes all events (like keydown > keypress > keyup) of single key at a time, if two different keys are pressed then it is with keyboard
*/
if (cforKeyDown != (char) e.KeyCode || cforKeyDown == '\0')
{
cforKeyDown = '\0';
_barcode.Clear();
return;
}
// getting the time difference between 2 keys
int elapsed = (DateTime.Now.Millisecond - _lastKeystroke);
/*
* Barcode scanner usually takes less than 17 milliseconds to read, increase this if neccessary of your barcode scanner is slower
* also assuming human can not type faster than 17 milliseconds
*/
// Bumped it up to 35[ms]
if (elapsed > 50)
_barcode.Clear();
// Do not push in array if Enter/Return is pressed, since it is not any Character that need to be read
if (e.KeyCode != Keys.Return)
{
//_barcode.Add((char) e.KeyData);
_barcode.Add((char) e.KeyData);
}
// Barcode scanner hits Enter/Return after reading barcode
if (e.KeyCode == Keys.Return && _barcode.Count > 0)
{
string BarCodeData = new String(_barcode.ToArray());
if (!UseKeyboard)
{
//MessageBox.Show(String.Format("{0}", BarCodeData));
label1.Text = String.Format("{0}", BarCodeData);
}
//_barcode.Clear();
}
// update the last key stroke time
_lastKeystroke = DateTime.Now.Millisecond;
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
//Debug.WriteLine("CS_Single_Label_Printer_KeyDown : " + (char)e.KeyCode);
cforKeyDown = (char) e.KeyCode;
}
I've narrowed down the problem to the fact that the alphabetic parts of the alpha-numeric barcodes being scanned are uppercase. I generated a test barcode using lower-case letters, and everything worked as it should, but this still doesn't fix the problem as I have to scan thousands of already printed barcodes that have uppercase characters encoded into the barcode data matrix.
Is there a way to switch the above code in order to account for the 'character | Shift' KeyData?
The answer is that KeyCode returns the KEY pressed. It does not respect the case of the key. Use Shift to get the case. Below is a working example based on your supplied code. The magic is in the local function ToCharacter. You'll need to play with the if statements at the top to include numbers too if that's your desire.
From1.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace DesktopApp2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
char cforKeyDown = '\0';
int _lastKeystroke = DateTime.Now.Millisecond;
List<char> _barcode = new List<char>();
bool UseKeyboard = false;
private void Form1_Load(object sender, EventArgs e)
{
this.ActiveControl = InputBox;
this.InputBox.KeyDown += new KeyEventHandler(Form1_KeyDown);
this.InputBox.KeyUp += new KeyEventHandler(Form1_KeyUp);
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
char NotALetter = '-';
DebugBox.Items.Clear();
DebugBox.Items.Add($"KeyEventArgs->KeyCode = {e.KeyCode.ToString()}");
DebugBox.Items.Add($"KeyEventArgs->KeyData = {e.KeyData.ToString()}");
DebugBox.Items.Add($"KeyEventArgs->KeyValue = {e.KeyValue.ToString()}");
DebugBox.Items.Add($"KeyEventArgs->e.Shift = {e.Shift.ToString()}");
DebugBox.Items.Add($"ToCharacter = {ToCharacter(e) ?? NotALetter}");
char? ToCharacter(KeyEventArgs kea)
{
int DtoNumPadOffset = (int)(Keys.NumPad0 - Keys.D0);
if (kea.KeyCode < Keys.D0) return null; // Keys.D0 through Kyes.D9 are the Keys on the top row of the keyboard. D9 is right next to A in the enum type.
if (kea.KeyCode > Keys.NumPad9) return null;
if (kea.KeyCode > Keys.Z && kea.KeyCode < Keys.NumPad0) return null; //knocks out keys between Z and NumPad0
if (kea.Shift && kea.KeyCode < Keys.A) return null; // rejects special characters when numbers across the top are used with the <shift> key.
if (kea.KeyCode < Keys.A) return (char)kea.KeyCode;
if (kea.KeyCode > Keys.Z) return (char)((int)kea.KeyCode - DtoNumPadOffset);
return kea.Shift
? (char)kea.KeyCode
: char.ToLower((char)kea.KeyCode);
}
// if keyboard input is allowed to read
if (UseKeyboard && e.KeyData != Keys.Enter)
{
MessageBox.Show(e.KeyData.ToString());
}
/* check if keydown and keyup is not different
* and keydown event is not fired again before the keyup event fired for the same key
* and keydown is not null
* Barcode never fired keydown event more than 1 time before the same key fired keyup event
* Barcode generally finishes all events (like keydown > keypress > keyup) of single key at a time, if two different keys are pressed then it is with keyboard
*/
//if (cforKeyDown != (char)e.KeyCode || cforKeyDown == '\0')
//{
// cforKeyDown = '\0';
// _barcode.Clear();
// return;
//}
// getting the time difference between 2 keys
int elapsed = (DateTime.Now.Millisecond - _lastKeystroke);
/*
* Barcode scanner usually takes less than 17 milliseconds to read, increase this if neccessary of your barcode scanner is slower
* also assuming human can not type faster than 17 milliseconds
*/
// Bumped it up to 35[ms]
//if (elapsed > 2000)
if (e.KeyCode == Keys.Return)
{
InputBox.Text = String.Empty;
_barcode.Clear();
}
// Do not push in array if Enter/Return is pressed, since it is not any Character that need to be read
if (e.KeyCode != Keys.Return)
{
char? TestForCharacter = ToCharacter(e);
if (TestForCharacter != null)
{
//_barcode.Add((char) e.KeyData);
_barcode.Add((char)TestForCharacter);
}
}
OutputBox.Text = string.Concat(_barcode);
// Barcode scanner hits Enter/Return after reading barcode
if (e.KeyCode == Keys.Return && _barcode.Count > 0)
{
string BarCodeData = new String(_barcode.ToArray());
if (!UseKeyboard)
{
//MessageBox.Show(String.Format("{0}", BarCodeData));
OutputBox.Text = String.Format("{0}", BarCodeData);
}
//_barcode.Clear();
}
// update the last key stroke time
_lastKeystroke = DateTime.Now.Millisecond;
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
//Debug.WriteLine("CS_Single_Label_Printer_KeyDown : " + (char)e.KeyCode);
cforKeyDown = (char)e.KeyCode;
}
}
}
Form1.Designer.cs
namespace DesktopApp2
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.OutputBox = new System.Windows.Forms.TextBox();
this.InputBox = new System.Windows.Forms.TextBox();
this.DebugBox = new System.Windows.Forms.ListBox();
this.SuspendLayout();
//
// OutputBox
//
this.OutputBox.Location = new System.Drawing.Point(52, 93);
this.OutputBox.Name = "OutputBox";
this.OutputBox.ReadOnly = true;
this.OutputBox.Size = new System.Drawing.Size(650, 22);
this.OutputBox.TabIndex = 1;
//
// InputBox
//
this.InputBox.Location = new System.Drawing.Point(52, 45);
this.InputBox.Name = "InputBox";
this.InputBox.Size = new System.Drawing.Size(650, 22);
this.InputBox.TabIndex = 2;
//
// DebugBox
//
this.DebugBox.FormattingEnabled = true;
this.DebugBox.ItemHeight = 16;
this.DebugBox.Location = new System.Drawing.Point(52, 131);
this.DebugBox.Name = "DebugBox";
this.DebugBox.Size = new System.Drawing.Size(650, 308);
this.DebugBox.TabIndex = 3;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(716, 450);
this.Controls.Add(this.DebugBox);
this.Controls.Add(this.InputBox);
this.Controls.Add(this.OutputBox);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox OutputBox;
private System.Windows.Forms.TextBox InputBox;
private System.Windows.Forms.ListBox DebugBox;
}
}
Tidy helper class for dealing with KeyEventArgs
The Is* functions group the keycodes into ranges and detect (if appropriate) the shift states. To* functions produce the char that best represents the KeyCode in the event. ToAlphaNumericAsciiCharacter simply pairs up detectors with producers.
public static class KeyEventArgs_Functions
{
public static bool Between(this Keys source, Keys lhv, Keys rhv)
{
return source >= lhv && source <= rhv;
}
public static bool IsLetterKeyBoardChar(this KeyEventArgs source)
{
return source.KeyCode.Between(Keys.A, Keys.Z);
}
public static bool IsNumberKeyBoardChar(this KeyEventArgs source)
{
return !source.Shift && source.KeyCode.Between(Keys.D0, Keys.D9);
}
public static bool IsNumber10KeyPadChar(this KeyEventArgs source)
{
return source.KeyCode.Between(Keys.NumPad0, Keys.NumPad9);
}
public static char ToLetterKeyBoardChar(this KeyEventArgs source) // Only returns a valid value if IsLetterKeyBoardChar returns true.
{
return source.Shift ? (char)source.KeyCode: char.ToLower((char)source.KeyCode);
}
public static char ToNumberKeyBoardChar(this KeyEventArgs source) // Only returns a valid value if IsNumberKeyBoardChar returns true.
{
return (char)source.KeyCode;
}
public static char ToNumber10KeyPadChar(this KeyEventArgs source) // Only returns a valid value if IsNumber10KeyPadChar returns true.
{
const int DtoNumPadOffset = (int)(Keys.NumPad0 - Keys.D0);
return (char)((int)source.KeyCode - DtoNumPadOffset);
}
public static char? ToAlphaNumericAsciiCharacter(this KeyEventArgs source)
{
if (source.IsLetterKeyBoardChar()) return source.ToLetterKeyBoardChar();
if (source.IsNumberKeyBoardChar()) return source.ToNumberKeyBoardChar();
if (source.IsNumber10KeyPadChar()) return source.ToNumber10KeyPadChar();
return null;
}
}
Related
I need the following code : When I press shift key then I write small letters in my TextBox in other case I write big letters something like a “reverse” or all time pressed Caps Lock Key.
So This code and other similar is helpless because there are only one kind of letter size :
textBox1.CharacterCasing = CharacterCasing.Upper;
textBox1.CharacterCasing = CharacterCasing.Lower;
Thanks for help !
The easiest option is to change the text in the TextChanged event method. After entering a character, change its layout. But you must remember that text can be pasted / cut.
If you ignore this problem, you can use this simple example:
public partial class Form1 : Form
{
int last_len = 0;
bool char_to_lower = false;
public Form1()
{
InitializeComponent();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
// save last cursor position
var select_index = textBox1.SelectionStart;
// if text not delete - change char casing
if (textBox1.Text.Length > last_len && select_index > 0)
{
StringBuilder sb = new StringBuilder();
sb.Append(textBox1.Text.Take(select_index - 1).ToArray());
// check SHIFT and CAPS
if (char_to_lower || Control.IsKeyLocked(Keys.CapsLock))
sb.Append(textBox1.Text[select_index - 1].ToString().ToLower());
else
sb.Append(textBox1.Text[select_index - 1].ToString().ToUpper());
sb.Append(textBox1.Text.Skip(select_index).ToArray());
// insert new text in textBox
textBox1.Text = sb.ToString();
// return cursor position
textBox1.SelectionStart = select_index;
}
// save last length
last_len = textBox1.Text.Length;
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey || e.KeyCode == Keys.Shift) char_to_lower = true;
}
private void textBox1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey || e.KeyCode == Keys.Shift) char_to_lower = false;
}
}
I have an option form where the user has to enter parameters for a mini-game, going from 8 to 32. My problem is that as soon as I start typing, if I insert a number under 8 (I want to put 20, for example), the event activates as soon as I type 2 and turn it into 8.
private void TXBheight_TextChanged(object sender, EventArgs e)
{
if(int.Parse(TXBheight.Text) < 8)
{
TXBheight.Text = "8";
}
else if (int.Parse(TXBheight.Text) > 32)
{
TXBheight.Text = "32";
}
}
Is there any easy way to make a delay, or wait until I finish typing?
For those who identify this question as a possible duplicate, i took a look, and the possible answers are from 6 years ago. During that time, languages and compilers evolve, so maybe there is something new we can all learn from
Instead of using a TextChanged event, use the TextBox _Validating event and the _Validated event. The _Validating event is fired only when the text box loses focus, i.e., when the user clicks on another control, e.g., a Button or another TextBox. When this happens, the _Validating event is fired and you test the value in the text box. If it's invalid, you cancel the _Validating event. If its valid, you DON'T cancel the _Validating event, and as a a result the _Validated event is fired. In the _Validated event, you do what you neeed to do when the input data is valid. Use an errorprovider to inform the user when the input data is invalid.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
errorProvider1.SetError(TXBheight, "");
//NEW NEW NEW
buttonCancel.CausesValidation = false;
}
private void Button1_Click(object sender, EventArgs e)
{
// do what is needed when the button is clicked
}
private void TXBheight_Validating(object sender, CancelEventArgs e)
{
errorProvider1.SetError(TXBheight, "");
if (String.IsNullOrEmpty(TXBheight.Text))
{
errorProvider1.SetError(TXBheight, "Height is a required field");
e.Cancel = true;
return;
}
if (int.Parse(TXBheight.Text) < 8)
{
errorProvider1.SetError(TXBheight, "Height must be GE 8");
e.Cancel = true;
return;
}
if (int.Parse(TXBheight.Text) > 32)
{
errorProvider1.SetError(TXBheight, "Height must be LE 32");
e.Cancel = true;
return;
}
}
private void TXBheight_Validated(object sender, EventArgs e)
{
//this event is fired when the data is valid, i.e.,
// if e.Cancel in the _Validating method is NOT set to cancel
}
//NEW NEW NEW
private void ButtonCancel_Click(object sender, EventArgs e)
{
AutoValidate = AutoValidate.Disable;
Close();
}
// NEW #2
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
DialogResult result = MessageBox.Show("Do you really want to exit?", "Dialog Title", MessageBoxButtons.YesNo);
if (result == DialogResult.Yes)
{
Environment.Exit(0);
}
else
{
e.Cancel = true;
}
}
else
{
e.Cancel = true;
}
}
}
The simple answer is:
(C# 7 style)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.textBox1.TextChanged += TextBox1_TextChanged;
this.textBox1.Leave += TextBox1_Leave;
}
private void TextBox1_TextChanged(object sender, EventArgs e)
{
string text = this.textBox1.Text;
if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.CurrentCulture, out int number))
{
this.textBox1.Text = "";
return;
}
if (number > 32)
{
this.textBox1.Text = "32";
}
}
private void TextBox1_Leave(object sender, EventArgs e)
{
string text = this.textBox1.Text;
if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.CurrentCulture, out int number))
{
this.textBox1.Text = "8";
return;
}
if (number > 32)
{
this.textBox1.Text = "32";
}
if (number < 8)
{
this.textBox1.Text = "8";
}
}
I standardly do this with controlling the pressed keys and text changes (inclusive paste) to check correct content of the window. Unfortunately I have the code only for Borland C++ Builder and VS6 at work. Recreating this code is not that simple (too much code), therefore only the simple answer.
Use Microsoft's Reactive Framework and this becomes easy. Just do this:
private void Form1_Load(object sender, EventArgs e)
{
IObservable<long> query =
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => TXBheight.TextChanged += h,
h => TXBheight.TextChanged -= h)
.Select(x => Observable.Timer(TimeSpan.FromMilliseconds(250.0)))
.Switch()
.ObserveOn(this);
IDisposable subscription = query.Subscribe(ep =>
{
if (int.Parse(TXBheight.Text) < 8)
{
TXBheight.Text = "8";
}
else if (int.Parse(TXBheight.Text) > 32)
{
TXBheight.Text = "32";
}
});
}
Now there is a 250.0 millisecond delay after the last character is typed before your code runs. If a new character is typed before the 250.0 milliseconds is up then a new timer starts and the old one doesn't fire.
The .ObserveOn(this) code marshalls the timer back to the UI thread.
Just NuGet "System.Reactive" and "System.Reactive.Windows.Forms". Also add using System.Reactive.Linq; at the top of your class.
You can use the return as your breakpoint, when the user hit enter then you run your code.
You can use that with the KeypressEvent.
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
char ch = e.KeyChar; // Getting the Key that was pressed
if (ch == 13) // Checking if it equal to 13 ASCII code for return
{
if (int.Parse(textBox1.Text) < 8)
{
textBox1.Text = ""; // emptying the textbox
textBox1.AppendText("8"); // using AppendText() to keep the cursor at the end
}
else if (int.Parse(textBox1.Text) > 32)
{
textBox1.Text = "";
textBox1.AppendText("32");
}
e.Handled = true; // To say that the event was handled.
}
}
Why not create task and check if it is completed before executing?
private Task task; //declare it at the top
private void TXBheight_TextChanged(object sender, EventArgs e)
{
if(task?.Status == TaskStatus.Running) return;
task = Task.Run( () =>
{
if(int.Parse(TXBheight.Text) < 8)
{
TXBheight.Text = "8";
}
else if (int.Parse(TXBheight.Text) > 32)
{
TXBheight.Text = "32";
}
});
}
If it's strange its going to happen to me.
I'm writing a C# Windows Form application (VS 2010) comprised of multiple textboxes as well as multiple labels for them all. Except for Textbox names the actions are all the same (copied and pasted then the proper TextBox name substituted. They all include a TextChange event and a KeyDown event. The KeyDown event is to catch the Return key and process the data.
The bazaar part is that the first section (region Operating Frequency) works fine. All others never enter the KeyDown event. I've looked through and compared properties for all TextBoxes and I see no difference. I've tried moving the sections around to see if it is the order in the code. Nothing changes. Operating Frequency always works. Nothing else does. I've tried just about everything I can think of. I've even tried going from KeyDown to KeyUp with no difference;
Does anyone have any ideas?
Here is the code. I've only included the first 2 textboxes as the rest are essentially the same.
// Constants
public static readonly double rad = Math.PI / 180, DEG = 180 / Math.PI; // PI to RAD
public const double solM = 299792458; // Speed of light in meters/sec
public const double solMm = solM / 1000; // Speed of light in mega meters/sec
public const double solFt = 983571056.43; // Speed of light in feet/sec
public const double ft2mtr = 0.3048; // Convert Feet to Meters
// Parameters
public static double D = 60; // Loop diameter
public static double C = D*Math.PI; // Loop Circumfrence
public static double conductorD = 0.375; // Conductor diameter
public static double RL = 0; // Added loss resistance
public static double xmitP = 25; // RF xmitter power
public static double freq = 14.1; // Frequence
public MagLoopAntenna()
{
InitializeComponent();
// Start off with some default parameter values
tbFreq.Text = "14.1"; // Target Frequency
tbLoopDia.Text = "60"; // Antenna Loop Diameter
tbUnitsLoopDia.Text = "in";
tbConductorDia.Text = "0.5"; // Conductor Diameter
tbUnitsConductorDia.Text = "in"; // Conductor Diameter Units
tbAddedLossR.Text = "0"; // Added Loss in ohms
tbRfPower.Text = "25"; // RF Power in Watts
}
private bool nonNumberEntered = false; // Command Key Flag
#region Operating Frequency
private void tbFreq_TextChanged(object sender, EventArgs e)
{
int test;
test = 0; // place for breakpoint
}
private void tbFreq_KeyDown(object sender, KeyEventArgs e)
{
// Initialize the flag to false.
nonNumberEntered = false;
// Determine whether the keystroke is a number from the top of the keyboard.
if (e.KeyCode < Keys.D0 || e.KeyCode > Keys.D9)
{
// Determine whether the keystroke is a number from the keypad.
if (e.KeyCode < Keys.NumPad0 || e.KeyCode > Keys.NumPad9)
{
// Determine whether the keystroke is a backspace.
if (e.KeyCode != Keys.Back)
{
// A non-numerical keystroke was pressed.
// Set the flag to true and evaluate in KeyPress event.
nonNumberEntered = true;
}
if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Tab)
{
// Note: may want calculate everything at this point.
// We got here now move on to the next textbox
freq = Convert.ToDouble(tbFreq.Text);
tbLoopDia.Focus();
}
}
}
}
#endregion
#region Loop Diameter
private void tbLoopDia_TextChanged(object sender, EventArgs e)
{
int test;
test = 0; // Just a place to put a breakpoint
}
private void tbLoopDia_KeyDown(object sender, KeyEventArgs e)
{
// Initialize the flag to false.
nonNumberEntered = false;
// Determine whether the keystroke is a number from the top of the keyboard.
if (e.KeyCode < Keys.D0 || e.KeyCode > Keys.D9)
{
// Determine whether the keystroke is a number from the keypad.
if (e.KeyCode < Keys.NumPad0 || e.KeyCode > Keys.NumPad9)
{
// Determine whether the keystroke is a backspace.
if (e.KeyCode != Keys.Back)
{
// A non-numerical keystroke was pressed.
// Set the flag to true and evaluate in KeyPress event.
nonNumberEntered = true;
}
if (e.KeyCode == Keys.Enter)
{
int gothere;
gothere = 1; // Just for a breakpoint
// Note: may want calculate everything at this point.
// We got here now move on to the next textbox
//tbConductorDia.Focus(); // will implement this once its' working
}
}
}
}
I have started to learn some Microsoft.DirectX 3D basic techniques, and bumped to this problem that is too dificult for me. I have created 3D Scene, Camera, Light's etc, with some simple 3D objects drawn. What I want to do now is simple 3D rotation of that objects around all three axes. For that I want to use Arrow and ASWD keyboard keys as in manner in computer games are used. For that I have used OnKeyDown and OnKeyUp event handlers :
#region - Event handler -
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
#region - speed -
if(e.KeyValue == (char)Keys.Add || e.KeyValue == (char)Keys.Oemplus)
step_p = true;
if(e.KeyValue == (char)Keys.Subtract || e.KeyValue == (char)Keys.OemMinus)
step_m = true;
if(step_p)
step++;
if(step_m)
step--;
if(step==0)
step = 1;
#endregion
#region - rotation -
if(e.KeyCode == Keys.Left)
{
left = true;
}
if(e.KeyCode == Keys.Right)
{
right = true;
}
if(e.KeyCode == Keys.Up)
{
up = true;
}
if(e.KeyCode == Keys.Down)
{
down = true;
}
if(e.KeyCode == Keys.A)
{
a = true;
}
if(e.KeyCode == Keys.D)
{
d = true;
}
if(left)
angleZ_D+=step;
if(right)
angleZ_D-=step;
if(up)
angleX_D-=step;
if(down)
angleX_D+=step;
if(a)
angleY_D+=step;
if(d)
angleY_D-=step;
//
//
//
if(angleX_D == 360)
angleX_D = 0;
if(angleY_D >= 360)
angleY_D = 0;
if(angleZ_D >= 360)
angleZ_D = 0;
angleX_R = angleX_D * rad;
angleY_R = angleY_D * rad;
angleZ_R = angleZ_D * rad;
//
//
//
#endregion
#region - zoom -
if(e.KeyValue == (char)Keys.W)
{
w = true;
}
if(e.KeyValue == (char)Keys.S)
{
s = true;
}
if(w)
{
zoom+=0.1f * step;
if(zoom>10.0f) zoom=10.0f;
}
if(s)
{
zoom-=0.1f * step;
if(zoom<1.0f) zoom=1.0f;
}
#endregion
Draw();
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
#region - speed -
if(e.KeyValue == (char)Keys.Add || e.KeyValue == (char)Keys.Oemplus)
step_p = false;
if(e.KeyValue == (char)Keys.Subtract || e.KeyValue == (char)Keys.OemMinus)
step_m = false;
#endregion
#region - rotation -
if(e.KeyCode == Keys.Left)
{
left = false;
}
if(e.KeyCode == Keys.Right)
{
right = false;
}
if(e.KeyCode == Keys.Up)
{
up = false;
}
if(e.KeyCode == Keys.Down)
{
down = false;
}
if(e.KeyCode == Keys.A)
{
a = false;
}
if(e.KeyCode == Keys.D)
{
d = false;
}
#endregion
#region - zoom -
if(e.KeyValue == (char)Keys.W)
{
w = false;
}
if(e.KeyValue == (char)Keys.S)
{
s = false;
}
#endregion
Draw();
}
This is the problem :
When I press key A and while holding it down I press key W, program works perfectly but when I release key W the event handler do not recognize that key A is still pressed.
Is there solution for such problem in C# 4.0 and .Net framework 2.0
All the best,
Željko Perić
According to
Quote:
You are depending on the keyboard repeating a key when held down.
That stops working as soon as you press another key.
You instead need to use a Timer and in the Tick event handler
check which way you want to move. Now key-repeat no longer matters.
Set the timer's Interval property to 15 or 31 to get good consistency. – Hans Passant
this is problem solution :
using System;
using System.Windows.Forms;
namespace WASD_keyboard_game_control
{
public partial class MainForm : Form
{
//
// Global variables declaration
//
//
// Timer
//
// Implements a timer that
// raises event at user defined interval
//
Timer Clock;
//
// Set Timer tick Interval in miliseconds
//
// By changing value of Interval,
// we control how often the timer tick event handler is raised
// and eventually the call frequency of Main_Function()
//
const int Interval = 15;
//
// Variables for counting the number
// of times that program has confirmed
// the key down state of AWSD keys
//
int W = 0;
int A = 0;
int S = 0;
int D = 0;
//
// Variables ( flags ) for memorizing
// the key down state of AWSD keys
//
// true - key is down ( pressed )
//
// false - key is up ( released )
//
//
bool w = false;
bool a = false;
bool s = false;
bool d = false;
//
//
//
public MainForm()
{
//
// The InitializeComponent() call is required
// for Windows Forms designer support.
//
InitializeComponent();
//
// Call Initialize_Timer() function
//
Initialize_Timer();
}
void Initialize_Timer()
{
// Create new timer
Clock = new Timer();
// Set timer tick interval in miliseconds
//
// By changing value of interval,
// we control how often the timer tick event handler is raised
// and eventually the call frequency of Main_Function()
//
Clock.Interval = Interval;
//
// Add timer tick event handler
//
// This event handler is raised
// every time timer make tick
//
// The smaler value for timer interval,
// more often the event handler is raised
//
Clock.Tick += new System.EventHandler(this.ClockTick);
//
// Start timer
//
Clock.Start();
}
void ClockTick(object sender, EventArgs e)
{
//
// Timer tick event handler
//
//
// Call Main_Function()
//
Main_Function();
//
//
//
}
protected override void OnKeyDown(KeyEventArgs e)
{
//
// This event handler is raised every time
// some key on the keyboard is first pressed
//
base.OnKeyDown(e);
//
// Set corresponding key down flag state to true
//
if(e.KeyCode == Keys.W)
w = true;
if(e.KeyCode == Keys.A)
a = true;
if(e.KeyCode == Keys.S)
s = true;
if(e.KeyCode == Keys.D)
d = true;
}
protected override void OnKeyUp(KeyEventArgs e)
{
//
// This event handler is raised every time
// some key on the keyboard is released
//
base.OnKeyUp(e);
//
// Set corresponding key down flag state to false
//
if(e.KeyCode == Keys.W)
w = false;
if(e.KeyCode == Keys.A)
a = false;
if(e.KeyCode == Keys.S)
s = false;
if(e.KeyCode == Keys.D)
d = false;
}
void Main_Function()
{
//
// Main function
//
//
// This function is called every time the
// timer tick event handler is raised
//
//
// This function determines wich key is pressed
// upon key down flag value and updates correspondig counter value
//
// Counter value shows how many times program has confirmed the
// key down state and not how many times user has pressed the key
//
//
// Increase counter value if key is down
//
if(a)
A++;
if(s)
S++;
if(w)
W++;
if(d)
D++;
//
// Show values of counters
//
Label_A.Text = "A = " + A.ToString();
Label_S.Text = "S = " + S.ToString();
Label_W.Text = "W = " + W.ToString();
Label_D.Text = "D = " + D.ToString();
}
}
}
I am coding my own game using Windows Forms Applications. It is supposed to be a multiplayer game. Every player can control its brick to keep the ball in the pitch, but the problem is that both players cannot press controls at the same time. Every time the second player presses the key during the movement of the first, the brick of the first player stops. But, if they press the keys at the same tame, both bricks move. I used the KeyDown event:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.W && one.BrickLocationY > 0)
{
one.BrickLocationY -= 17;
}
if (e.KeyCode == Keys.S && one.BrickLocationY + Brick.BrickHeight < screenHeight)
{
one.BrickLocationY += 17;
}
if (e.KeyCode == Keys.Up)
{
two.BrickLocationY -= 17;
}
if (e.KeyCode == Keys.Down && two.BrickLocationY + Brick.BrickHeight < screenHeight)
{
two.BrickLocationY += 17;
}
if (e.KeyCode == Keys.Escape)
{
Application.Exit();
}
}
Ok, this allows me to move the brick up and down. If I press two keys at once, both bricks move in the wanted direction. The bricks are drawn on a paint event triggered by the timer tick whose interval is set to 1.
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawEllipse(ellipsePen, x, y, ballSize, ballSize );
e.Graphics.FillEllipse(Brushes.White ,x+1, y+1, ballSize, ballSize);
e.Graphics.FillRectangle(Brushes.White, one.BrickLocationX+1, one.BrickLocationY+1, Brick.BrickWidth, Brick.BrickHeight);
e.Graphics.FillRectangle(Brushes.White, two.BrickLocationX+1, two.BrickLocationY+1, Brick.BrickWidth, Brick.BrickHeight);
}
I also had attempts to do this with the combination of KeyUp and KeyPress but with no success. The only thing that came to my mind was threading bricks, but had no idea how to do it. I there any way I can handle multiplayer controls like this without threading?
P.S.The keyboard is capable of handling multiple buttons at once.
When the KeyDown event fires, you need to check which buttons are pressed every time. Here is what I found upon a quick search on the topic.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Collections.Generic;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(Form1_FormClosing);
tm.Tick += new System.EventHandler(DoSomethingWithKeyboardInput);
this.Load += new System.EventHandler(Form1_Load);
textBox1.KeyDown += new System.Windows.Forms.KeyEventHandler(textbox1_KeyDown);
textBox1.KeyUp += new System.Windows.Forms.KeyEventHandler(textbox1_KeyDown);
}
private Timer tm = new Timer();
private List<System.Windows.Forms.Keys> MovementKeys = new List<System.Windows.Forms.Keys>();
private _MyInputKeys MyInputKeys = new _MyInputKeys();
private struct _MyInputKeys
{
public bool Jump;
public bool Left;
public bool Right;
}
private void Form1_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
{
tm.Stop();
}
public void DoSomethingWithKeyboardInput(object sender, EventArgs e)
{
textBox1.Text = (MyInputKeys.Left ? "(left)" : "") +
(MyInputKeys.Right ? "(right)" : "") + (MyInputKeys.Jump ? "(jump)" : "");
}
private void Form1_Load(object sender, System.EventArgs e)
{
//define keys used for movement
MovementKeys.Add(Keys.Up); //Jump ?
MovementKeys.Add(Keys.Left); //Left Arrow - Move Left
MovementKeys.Add(Keys.Right); //Rigth Arrow - Move Right
tm.Interval = 50;
tm.Start();
}
private void textbox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (MovementKeys.IndexOf(e.KeyCode) != -1)
{
e.Handled = true;
MyInputKeys.Jump = IsKeyDown(Keys.Up);
MyInputKeys.Left = IsKeyDown(Keys.Left);
MyInputKeys.Right = IsKeyDown(Keys.Right);
}
}
public static bool IsKeyDown(Keys key)
{
return (GetKeyState(Convert.ToInt16(key)) & 0X80) == 0X80;
}
/// <summary>
/// If the high-order bit is 1, the key is down; otherwise, it is up.
/// If the low-order bit is 1, the key is toggled.
/// A key, such as the CAPS LOCK key, is toggled if it is turned on.
/// The key is off and untoggled if the low-order bit is 0.
/// A toggle key's indicator light (if any) on the keyboard will be on when
/// the key is toggled, and off when the key is untoggled.
/// </summary>
/// <param name="nVirtKey"></param>
[DllImport("user32.dll")]
public extern static Int16 GetKeyState(Int16 nVirtKey);
}
}
I believe you'll need to track this yourself. Basically on key down you recognize the w and consider it being held until key up releases the w. same with the s key.
Then in your run loop, you just look for what keys are active and do your direction logic there.