C# keyboard event handler rectangle - c#

I am new to C# and try to make an keyboard event. It should display when the keys W, A, S or D are pressed. First my plan was to show some pictureBox and just change the picture if the right keys are pressed.
But then I searched the internet an found something similar in Java http://docs.oracle.com/javase/8/javafx/sample-apps/KeyboardExample.zip
and it looks like this:
As I can understand the code is drawing a rectangle with some letter in it. I looked the msdn and found a example for drawing a rectangle:
https://msdn.microsoft.com/de-de/library/sx8yykw8(v=vs.110).aspx
Unfortunately I stucking in the drawing. Normally I use the toolbox to add things to the form. Then I double-click it and write my code inside the braces. But there is no "Rectangle" in the toolbox, so I am not sure how to add it.
This is my code so far:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//Stay always on top
this.TopMost = true;
//Does not work. Removes border but you can't move the window after this
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
}
private void Form1_Load(object sender, EventArgs e)
{
//Can I delete this?
}
private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar >= 65 && e.KeyChar <= 122)
{
switch (e.KeyChar)
{
//If pressed w or W
case (char)119:
case (char)87:
Console.WriteLine(e.KeyChar);
break;
//If pressed a or A
case (char)97:
case (char)65:
Console.WriteLine(e.KeyChar);
break;
//If pressed s or S
case (char)83:
case (char)115:
Console.WriteLine(e.KeyChar);
break;
//If pressed d or D
case (char)100:
case (char)68:
Console.WriteLine(e.KeyChar);
break;
//Other keys
default:
lblMessage.Text = "Key not supported";
//does not work
//timer1_Tick();
break;
}
}
}
private void timer1_Tick(object sender, EventArgs e)
{
lblMessage.Hide();
}
}
And here is how my Form looks now:
Other things I am stucking at the moment:
How I can call the timer from the Form1_KeyPress to hide the lblMessage after some seconds?
Remove border without losing the ability to move the window (like with this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; for example)
EDIT: I changed the code to the newest working state.

Welcome to the world of Windows desktop programming!
You have two choices here; you can either:
Add components to the WASD form using the Design View (and since you have the W, A, S, D boxes there it looks like you have already added them in) and in your Form1_KeyPress() handler, just update the properties of the boxes. This can be as simple as the following, just make sure to change it to the correct component name:
//If pressed w or W
case (char)119:
case (char)87:
Console.WriteLine(e.KeyChar);
button1.BackColor = Color.Red;//Highlight W
button2.BackColor = Color.Empty;//Ignore A
button3.BackColor = Color.Empty;//Ignore S
button3.BackColor = Color.Empty;//Ignore D
break;
Override the form's OnDraw() handler and paint the boxes directly on the screen. This is harder but gives you a lot more power.
Turning off the label is easy. In your Form1_Load() handler make sure to set the timer1's timeout property:
private void Form1_Load(object sender, EventArgs e)
{
timer1.Interval = 5000;//In ms = thousandths-of-a-second
}
Turn the timer on in the Form1_KeyPress() handler:
private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
...
lblMessage.Enabled = true;
timer1.Start();
}
Do your work and turn off the timer in the timer1_Tick() handler:
private void timer1_Tick(object sender, EventArgs e)
{
lblMessage.Enabled = false;
timer1.Stop();
}

