C# WPF Toggle Window Visibility using GetAsyncKeyState - c#

I would like my WPF window to hide when the left shift key is pressed and then when it is pressed again show itself. The problem I am having is that if the key is held down the the visibility flickers. It flickers at the speed of the Thread.Sleep() calls that I have within the code. I don't know how to get it to work though without being in some sort of thread. Currently I have my code in a DispatchTimer, but I have also tried using a BackgroundWorker. How can I check if the left shift key is pressed, whether my window has focus or not, and keep this flickering from occuring? Thanks.
Current code as DispatchTimer:
[DllImport("user32.dll")]
private static extern short GetAsyncKeyState(Keys vKey);
DispatcherTimer dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += dispatcherTimer_Tick;
dispatcherTimer.Interval = TimeSpan.FromMilliseconds(10);
dispatcherTimer.Start();
public MainWindow()
{
InitializeComponent();
}
private async void dispatcherTimer_Tick(object sender, EventArgs e)
{
if (GetAsyncKeyState(Keys.LShiftKey) < 0 && this.IsVisible)
{
this.Hide();
Thread.Sleep(200);
}
else if (GetAsyncKeyState(Keys.LShiftKey) < 0 && !this.IsVisible)
{
this.Show();
Thread.Sleep(200);
}
else if (GetAsyncKeyState(Keys.RShiftKey) < 0)
{
Environment.Exit(Environment.ExitCode);
}
}

Related

How to move a winforms tool to the right and then to the left continuously in the forms by using point class and timer tools?

Purpose of this code was to move a title(label) first rightwards until it hits the 600th pixel on the X axis and then leftwards until it hits the 27th pixel on the X axis of the form by using 2 timer tools and the Point class. One timer for going right and the other timer for going left. They should've work by swithing on and off consecutively after one another, however it does not work.
The label is stuck at 600th X location and does not move back to where it was.
The timer interval is 100 so it moves with a decent speed that allows us to see it moving.
namespace AlanCevreHesabiUygulamasi
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
if (label11Point.X == 27)
{
timer2.Stop();
timer1.Start();
}
if (label11Point.X == 599)
{
timer1.Stop();
timer2.Start();
}
}
Point label11Point = new Point(27, 32);
private void timer1_Tick(object sender, EventArgs e)
{
while (label11Point.X <= 600)
{
label12.Text = label11Point.X.ToString();
label11Point.X += 1;
label11.Location = label11Point;
break;
}
}
private void timer2_Tick(object sender, EventArgs e)
{
while (label11Point.X >= 27)
{
label12.Text = label11Point.X.ToString();
label11Point.X -= 1;
label11.Location = label11Point;
break;
}
}
}
}
Label is stuck at 600th pixel of the form, does not move back. How to make it work?
I'm surprised you see movement resulting from a while loop in a timer tick handler. Why have a timer if your are going to do it that way.
In this solution, I have a timer, and the movements happen during a timer tick. I also have three possible directions, Right, Left and Stopped (in case you want to have a start/stop button).
In the Windows Forms designer, I dropped both a label and a timer on the form. I left their properties alone except for the timer's Interval property (that I set to 10 (ms))
Then I added an enum and an instance of the enum as a field to the Form class:
public partial class Form1 : Form
{
private enum Direction { MoveRight, MoveLeft, Stopped }
Direction _direction;
public Form1()
{
InitializeComponent();
}
}
I double-clicked the Caption area of the form in the designer to create a form Load handler, and added a call to start the timer:
private void Form1_Load(object sender, EventArgs e)
{
timer1.Start();
}
Finally, I double-clicked the timer to get a timer Tick handler and added some code:
private void timer1_Tick(object sender, EventArgs e)
{
var curLocation = label1.Location;
if (_direction == Direction.MoveRight && curLocation.X > 600)
{
_direction = Direction.MoveLeft;
}
else if (_direction == Direction.MoveLeft && curLocation.X < 27)
{
_direction = Direction.MoveRight;
}
int offset = _direction switch
{
Direction.MoveRight => 1,
Direction.MoveLeft => -1,
_ => 0,
};
curLocation.X += offset;
label1.Location = curLocation;
}
The _direction field determines if the label is moving to the right or the left.
How can you write the code without a timer?"
You asked "How can you write the code without a timer?" I'm still flabbergasted that your label moves as the result of a while loop in an event handler - something must have changed from my good-old understanding of Win32 processing.
Anyways, I cheat and await a call to Task.Delay instead of using a timer. Take my existing code and do the following:
Add two buttons to your form (One labeled Start (named StartBtn) and the other labeled Stop (named StopBtn).
Add another Direction-typed field to the class: Direction _previousDirection;
Comment out the call to timer1.Start(); in the Form1_Load handler
Comment out all the code in the timer1_Tick method (at this point, you could remove the timer from the form if you want)
Select both buttons (Start and Stop) and press <Enter>. This will bring up click handlers for both buttons.
Change the StopBtn button's handler to look like:
New Stop Button code:
private void StopBtn_Click(object sender, EventArgs e)
{
_previousDirection = _direction;
_direction = Direction.Stopped;
}
Change the StartBtn's handler to look like the following. Note that nearly everything in the while loop (except the call to Task.Delay) is the same as the previous timer tick handler code. Also note that I made the handler async void to allow for the await keyword to do it's magic.
Start Button code:
private async void StartBtn_Click(object sender, EventArgs e)
{
_direction = _previousDirection;
while (_direction != Direction.Stopped)
{
var curLocation = label1.Location;
if (_direction == Direction.MoveRight && curLocation.X > 600)
{
_direction = Direction.MoveLeft;
}
else if (_direction == Direction.MoveLeft && curLocation.X < 27)
{
_direction = Direction.MoveRight;
}
int offset = _direction switch
{
Direction.MoveRight => 1,
Direction.MoveLeft => -1,
_ => 0,
};
curLocation.X += offset;
label1.Location = curLocation;
await Task.Delay(10);
}
}
I solved it, I don't know why but in my opening post the Form1() function only makes one of the if() conditions work. However, putting the if() statements into the timers solved the problem. Now, the title goes back and forth in the specified x axis intervals.
namespace AlanCevreHesabiUygulamasi
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
timer1.Start();
}
Point label11Point = new Point(27, 32);
private void timer1_Tick(object sender, EventArgs e)
{
while (label11Point.X >= 27)
{
label12.Text = label11Point.X.ToString();
label11Point.X += 1;
label11.Location = label11Point;
break;
}
if (label11Point.X == 600)
{
timer2.Start();
timer1.Stop();
}
}
private void timer2_Tick(object sender, EventArgs e)
{
while (label11Point.X <= 600)
{
label12.Text = label11Point.X.ToString();
label11Point.X -= 1;
label11.Location = label11Point;
break;
}
if (label11Point.X == 27)
{
timer1.Start();
timer2.Stop();
}
}

