I made a panel with several buttons in WPF.
When mouse enter in some button, new buttons appear and will disappear 1000 ms after mouse leave.
But I have a strange behavior: 1000 ms in some case become shorter and shorter after each use.
Timer event
private void TimerEventProcessorForButtonA(Object myObject, EventArgs myEventArgs)
{
_myTimerForButtonA.Stop();
miniButton1.Visibility = System.Windows.Visibility.Hidden;
}
private void TimerEventProcessorForButtonB(Object myObject, EventArgs myEventArgs)
{
_myTimerForButtonB.Stop();
miniButton2.Visibility = System.Windows.Visibility.Hidden;
}
WaitTime functions calling timer:
public void WaitThisTimeAndHideMiniButton1(int givenTime)
{
_myTimerForButtonA = new System.Windows.Forms.Timer();
_myTimerButtonA.Tick += new EventHandler(TimerEventProcessorForForButtonA);
_myTimerForForButtonA.Interval = givenTime;
_myTimerForForButtonA.Start();
}
public void WaitThisTimeAndHideMiniButton2(int givenTime)
{
_myTimerForButtonB = new System.Windows.Forms.Timer();
_myTimerForButtonB.Tick += new EventHandler(TimerEventProcessorForForButtonB);
_myTimerForForButtonB.Interval = givenTime;
_myTimerForForButtonB.Start();
}
Event when leave button:
private void buttonA_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
Border button = sender as Border;
button.Background = (SolidColorBrush)new BrushConverter().ConvertFromString(_colorOut);
WaitThisTimeAndHideMiniButton1(1000); // hide minibuttons
}
private void buttonB_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
Border button = sender as Border;
button.Background = (SolidColorBrush) new BrushConverter().ConvertFromString(_colorOut);
WaitThisTimeAndHideMiniButton2(1000);
}
As described in code, I have buttonA and buttonB. When I enter buttonA, miniButton1 appear - it will disappear 1000 ms after mouse leave event.
Same thing for buttonB that reveal miniButton2.
If I only enter and leave buttonB everything is correct. Same as ButtonA.
The problem: If I enter/leave buttonA and B, then, these 1000ms go shorter and shorter. Disappearance of miniButton also appear before I leave buttonA and B. also happen before I leave the button.
Eveything behave like if timers were confusing each other.
Do you know how to solve it?
You're recreating a new Timer every time a MouseLeave event is fired, if you happen to fire that event twice within 1000ms, you'll be recreating a new Timer before the Tick event of the previous Timer execute, which will make the previous Timer run indefinitely.
Here's what's happening when you fire MouseLeave event twice within 1000ms :
1) MouseLeave: _myTimerForButtonA is assigned a new Timer instance (ie: Timer1), Tick event registered.
2) MouseLeave: _myTimerForButtonA is assigned a new Timer instance (ie: Timer2), Tick event registered.
3) Timer1.Tick event fires TimerEventProcessorForButtonA, whichs stops _myTimerForButtonA which points to Timer2.
4) Timer2.Tick event fires TimerEventProcessorForButtonA, whichs stops _myTimerForButtonA which points to Timer2 (and is already stopped).
5) Timer1.Tick event fires TimerEventProcessorForButtonA indefinitely, because Timer1 is not referenced anymore and no one will ever call `Stop` on it.
I was able to fix your code by stopping the timer on MouseEnter event
private void TimerEventProcessorForButtonA(Object myObject, EventArgs myEventArgs)
{
Debug.WriteLine("TimerEventProcessorForButtonA");
_myTimerForButtonA.Stop();
_myTimerForButtonA.Dispose();
miniButton1.Visibility = System.Windows.Visibility.Hidden;
}
private void TimerEventProcessorForButtonB(Object myObject, EventArgs myEventArgs)
{
Debug.WriteLine("** TimerEventProcessorForButtonB");
_myTimerForButtonB.Stop();
_myTimerForButtonB.Dispose();
miniButton2.Visibility = System.Windows.Visibility.Hidden;
}
public void WaitThisTimeAndHideMiniButton1(int givenTime)
{
_myTimerForButtonA = new System.Windows.Forms.Timer();
_myTimerForButtonA.Tick += new EventHandler(TimerEventProcessorForButtonA);
_myTimerForButtonA.Interval = givenTime;
_myTimerForButtonA.Start();
}
public void WaitThisTimeAndHideMiniButton2(int givenTime)
{
_myTimerForButtonB = new System.Windows.Forms.Timer();
_myTimerForButtonB.Tick += new EventHandler(TimerEventProcessorForButtonB);
_myTimerForButtonB.Interval = givenTime;
_myTimerForButtonB.Start();
}
private void buttonA_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
Border button = sender as Border;
button.Background = (SolidColorBrush)new BrushConverter().ConvertFromString(_colorOut);
WaitThisTimeAndHideMiniButton1(1000); // hide minibuttons
}
private void buttonB_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
Border button = sender as Border;
button.Background = (SolidColorBrush)new BrushConverter().ConvertFromString(_colorOut);
WaitThisTimeAndHideMiniButton2(1000);
}
private void buttonA_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
if (_myTimerForButtonA?.Enabled == true)
_myTimerForButtonA.Stop();
miniButton1.Visibility = System.Windows.Visibility.Visible;
}
private void buttonB_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
if (_myTimerForButtonB?.Enabled == true)
_myTimerForButtonB.Stop();
miniButton2.Visibility = System.Windows.Visibility.Visible;
}
As stated in comments of your post, you shouldn't create a new Timer each time, and you should dispose it once you're done. Also, referencing Forms in a WPF project is a bad idea unless really necessary, which rarely is. You'll probably want to read about Storyboard for UI animation.
So basically this happens on mouse leave:
creating the timer
starting the timer
And this happens when timer expires:
stopping the timer
So if mouse repeatedly enters and leaves a button, it keeps creating and starting new timers. Which causes more than one timer to be running at the same time.
public void WaitThisTimeAndHideMiniButton1(int givenTime)
{
if(_myTimerForButtonA != null && _myTimerForButtonA.Enabled)
{
_myTimerForButtonA.Stop();
}
_myTimerForButtonA = new System.Windows.Forms.Timer();
_myTimerButtonA.Tick += new EventHandler(TimerEventProcessorForForButtonA);
_myTimerForForButtonA.Interval = givenTime;
_myTimerForForButtonA.Start();
}
Although there are more issues here as mentioned in comments, this should fix that problem.
Related
I believe I have a misunderstanding about either how a lock works or how the System.Windows.Forms.Timer works in C#.
So I made a simple Windows Forms Application (.NET Framework) and I added a Timer and a Button to the Form from the Toolbox. The Button starts the Timer when clicked, and the Timer enters a lock on a dummy object and blocks it on the Tick event. For the Button's Click event I have the following method:
private void button1_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
}
And for the Timer's Tick event I have this method:
readonly object lockObj = new object();
private void timer1_Tick(object sender, EventArgs e)
{
lock (lockObj)
{
MessageBox.Show("Entered the lock!");
MessageBox.Show("Exiting the lock...");
}
}
Everything else is left to default and there is no additional code.
I expected this program to show a single MessageBox with the text "Entered the lock!", then after I close it and also the following one with the message "Exiting the lock..." I thought the lock would be released and a queued up Tick event if any would acquire the lock, the process reapeating. Instead, the "Entered the lock!" MessageBox keeps opening multiple times without having to close it, as if every Tick event call enters the lock even if nobody releases it.
I tried to replicate this in a Console Application but with no luck. I'd appreciate a hint about what causes this problem so I know where to look into it.
Alternative code you can test in a Windows Forms Application:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Lock_Test_2
{
public partial class Form1 : Form
{
Timer timer1;
readonly object lockObj = new object();
public Form1()
{
InitializeComponent();
Button button1 = new Button();
button1.Location = new Point(100, 100);
button1.Size = new Size(187, 67);
button1.Text = "button1";
button1.Click += button1_Click;
Controls.Add(button1);
timer1 = new Timer();
timer1.Tick += timer1_Tick;
}
private void button1_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
lock (lockObj)
{
MessageBox.Show("Entered the lock!");
MessageBox.Show("Exiting the lock...");
}
}
}
}
System.Windows.Forms.Timer dispatches its events via a windows message loop.
MessageBox.Show shows a messagebox and then pumps the windows message loop as a nested loop. This can include dispatching more events for a timer.
Since only a single thread (the UI thread) is involved, and lock is reentrant, that's why you get multiple message boxes shown.
Here's my code
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new ElapsedEventHandler(DisplayTimeEvent);
myTimer.Interval = Convert.ToInt32(textBox5.Text);
if (checkBox1.Checked)
{
myTimer.Start();
}
else
{
myTimer.Stop();
}
}
It should stop repeating the function when unchecked, but it doesn't. What's wrong with it?
I recommend that you stop using System.Timers.Timer and start using a System.Windows.Forms.Timer component.
Begin by removing your myTimer-related code (the entire body of checkBox1_CheckedChanged will need to be replaced with code from below.)
Add a Timer component to your form using the designer and name it myTimer. This will add a System.Windows.Forms.Timer field to your form called myTimer.
Using the designer, set the Tick event handler of myTimer to DisplayTimeEvent. (Or add a new handler and replace its code with the code of your DisplayTimeEvent function.)
Then change your checkBox1_CheckedChange function to look like this:
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (int.TryParse(textBox5.Text, out int interval)) {
this.myTimer.Interval = interval;
}
this.myTimer.Enabled = checkBox1.Checked;
this.textBox5.Enabled = !checkBox1.Checked;
}
I also recommend adding the following handler to textBox5 to perform the bare minimum validation so you can't crash your app by entering an interval of 0 or the empty string, or some text that is not an integer.
private void textBox5_TextChanged(object sender, EventArgs e)
{
this.checkBox1.Enabled = (int.TryParse(textBox5.Text, out int interval) && interval > 0);
}
The System.Windows.Forms.Timer's Tick handler will be called in the UI thread, meaning it will be safe to do things like update labels of your form in that handler. In contrast to that, the System.Timers.Timer will be called on a worker thread and will require that you take on some some thread-management responsibilities you likely don't want to incur, such as invoking your UI updates back to the main UI thread. See Why there are 5 Versions of Timer Classes in .NET? for more info.
Everytime checkbox1 is changed, new Timer is created. When checkbox is ticked, created timer is active and will invoke DisplayTimeEvent forever. When checkbox is unticked, you stop another Timer, which was just created.
You need to create Timer only once (probably when form is created), or when checkbox1 is changed first time:
private System.Timers.Timer myTimer;
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (myTimer == null) {
myTimer = new System.Timers.Timer();
myTimer.Elapsed += new ElapsedEventHandler(DisplayTimeEvent);
myTimer.Interval = Convert.ToInt32(textBox5.Text);
}
if (checkBox1.Checked)
{
myTimer.Start();
}
else
{
myTimer.Stop();
}
}
I am working on antivirus program and on real-time protection panel I want checkbox when for example "Malware protection" checkbox is unchecked to make it not enable for like 15 minutes and after that time it is enabled again so it prevents spam.
If somebody can help me it would be great
I tried with Thread.Sleep() but it stops whole application, and I tried with timer but I think I did it wrong.
This is code for timer
private void checkBox1_CheckStateChanged(object sender, EventArgs e)
{
if (this.checkBox1.Checked)
{
this.checkBox1.Text = "On";
// these two pictureboxes are for "You are (not) protected"
// picture
picturebox1.Show();
pictureBox5.Hide();
timer1.Stop();
}
else
{
this.checkBox1.Text = "Off";
// this is the problem
timer1.Start();
this.checkBox1.Enabled = true;
pictureBox1.Hide();
pictureBox5.Show();
}
}
private void timer1_Tick(object sender, EventArgs e)
{
this.checkBox1.Enabled = false;
}
Short Answer
From the code you posted, it really only appears that you need to change the code to disable the checkbox in the CheckChanged event and enable it in the timer1_Tick event (and also Stop the timer in the Tick event).
Full Answer
Winforms has a Timer control that you can use for this. After you drop a Timer onto the designer, set the Interval property to the number of milliseconds you want to wait before enabling the checkbox (1 second is 1000 milliseconds, so 15 minutes is 15min * 60sec/min * 1000ms/sec, or 900,000 ms). Then double-click it to create the Tick event handler (or add one in your Form_Load event as I've done below).
Next, in the CheckChanged event, if the checkbox is not checked, disable the checkbox and start the timer.
Then, in the Tick event, simply enable the checkbox (remember, this event is triggered after Interval milliseconds have passed) and stop the timer.
For example:
private void Form1_Load(object sender, EventArgs e)
{
// These could also be done in through designer & property window instead
timer1.Tick += timer1_Tick; // Hook up the Tick event
timer1.Interval = (int) TimeSpan.FromMinutes(15).TotalMilliseconds; // Set the Interval
}
private void timer1_Tick(object sender, EventArgs e)
{
// When the Interval amount of time has elapsed, enable the checkbox and stop the timer
checkBox1.Enabled = true;
timer1.Stop();
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (!checkBox1.Checked)
{
// When the checkbox is unchecked, disable it and start the timer
checkBox1.Enabled = false;
timer1.Start();
}
}
This can be done without using Timer explicitly. Instead use asynchronous Task.Delay, which will simplify the code and make it easy to understand actual/domain intentions.
// Create extension method for better readability
public class ControlExtensions
{
public static Task DisableForSeconds(int seconds)
{
control.Enabled = false;
await Task.Delay(seconds * 1000);
control.Enabled = true;
}
}
private void checkBox1_CheckStateChanged(object sender, EventArgs e)
{
var checkbox = (CheckBox)sender;
if (checkbox.Checked)
{
checkbox.Text = "On";
picturebox1.Show();
pictureBox5.Hide();
}
else
{
checkbox.Text = "Off";
checkbox.DisableForSeconds(15 * 60);
pictureBox1.Hide();
pictureBox5.Show();
}
}
You could diseable and enable it with task.Delay(). ContinueWith(). This creates a new thread that fires after the delay is done. You need to make it thread safe, winforms isnt thread safe on its own
You should use Timer.SynchronizationObject
After a button is clicked in a Windows form application written in C#, how to wait for another button to be clicked? Meanwhile I am updating a datagridview dynamically by current information.
EDIT
After button1 is clicked, I want to repeatedly update a dataGridView with current information and when button2 is clicked I want to stop updating the dataGridView.
Use Timer Class.
public partial class Form1 : Form
{
System.Windows.Forms.Timer timer;
public Form1()
{
InitializeComponent();
//create it
timer = new Timer();
// set the interval, so it'll fire every 1 sec. (1000 ms)
timer.Interval = 1000;
// bind an event handler
timer.Tick += new EventHandler(timer_Tick);
//...
}
void timer_Tick(object sender, EventArgs e)
{
//do what you need
}
private void button1_Click(object sender, EventArgs e)
{
timer.Start(); //start the timer
// switch buttons
button1.Enabled = false;
button2.Enabled = true;
}
private void button2_Click(object sender, EventArgs e)
{
timer.Stop(); //stop the timer
// switch buttons back
button1.Enabled = true;
button2.Enabled = false;
}
From MSDN:
A Timer is used to raise an event at user-defined intervals. This
Windows timer is designed for a single-threaded environment where UI
threads are used to perform processing. It requires that the user code
have a UI message pump available and always operate from the same
thread, or marshal the call onto another thread.
When you use this timer, use the Tick event to perform a polling
operation or to display a splash screen for a specified period of
time. Whenever the Enabled property is set to true and the Interval
property is greater than zero, the Tick event is raised at intervals
based on the Interval property setting.
So you have button A and button B. When button A is pressed you want to wait for button B to be pressed, then do something special? Without more information the simplest way is something like this:
private void OnButtonAClicked(object sender, EventArgs e)
{
ButtonA.Enabled = false;
ButtonA.Click -= OnButtonAClicked;
ButtonB.Click += OnButtonBClicked;
ButtonB.Enabled = true;
}
private void OnButtonBClicked(object sender, EventArgs e)
{
ButtonB.Enabled = false;
ButtonB.Click -= OnButtonBClicked;
// Do something truly special here
ButtonA.Click += OnButtonAClicked;
ButtonA.Enabled = true;
}
This code will toggle(initial state; button A is enabled, button B is disabled), when button A is pressed, button B becomes enabled and processes events, etc.
Use the BackgroundWorker http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
It doesn't freeze the UI, support ProgressBar, also it can be async. At the link you will see a good example with same functional that you want (start some task by click on one button and cancel it by click on another button).
I am developing an application where i need the following requirement:
Suppose i have a button and a label (initially visibility set to false) in my form, and user clicks on the button, then the label should be displayed with some text that i assign to the label in the button click. But this label should be displayed only for some time, say some 3 secs and then it should automatically go invisible. For this if i gave:
private void button1_Click(object sender, EventArgs e)
{
label1.Visible=true;
label1.Text= "Magic";
Thread.Sleep(3000);
label1.Visible=false;
}
This code does not help the purpose. What is the approach to do it?
Try replacing the last two lines of your method with this:
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Interval = 3000;
timer.Tick += (source, e) => {label1.Visible = false; timer.Stop();};
timer.Start();
Using Thread.Sleep() is never a good idea in WinForms; use timers instead.
Create a timer with 3s as Interval, start it, and set Visible to false in your timer Tick event. Also don't forget to stop the timer.
Never call Thread.Sleep in your UI thread: the application will be blocked as the UI thread won't accept messages (user click...) while sleeping.
// create a global _timer object
static Timer _timer; // From System.Timers
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Magic";
// Thread.Sleep(3000); // don't do Thread.Sleep()!
label1.Visible = false;
Start();
}
static void Start()
{
label1.Visible = true;
_timer = new Timer(3000); // Set up the timer for 3 seconds
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
_timer.Enabled = true; // Enable it
}
static void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
label1.Visible = false;
_timer.Stop();
}
Use Timer, perhaps Windows.Forms.Timer. You may have to play with enabling and disabling it when needed and not needed. Your current way does not help, coz your making the main UI thread to sleep. No use.