Here is what I quickly put together:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WASD_Keyboard
{
public partial class Form1 : Form
{
private PictureBox pictureBox1 = new PictureBox();
private bool wPressed = false;
private bool aPressed = false;
private bool sPressed = false;
private bool dPressed = false;
private Timer timer = new Timer();
public Form1()
{
InitializeComponent();
//Stay always on top
this.TopMost = true;
//Does not work. Removes border but you can't move the window after this
this.FormBorderStyle = FormBorderStyle.None;
timer.Interval = 3000;
//this is an event binding
timer.Tick += timer1_Tick;
}
private void Form1_Load(object sender, EventArgs e)
{
// Dock the PictureBox to the form and set its background to white.
pictureBox1.Dock = DockStyle.Fill;
pictureBox1.BackColor = Color.White;
pictureBox1.Paint += DrawRectangleRectangle;
// Add the PictureBox control to the Form.
this.Controls.Add(pictureBox1);
}
private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
// reversed logic to stop nesting
if (e.KeyChar < 65 || e.KeyChar > 122) return;
wPressed = false;
aPressed = false;
sPressed = false;
dPressed = false;
//this should really be multiple if statement so it can do more than one key
//If pressed w or W
if (e.KeyChar == (char) 119 || e.KeyChar == (char) 87) {
wPressed = true;
Console.WriteLine(e.KeyChar);
}
//If pressed a or A
if (e.KeyChar == (char) 97 || e.KeyChar == (char) 65) {
aPressed = true;
Console.WriteLine(e.KeyChar);
}
//If pressed s or S
if (e.KeyChar == (char) 83 || e.KeyChar == (char) 115) {
sPressed = true;
Console.WriteLine(e.KeyChar);
}
//If pressed d or D
if (e.KeyChar == (char) 100 || e.KeyChar == (char) 68) {
dPressed = true;
Console.WriteLine(e.KeyChar);
}
if (!wPressed && !aPressed && !sPressed && !dPressed) {
//Something goes wrong
lblMessage.Text = "Key not supported";
return;
}
pictureBox1.Refresh();
// in older .net if you didn't do both you ran into multiple issues
timer.Enabled = true;
timer.Start();
}
public void DrawRectangleRectangle(object sender, PaintEventArgs e)
{
DrawRectangle(e, new Point(40, 10), new Size(20, 20), 'W', wPressed ? Color.Red : Color.White);
DrawRectangle(e, new Point(10, 40), new Size(20, 20), 'A', aPressed ? Color.Red : Color.White);
DrawRectangle(e, new Point(40, 40), new Size(20, 20), 'S', sPressed ? Color.Red : Color.White);
DrawRectangle(e, new Point(70, 40), new Size(20, 20), 'D', dPressed ? Color.Red : Color.White);
}
public void DrawRectangle(PaintEventArgs e, Point p, Size s, char letter, Color c)
{
// Create pen.
var blackPen = new Pen(Color.Black, 3);
var brush = new SolidBrush(c);
// Create rectangle.
var rect = new Rectangle(p, s);
// Draw rectangle to screen.
e.Graphics.DrawRectangle(blackPen, rect);
e.Graphics.FillRectangle(brush, rect);
e.Graphics.DrawString(letter.ToString(), new Font(FontFamily.GenericSerif, 12), Brushes.Blue, rect);
}
private void timer1_Tick(object sender, EventArgs e)
{
wPressed = false;
aPressed = false;
sPressed = false;
dPressed = false;
timer.Enabled = false;
timer.Stop();
pictureBox1.Refresh();
}
}
}
Note: This is highly async but doesn't use any locking...

Related

How to trigger KeyDown when there is a button on Form1 [duplicate]

This question already has answers here:
Disadvantage of setting Form.KeyPreview = true?
(4 answers)
Closed 5 years ago.
The presence of a button on the Form1 window seems to prevent the KeyDown event from being triggered. What should I do to make that the button doesn't prevent KeyDown to be triggered?
Here is a picture of my Form1. timer is Enabled.
Code :
Brush brush;
int dimensions;
Point point;
Size size;
Rectangle rectangle;
Color color;
public Form1()
{
InitializeComponent();
DefineRectangle();
brush = Brushes.Black
color = BackColor;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.FillRectangle(brush, rectangle);
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Left)
brush = Brushes.Green;
if (e.KeyCode == Keys.Up)
brush = Brushes.Yellow;
if (e.KeyCode == Keys.Right)
brush = Brushes.Orange;
if (e.KeyCode == Keys.Down)
brush = Brushes.Red;
}
private void timer_Tick(object sender, EventArgs e)
{
DefineRectangle();
Invalidate();
}
private void DefineRectangle()
{
Random random = new Random();
dimensions = random.Next(5, 51) * 2;
point = new Point
(
random.Next(284 / dimensions) * dimensions,
random.Next(260 / dimensions) * dimensions
);
size = new Size(dimensions, dimensions);
rectangle = new Rectangle(point, size);
}
private void button1_Click(object sender, EventArgs e)
{
BackColor = Color.Blue;
timer.Enabled = false;
}
private void button2_Click(object sender, EventArgs e)
{
BackColor = color;
timer.Enabled = true;
}
In your Form, you may try to override ProcessCmdKey:
protected override bool ProcessCmdKey(ref Message message, Keys keyData)
{
if (keyData == Keys.Down)
return true;
return base.ProcessCmdKey(ref message, keyData);
}
Before doing this method, set the KeyPreview property = true for the form
If you want to do an easy workaround. Just add the keydown handler you specified to the handlers of the two buttons aswell. This should make it work and you can continue with your homework.
Just select them in the designer and in the properties menu you can bind the same method to their keyDown event.
Proper solution would be to do this in the constructor
public Form1() : base()
{
// Set KeyPreview object to true to allow the form to process
// the key before the control with focus processes it.
this.KeyPreview = true;
}