Move a control smoothly using KeyDown event

I am trying to make a simple game in C# using Visual Studio Windows Form Application. I want to let the user be able to move the blue box upwards, rightwards, downwards, and leftwards freely using the corresponding keys.
I am using a Timer which detects the new location of the box every 0.1 seconds, and a keydown event that actually changes the location of the box.
The box needs to keep moving in the corresponding direction while the key is held down.
My problem is, my current program does the job except that when the user first presses a key, the box moves a little bit once and pauses for a moment before it keeps moving. I want to make this box move more smoothly from the first key press without pausing like that. This might be difficult to explain by words so I added a gif file.
Is there a way to fix this? Here is my current code.
private int posX, posY; //Both initialized in Form Load event
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Up)
posY -= 3;
else if (e.KeyCode == Keys.Right)
posX += 3;
else if (e.KeyCode == Keys.Down)
posY += 3;
else if (e.KeyCode == Keys.Left)
posX -= 3;
}
//Timer ticks every 0.1 second
private void Timer_Tick(object sender, EventArgs e)
{
Box.Location = new Point(posX, posY);
labelPosX.Text = posX.ToString(); //Testing purposes
labelPosY.Text = posY.ToString(); //Testing purposes
}
I would love to use KeyDown event to achieve this, but if there is a better or more common way actually used in real game worlds, I would love to know about it too!
Use Keyboard.IsKeyDown method in your Timer_Tick method, and don't listen for the keydown event.
Like so:
double posX, posY;
private void Timer_Tick(object sender, EventArgs e)
{
double velocity = /*(speed: pixels per seconds)*/ 100 * /*(timer tick time in seconds)*/ 0.003;
if (Keyboard.IsKeyDown(Keys.Up))
{
posY -= velocity;
}
else if (Keyboard.IsKeyDown(Keys.Down))
{
posY += velocity;
}
//Also, don't put else here, so you can go diagonally.
if (Keyboard.IsKeyDown(Keys.Left))
{
posX -= velocity;
}
else if (Keyboard.IsKeyDown(Keys.Right))
{
posX += velocity;
}
Box.Location = new Point((int)posX, (int)posY);
labelPosX.Text = posX.ToString(); //Testing purposes
labelPosY.Text = posY.ToString(); //Testing purposes
}
public static class Keyboard
{
private static readonly HashSet<Keys> keys = new HashSet<Keys>();
public static void OnKeyDown(object sender, KeyEventArgs e)
{
if (keys.Contains(e.KeyCode) == false)
{
keys.Add(e.KeyCode);
}
}
public static void OnKeyUp(object sender, KeyEventArgs e)
{
if (keys.Contains(e.KeyCode))
{
keys.Remove(e.KeyCode);
}
}
public static bool IsKeyDown(Keys key)
{
return keys.Contains(key);
}
}
And to use the Keyboard class, sets the Form1's KeyDown and KeyUp events in the InitializeComponent method.
KeyDown += Keyboard.OnKeyDown;
KeyUp += Keyboard.OnKeyUp;
You can control it's speed by changing the velocity.

