I am having some problems with using some of Dispatcher Timers on a WPF window.
On a window I would usually use a timer, but this function doesn't seem to be present on WPF forms so I was advised that DispatcherTimer was the equivalent.
So I have 3 of these timers:
The first one every 30 seconds brings the form forward - this one works correctly.
dispatcherTimer1.Tick += new EventHandler(dispatcherTimer1_Tick);
dispatcherTimer1.Interval = TimeSpan.FromSeconds(30);
dispatcherTimer1.Start();
private void dispatcherTimer1_Tick(object sender, EventArgs e)
{
this.Topmost.Equals(true);
this.Activate();
this.BringIntoView();
this.Focus();
this.Topmost.Equals(false);
}
The second one keeps checking every 100 milliseconds to see if IExplorer is running and if so hides the OK button and shows a message on the forms telling the user to close IExplorer - When you run the form if IE is running is will disable the button and show the message, but after you close IE it doesn't change it back.
What could i do to get the timer to constantly run and update the form if IE is opened or closed?
public Process[] aProc = Process.GetProcessesByName("IExplore");
dispatcherTimer2.Tick += new EventHandler(dispatcherTimer2_Tick);
dispatcherTimer2.Interval = TimeSpan.FromMilliseconds(100);
dispatcherTimer2.Start();
private void dispatcherTimer2_Tick(object sender, EventArgs e)
{
if (aProc.Length == 0)
{
richTextBox3.Visibility = System.Windows.Visibility.Hidden;
button1.Visibility = System.Windows.Visibility.Visible;
}
else
{
button1.Visibility = System.Windows.Visibility.Hidden;
richTextBox3.Visibility = System.Windows.Visibility.Visible;
}
}
And Thirdly, like the second timer runs every 100 milliseconds, once they have click on the OK button, I want to to kill the IExplorer process in the event that the user tries to invoke it, but again like the second timer is doesn't seem to be running constantly.
Any ideas?
dispatcherTimer3.Tick += new EventHandler(dispatcherTimer3_Tick);
dispatcherTimer3.Interval = TimeSpan.FromMilliseconds(100);
dispatcherTimer3.Start();
private void dispatcherTimer3_Tick(object sender, EventArgs e)
{
Process[] Processes = Process.GetProcessesByName("IExplore");
foreach (Process Proc1 in Processes)
{
Proc1.Kill();
}
}
if IE is running is will disable the button and show the message, but after you close IE it doesn't change it back. This is happening because you are not getting process in timer tick event. So change you code as shown below.
dispatcherTimer2.Tick += new EventHandler(dispatcherTimer2_Tick);
dispatcherTimer2.Interval = TimeSpan.FromMilliseconds(100);
dispatcherTimer2.Start();
private void dispatcherTimer2_Tick(object sender, EventArgs e)
{
Process[] aProc = Process.GetProcessesByName("IExplore");
if (aProc.Length == 0)
{
richTextBox3.Visibility = System.Windows.Visibility.Hidden;
button1.Visibility = System.Windows.Visibility.Visible;
}
else
{
button1.Visibility = System.Windows.Visibility.Hidden;
richTextBox3.Visibility = System.Windows.Visibility.Visible;
}
}
In the code snippet, you only get the list of the processes once and then check the same array every time. If it is the same in your real code, make sure you update the process list with each tick.
Related
I need to make a splash screen that is visible for 5000 milliseconds before closing. I pass in 5000 to my SplashScreen form constructor and set timer1.Interval = time. I can't seem to find a straightforward answer online and I don't have much experience with timers. I assume that I need to show the splash screen, start the timer, check for when timer1.Tick occurs, and close the form but I don't know the syntax on how to do that.
private void Form1_Load(object sender, EventArgs e)
{
SplashScreen splash = new SplashScreen(5000, appLogo, "Text Editor", "Copyright (c) 2020", "John Doe");
splash.timer1.Enabled = true;
splash.ShowDialog();
splash.timer1.Start();
// Wait for Tick event to occur.....
splash.Close();
}
In your SplashScreen form, you need to define the Tick event:
timer1.Tick += new EventHandler(CloseForm);
That calls a method to close the form:
private void CloseForm(Object source, EventArgs eventArgs)
{
this.Close();
}
The issue doing so is i have used tips and tricks here but the seems to run in a loop and wont give the results.
Basically i run a BackgroundWorker to hit a url get result and paste result to some Labels.
I have used Form_Activated but it just keeps on running in a loop and wont stop ever reached to the BackgroundWorker completed event .
MAIN CODE BLOCKS:
On Form_Load I Run the Function and get the results and show:
private void Form1_Load(object sender, EventArgs e)
{
pictureBox1.Show();
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
else
{
MessageBox.Show("Thread already running....");
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
loadData(); // scrape a URL and paste info to Labels ....
}
This is it, now the user will minimize the application , now whenever he hits the the taskbar icon the form should rerun the same as in Form_Load. I hope that make sense , i have been able to do that using Form_Activate but it keeps going on .
Any suggestion how to get it done ?
I would store a boolean to remember if the form was minimized at the last FormResized event, and then if it was and if the form isn't currently minimized then call your method.
private bool minimized = false;
public void FormResized(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
minimized = true;
}
if (minimized && this.WindowState != FormWindowState.Minimized)
{
minimized = false;
MyMethod();
}
}
can someone please let me know why the System.Windows.Forms.Timer continues to show multiple message boxes? I thought that it is on GUI thread ... and therefore after the first messagebox the GUI thread should block. But this is not the case
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
int nValue = 0;
void tmr_Tick(object sender, EventArgs e)
{
nValue++;
MessageBox.Show(nValue.ToString());
}
System.Windows.Forms.Timer tmr = new System.Windows.Forms.Timer();
private void btnStartTimer_Click(object sender, EventArgs e)
{
tmr.Interval = 500;
tmr.Enabled = true;
tmr.Tick += new EventHandler(tmr_Tick);
}
}
The MessageBox.Show() method includes (as all modal dialogs do) a message loop that continues to pump window messages.
Window messages are what allow a window to interact with the user (update itself, accept input, etc.), as well as what allows the Forms.Timer class to work.
If you want your Forms.Timer to stop ticking when the dialog is shown, you need to set the timer's Enabled property to false before you show the dialog.
In your Tick event stop the timer and then start again after MessageBox.Show like:
void tmr_Tick(object sender, EventArgs e)
{
tmr.Enabled = false;
nValue++;
MessageBox.Show(nValue.ToString());
tmr.Enabled = true;
}
The reason you are getting repeated MessgeBoxes is because your timer is continuing after showing the first MessageBox.
A message box does not block the GUI-Thread. It's as simple as that. You can interact with the message box, after all :)
Also: The internal workings of the timer are not clear, but I would guess that it runs on another thread and just returns on the GUI-Thread.
I have a main thread that is a form, that starts another application, in this case Notepad, then I spawn off a BackgroundWorker that waits for Notepad to be closed. When its closed, the BackgroundWorker shows another Form to display, topmost, to the user. This Form needs to be non-modal, so that the user can click on some buttons on the main thread dialog. The problem is this form (Form2, from the BackgroundWorker) is NOT TopMost, even though I set it to true. It works when I hit F5, but when I publish, as a ClickOnce application, to my server, form2 is no longer TopMost. I have tired Form2.Topmost = true, BringToFront, Activate, "MakeTopMost" from What is powerful way to force a form to bring front? .... nothing seems to work.
I even tried to get the handle of the main form, and use that as the parent of form2, but I'm getting "InvalidOperationException: Cross-thread operation not valid: Control 'Form2' accessed from a thread other than the thread it was created on."
Here is a code snippet:
public partial class Form1 : Form
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
private BackgroundWorker endApplicationBackgroundWorker= new BackgroundWorker();
public Form1(string[] args)
{
endApplicationBackgroundWorker.DoWork += new DoWorkEventHandler(endApplicationBackgroundWorker_DoWork);
p.StartInfo.FileName = "notepad";
p.Start();
endApplicationBackgroundWorker.RunWorkerAsync();
//Quit here so we can accept user inputs (button pushes ..)
}
private void endApplicationBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
p.WaitForExit();
Form2 form2 = new Form2();
form2.TopMost = true;
System.Diagnostics.Process[] procs = System.Diagnostics.Process.GetProcessesByName(form1ProcessName);
if (procs.Length != 0)
{
IntPtr hwnd = procs[0].MainWindowHandle;
if (form2.ShowDialog(new WindowWrapper(hwnd)) == DialogResult.OK)
{
// process stuff
}
}
this.Close();
}
}
Any other ideas? Or can someone fix my code above? I have been dealing with this issue for weeks now and getting flustered.
Thanks!
Any work you do in a BackgroundWorker's DoWork method after calling the RunWorkerAsync procedure is NOT running on the UI thread, but your code is creating a form in the background.
Forms are UI elements, so this won't work:
private void endApplicationBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Form2 form2 = new Form2();
form2.TopMost = true;
// etc..
if (form2.ShowDialog(new WindowWrapper(hwnd)) == DialogResult.OK)
{
// process stuff
}
}
From the comments, you should subscribe to the RunWorkerCompleted event to show your second form. Also, you can't call the Close method either since you are trying to keep Form2 alive without a ShowDialog call, so try subscribing to the Form_Closing() event of the second form to notify when the main form should be closed, too:
public Form1(string[] args)
{
endApplicationBackgroundWorker.DoWork +=
new DoWorkEventHandler(endApplicationBackgroundWorker_DoWork);
endApplicationBackgroundWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(endApplicationBackgroundWorker_RunWorkerCompleted);
p.StartInfo.FileName = "notepad";
endApplicationBackgroundWorker.RunWorkerAsync();
}
private void endApplicationBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
p.Start();
p.WaitForExit();
}
private void endApplicationBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Form2 form2 = new Form2();
form2.TopMost = true;
form2.FormClosing += new FormClosingEventHandler(form2_FormClosing);
form2.Show(this);
}
private void form2_FormClosing(object sender, FormClosingEventArgs e)
{
this.BeginInvoke(new MethodInvoker(delegate { this.Close(); }));
}
Another workaround can be, if you set the topmost to false and then set it back to true. It is strange, but it works. So the code could be the following for a form to be show with descending oppacity in a simple background thread:
for (int i = 0; i < 50; i++)
{
ppaForm.SetBitmap(bitmap, (byte)(255 - i * 5));
ppaForm.Show();
ppaForm.TopMost = false;
ppaForm.TopMost = true;
Thread.Sleep(6);
}
In this case ppaForm is a PerPixelAlphaForm, for which you can find a description here.
When I use System.Windows.Forms.Timer class and finish using it then I can't disable it.. it ticks even if I set its property Enabled to false. What is wrong with the code? here is an example:
int counter = 0;
private void timer1_Tick(object sender, EventArgs e) {
MessageBox.Show("Hello");
counter++;
if (counter == 10){
timer1.Enabled = false;
}
}
This is a subtle bug that's induced by the MessageBox.Show() call. MessageBox pumps a message loop to keep the UI alive. Which allows the Tick event handler to run again, even though it is already active from the previous tick. The counter variable doesn't get incremented until you click the OK button. As a result, the screen fills with message boxes and that won't stop until you click the OK button ten times.
You need to increment the counter before showing the message box. Fix:
int counter = 0;
private void timer1_Tick(object sender, EventArgs e) {
counter++;
if (counter > 10) timer1.Enabled = false;
else MessageBox.Show("Hello");
}
This kind of problem is also the reason that DoEvents() got such a bad reputation. It is pretty difficult to write code that can properly deal with the re-entrancy induced by the message loop. You need to keep boolean flags around that indicate that code is already active. Which is another way to solve your problem:
int counter = 0;
bool showingBox = false;
private void timer1_Tick(object sender, EventArgs e) {
if (showingBox) return;
showingBox = true;
try {
MessageBox.Show("Hello");
counter++;
if (counter == 10) timer1.Enabled = false;
}
finally {
showingBox = false;
}
}
You now get only one message box at a time, probably what you are really looking for.
I should mention that this re-entrancy problem is pretty specific to timers. A dialog takes counter-measures to avoid re-entrancy problems, it disables all the windows in the application. That ensures that the user cannot do things like closing the main window or clicking a button that brings up the dialog again. Both rather disastrous mishaps. That takes care of most of the 'unexpected' Windows notifications, basically any of the messages that are generated by the user. The edge case is a timer (WM_TIMER is not disabled) whose event handler has a UI effect.
It is because the MessageBox.Show blocks until the user presses OK.
The code below the MessageBox will not execute until after 10 OK buttons are pressed.
But the timer continues to fire even if the execution is blocked.
Try this code
int counter = 0;
private void timer1_Tick(object sender, EventArgs e) {
counter++;
if (counter == 10){
timer1.Enabled = false;
}
MessageBox.Show("Hello");
}
(just moved the MessageBox)
What about timer1.Stop()? I am not too familiar with this class, but looked it up quickly: Timer Class
try this:
int counter = 0;
private void timer1_Tick(object sender, EventArgs e) {
MessageBox.Show("Hello");
counter++;
if (counter == 10){
timer1.Stop();
timer1.Dispose();
timer1 = null;
}
}
It is also working for me. I placed the timer in the form, on the button click, I am calling timer1.start() and I put the following code in the tick event and its working.
int i = 0;
private void timer1_Tick(object sender, EventArgs e)
{
i++;
this.Text = i.ToString();
if (i == 10)
{
timer1.Enabled = false;
}
}
You need to call Stop.
int i = 0;
private void timer1_Tick(object sender, EventArgs e)
{
i++;
if (i == 10)
{
timer1.Stop();
}
}
I'm pretty sure the MessageBox is the culprit here. Maybe if you use a short execution interval for the timer handler then this could potentially cause your code to function undesirably, if executions are overlapping.
The problem would be, in this case, that the handler executes and displays the MessageBox which in turn halts execution of the current scope until the the prompt is acknowledged by the user, meanwhile the handler has started again, showing another prompt, and another, and so on. At this point, we have multiple MessageBoxes waiting for input, yet counter hasn't even been incremented once. When we click 'OK' on the prompt, counter increments as desired, but at this point has a value of 1, rather than a value representing the number of prompts shown. This means yet another prompt will be displayed if more time elapses, until the user clicks 'OK' on at least 10 prompts.
You could try inhibiting execution if the process is already under way in order to prevent concurrent runs:
private readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
int counter = 0;
private void timer1_Tick(object sender, EventArgs e)
{
if (Locker.TryEnterWriteLock(0))
{
try
{
MessageBox.Show("Hello");
Counter++;
if (Counter == 10)
{
Timer.Enabled = false;
}
}
catch { }
finally
{
Locker.ExitWriteLock();
}
}
}