C# WASD control?

So I have a picturebox named picbox1 and an image inside it. I need it to move up if W is pressed , left if A is pressed and you get the idea. What's the easiest way to do this? Could you please explain the code as well.
Here is my code(just in case):
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Threading;
public class MyProgram {
public static void Main() {
Form form1 = new Form();
form1.Show();
form1.Width = 800;
form1.Height = 600;
PictureBox picbox1 = new PictureBox();
form1.Controls.Add(picbox1);
picbox1.Image = Image.FromFile("c:\\Users\\FakeUsername\\Downloads\\AnimatedTest.gif");
Application.Run();
}
}
Creating form and picture box dynamically in Program.cs code is not good idea. You should declare your Form1 from in another file. Put pictureThen use KeyPress event:
private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
int offset = 10;
if(e.KeyChar == 'a')
{
pictureBox1.Location = new Point(pictureBox1.Location.X - offset, pictureBox1.Location.Y);
}
else if(e.KeyChar == 'w')
{
pictureBox1.Location = new Point(pictureBox1.Location.X, pictureBox1.Location.Y - offset);
}
else if (e.KeyChar == 's')
{
pictureBox1.Location = new Point(pictureBox1.Location.X, pictureBox1.Location.Y + offset);
}
else if (e.KeyChar == 'd')
{
pictureBox1.Location = new Point(pictureBox1.Location.X + offset, pictureBox1.Location.Y);
}
}

Change the location of an arrow by pressing ENTER key

I am working on a C# project and I want to change the location of an arrow by pressing ENTER key with different size of iteration for different comboBox selections. Actually it works but the problem is that I can not refresh the Form before changing comboBox Selection. I want to see iteration step by step but it moves if I change the comboBox selection. Here is the code:
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == 0)
{
this.BackColor = Color.Black;
label1.ForeColor = Color.Silver;
label1.Text = "Environment is Space";
pictureBox2.Image = list.Images[4];
t = 100; // iteration amount
}
else if (comboBox1.SelectedIndex == 1)
{
this.BackColor = Color.PaleTurquoise;
label1.Text = "Environment is Water";
pictureBox2.Image = list.Images[3];
t = 50; // iteration amount
}
else if (comboBox1.SelectedIndex == 2)
{
this.BackColor = Color.DarkGoldenrod;
label1.ForeColor = Color.Firebrick;
label1.Text = "Environment is Honey";
pictureBox2.Image = list.Images[2];
t = 20; // iteration amount
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
// Drawing arrow
Pen pen = new Pen(Color.FromArgb(255, 0, 0, 255), 8);
pen.StartCap = LineCap.ArrowAnchor;
pen.EndCap = LineCap.RoundAnchor;
e.Graphics.DrawLine(pen, x+50, 200, x, 200);
}
private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
// pressed Enter => change x
if (e.KeyChar == (char)Keys.Return)
{
e.Handled = true;
if (x < y)
{
x += t;
}
}
}
To be more clear:
I want like: Click-> Enter + Move-> Arrow + Click->Enter + Move -> Arrow
Now it works like: Click-> Enter + Change-> comboBox + Move->Arrow
Thanks a lot!
Do you want a timer solution?
System.Timers.Timer timer = new System.Timers.Timer(500);
timer.Elapsed += (s, e) => {
this.Invoke((MethodInvoker)delegate { comboBox1.SelectedIndex = (comboBox1.SelectedIndex + 1) % comboBox1.Items.Count; });
};
timer.Start();
It's hard to understand what do you mean.

I still can't clear the rectangle i drawn over a control inside OnPaint event why i can'y clear it?