WPF Timer + Show windows went wrong

I have the following code :
public partial class FereastraAlerta : UserControl
{
private static DispatcherTimer dispatcherTimer;
public FereastraAlerta()
{
InitializeComponent();
}
public void InitTimer()
{
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, Convert.ToInt32(textBox.Text), 0);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
CommandManager.InvalidateRequerySuggested();
Window.GetWindow(this).Show();
}
private void textBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key < Key.D0 || e.Key > Key.D9)
{
e.Handled = true;
}
}
private void btnOK_Click(object sender, RoutedEventArgs e)
{
Window.GetWindow(this).Close();
}
private void btnRemind_Click(object sender, RoutedEventArgs e)
{
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
if(Window.GetWindow(this).Title == "ANM")
config.AppSettings.Settings["nIntervalDeTimpReminderANM"].Value = textBox.Text;
else if (Window.GetWindow(this).Title == "CAS")
config.AppSettings.Settings["nIntervalDeTimpReminderCAS"].Value = textBox.Text;
else if (Window.GetWindow(this).Title == "MS")
config.AppSettings.Settings["nIntervalDeTimpReminderMS"].Value = textBox.Text;
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
Window.GetWindow(this).Hide();
InitTimer();
}
}
I open this window with the following :
if (data != strDataCurentaANM)
{
FereastraAlerta win1 = new FereastraAlerta();
Window window1 = new Window
{
Title = "ANM",
Content = win1,
Height = 300,
Width = 300
};
window1.Show();
win1.label.Content = textAvertizare + " " + data;
win1.textBox.Text = ConfigurationManager.AppSettings["nIntervalDeTimpReminderANM"];
strDataCurentaANM = data;
modifica = true;
}
This application check some websites and if the information changed it give me a warning, it have 3 warning windows at the moment and I got this problem:
I open 3 windows and I want them to remind me about the warning in 2 , 4 ,6 min, no problem until here, they will hide and will apear on the screen in that time. But if I try to modify the reminder again it will create another timer or something like this , I don't really understand the behavior.
Also the period that the window will apear it will be much shorter that the time inserted .https://i.stack.imgur.com/gkeu6.png
If I press ok the window should close and they does but the timer is somehow still alive and will try to show the window which I closed so it will generate this error https://i.stack.imgur.com/4dDzJ.png
Ok my guess is that whenever I hit the remind button it will create a new timer and the old one will still run(but this don't explain why sometimes all the windows will popup at once even if the reminder is not the same) and this is right:
What can I do in order to get rid of the old timer and let only the new one.
Also if you can give me any suggestions about how I should improve my code I'm listening .
If I press ok the window should close and they does but the timer is somehow still alive and will try to show the window which I closed so it will generate this error
Unsubscribe the dispatcherTimer_Tick and stop the timer before closing your window
private void btnOK_Click(object sender, RoutedEventArgs e)
{
if (dispatcherTimer != null)
{
dispatcherTimer.Tick -= dispatcherTimer_Tick;
dispatcherTimer.Stop();
}
Window.GetWindow(this).Close();
}
if I try to modify the reminder again it will create another timer
Same thing when you start a new timer
public void InitTimer()
{
if (dispatcherTimer != null)
{
dispatcherTimer.Tick -= dispatcherTimer_Tick;
dispatcherTimer.Stop();
}
Ok my guess is that whenever I hit the remind button it will create a
new timer and the old one will still run
Yes, definitely, each time you call InitTimer() you create a new timer. But also you didn't kill the old one, and it wont be garbage collected because it has an event subscription.
Thus each time it creates a new timer and they all tick whenever they should.
To avoid that, you have to be carefull with subscription in general. Whenever you write something like:
dispatcherTimer.Tick += dispatcherTimer_Tick;
You have to write also somewhere (usually in object cleanup):
dispatcherTimer.Tick -= dispatcherTimer_Tick;
Otherwise the event will still live. It is a really common error in C#, responsible for lots of memory leaks.
A quick workaround for you could be:
public void InitTimer()
{
if (dispatcherTimer != null)
{
dispatcherTimer.Tick -= dispatcherTimer_Tick;
}
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += dispatcherTimer_Tick;
dispatcherTimer.Interval = new TimeSpan(0, Convert.ToInt32(textBox.Text), 0);
dispatcherTimer.Start();
}
Also if you can give me any suggestions about how I should improve my
code I'm listening
To be honest, this code is pretty bad, not testable and with bad names. To improve you craft I can suggest you to learn about the MVVM pattern and to read Clean Code.
Hope it helps.
What can I do in order to get rid of the old timer and let only the new one?
Try to stop the old timer before you start a new one:
private void btnRemind_Click(object sender, RoutedEventArgs e)
{
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
if (Window.GetWindow(this).Title == "ANM")
config.AppSettings.Settings["nIntervalDeTimpReminderANM"].Value = textBox.Text;
else if (Window.GetWindow(this).Title == "CAS")
config.AppSettings.Settings["nIntervalDeTimpReminderCAS"].Value = textBox.Text;
else if (Window.GetWindow(this).Title == "MS")
config.AppSettings.Settings["nIntervalDeTimpReminderMS"].Value = textBox.Text;
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
Window.GetWindow(this).Hide();
//STOP the old timer here:
if (dispatcherTimer != null)
{
dispatcherTimer.Stop();
}
InitTimer();
}

WPF DispatcherTimer not stop working once I started it more than once

I want to make a timer to update the UI counter using DispatcherTimer class.
Here's my code:
private static DispatcherTimer timer;
private static int count;
//counter is TextBlock in WPF xaml
private void countdownBtn_Click(object sender, RoutedEventArgs e)
{
count = 3;
timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 0, 500);
timer.Tick += CountDown;
timer.Start();
}
private void CountDown(object sender, EventArgs e)
{
//if counter becomes 0, stop the counter
if (count <= 0)
{
counter.Visibility = Visibility.Hidden;
timer.Stop();
return;
}
if (counter.Visibility == Visibility.Hidden)
{
counter.Text = count.ToString();
counter.Visibility = Visibility.Visible;
}
else
{
count--;
counter.Visibility = Visibility.Hidden;
}
}
This code works fine if I click the button one time and wait it to complete its task for 3 seconds. But if I clicked the button 2 times in a row, it will continue following this:
If I click the button, the counter will be updated with more than 1 threads (make it visible and hidden faster than usual).
It will continue working even after timer.Stop() is executed ( it will enter loop CountDown -> if(count<=0) -> timer.Stop() -> return; -> CountDown -> if(count<=0) -> ... ).
And if I want to do something after the timer is stopped, where should I modify my code?
Every time you click the button a new DispatcherTimer is created with the previous ones still running.
You should stop and then dispose the old timer before create new ones.

C# - Make form semi-transparent while moving

Is there any way to make the form semi-transparent while it is being moved and then become opaque when it's not being moved anymore? I have tried the Form_Move event with no luck.
I'm stuck, any help?
The reason the form loads as semi-transparent is because the form has to be moved into the starting position, which triggers the Move event. You can overcome that by basing whether the opacity is set, on whether the form has fully loaded.
The ResizeEnd event fires after a form has finished moving, so something like this should work:
bool canMove = false;
private void Form1_Load(object sender, EventArgs e)
{
canMove = true;
}
private void Form1_Move(object sender, EventArgs e)
{
if (canMove)
{
this.Opacity = 0.5;
}
}
private void Form1_ResizeEnd(object sender, EventArgs e)
{
this.Opacity = 1;
}
To do it properly I expect you'd need to override the message processing to respond to the title bar being held, etc. But you could cheat, and just use a timer so that you make it opaque for a little while when moved, so continuous movement works:
[STAThread]
static void Main()
{
using (Form form = new Form())
using (Timer tmr = new Timer())
{
tmr.Interval = 500;
bool first = true;
tmr.Tick += delegate
{
tmr.Stop();
form.Opacity = 1;
};
form.Move += delegate
{
if (first) { first = false; return; }
tmr.Stop();
tmr.Start();
form.Opacity = 0.3;
};
Application.Run(form);
}
}
Obviously you could tweak this to fade in/out, etc - this is just to show the overall concept.

Categories