By clear i mean to redraw or paint or color the control back to it's original.
This is the working code:
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Drawing;
namespace FTP_ProgressBar
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
txtHost.TextChanged += anyTextBox_TextChanged;
txtUploadFile.TextChanged += anyTextBox_TextChanged;
txtDir.TextChanged += anyTextBox_TextChanged;
anyTextBox_TextChanged(null, null);
if ((txtHost.Text == "") || txtUploadFile.Text == "")
{
btnUpload.Enabled = false;
}
if (txtDir.Text == "")
{
checkBox1.Enabled = false;
}
}
private void anyTextBox_TextChanged(object sender, EventArgs e)
{
btnUpload.Enabled = txtHost.TextLength > 0 && txtUploadFile.TextLength > 0;
checkBox1.Enabled = txtDir.TextLength > 0;
this.Invalidate();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void btnBrowse_Click(object sender, EventArgs e)
{
if(this.openFileDialog1.ShowDialog() != DialogResult.Cancel)
this.txtUploadFile.Text = this.openFileDialog1.FileName;
}
private void btnUpload_Click(object sender, EventArgs e)
{
if(this.ftpProgress1.IsBusy)
{
this.ftpProgress1.CancelAsync();
this.btnUpload.Text = "Upload";
}
else
{
FtpSettings f = new FtpSettings();
f.Host = this.txtHost.Text;
f.Username = this.txtUsername.Text;
f.Password = this.txtPassword.Text;
f.TargetFolder = this.txtDir.Text;
f.SourceFile = this.txtUploadFile.Text;
f.Passive = this.chkPassive.Checked;
try
{
f.Port = Int32.Parse(this.txtPort.Text);
}
catch { }
this.toolStripProgressBar1.Visible = true;
this.ftpProgress1.RunWorkerAsync(f);
this.btnUpload.Text = "Cancel";
}
}
private void ftpProgress1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.toolStripStatusLabel1.Text = e.UserState.ToString(); // the message will be something like: 45 Kb / 102.12 Mb
this.toolStripProgressBar1.Value = Math.Min(this.toolStripProgressBar1.Maximum, e.ProgressPercentage);
}
private void ftpProgress1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
MessageBox.Show(e.Error.ToString(), "FTP error");
else if(e.Cancelled)
this.toolStripStatusLabel1.Text = "Upload Cancelled";
else
this.toolStripStatusLabel1.Text = "Upload Complete";
this.btnUpload.Text = "Upload";
this.toolStripProgressBar1.Visible = false;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Pen penBorder;
if (txtHost.TextLength <= 0)
{
penBorder = new Pen(Color.Red, 3);
e.Graphics.DrawRectangle(penBorder, txtHost.Location.X, txtHost.Location.Y, txtHost.Width - 1, txtHost.Height - 1);
}
if (txtUploadFile.TextLength <= 0)
{
penBorder = new Pen(Color.Red, 3);
e.Graphics.DrawRectangle(penBorder, txtUploadFile.Location.X, txtUploadFile.Location.Y, txtUploadFile.Width - 1, txtUploadFile.Height - 1);
}
}
}
}
I saw now that without a breakpoint if i minimize form1 when the program is running after typed text in both textBoxes and then resize the form1 it does clear the rectangles.
Strange it seems that it's taking effect only when i minimize and resize back the form1.
In the TextChanged event i tried to add: txtHost.Invalidate(); but it didn't help.
The only way that the rectangle get clear is if i minmize and resize back form1.
Or adding this.Invalidate(); did the trick.
OnPaint() only gets called when the window needs to be updated. This is a basic principle about how Windows works. If you need the window to be updated now then, yes, you need to invalidate the window so that OnPaint() will be called.
But is it ok to redraw all the form?
Sure, but it's not very performant as you are redrawing areas that don't necessarily need redrawing. Invalidate() should have a version that accepts a rectangle argument. Use it to only invalidate the area you want to update.

Detect Keypress using a timer

Code:
private void sprites_updater_Tick(object sender, EventArgs e)
{
s++;
int x = player.Location.X;
int y = player.Location.Y;
if (s == 1)
if (ModifierKeys.HasFlag(Keys.A))
{
player.Location = new Point(x - 5, y);
}
s = 0;
sprites_updater.Start();
}
So while using timer code, I wrote the same thing above (ModifierKeys.HasFlag(Keys.A)) but it didnĀ“t work. Why?!
BTW, is there any way to show a 3d camera perspective inside a WinForms Panel WITHOUT USING XNA, WPF or any other stuff (only .NET)?!
The best way to get keyboard strokes and processing them later is to catch the Keyboards events in the form using both KeyDown, KeyUp and flags:
bool isAPressed;
...
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch(e.KeyCode)
{
case Key.A:
isAPressed = true;
break;
case Key.XXXX:
...
}
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
switch(e.KeyCode)
{
case Key.A:
isAPressed = false;
break;
case Key.XXXX:
...
}
}
Then you can use this information in your timer :
private void sprites_updater_Tick(object sender, EventArgs e)
{
s++;
int x = player.Location.X;
int y = player.Location.Y;
if (s == 1)
if (isAPressed)
{
player.Location = new Point(x - 5, y);
}
s = 0;
sprites_updater.Start();
}
This is particularily interresting to handle player moves this way (arrows).

Categories