My form contain two controls: button1 and timer1
timer1.Interval=1000; timer1.Enable=true;
While click button1, application on windows will start. Ex:notepad will show.
But timer1 is not running while notepad is showing.
How to timer1 so running ??.
My code:
private void button1_Click(object sender, EventArgs e)
{
Process pro = new Process();
pro.StartInfo.FileName = "notepad";
pro.StartInfo.Arguments = "";
pro.Start();
pro.WaitForExit();
}
private void timer1_Tick(object sender, EventArgs e)
{
DateTime dtime = DateTime.Now;
string date_time = dtime.ToString("dd/MM/yyyy HH:mm:ss");
textBox2.Text = date_time;
}
From Process.WaitForExit:
Instructs the Process component to wait indefinitely for the associated process to exit.
Your timer is trying to invoke timer1_Tick, but your UI Thread is currently stuck waiting for the process to exit, which it wont.
You have two choices to work around this:
Simply remove the call to WaitForExit if you dont really need to wait
If you do need to be notified when the process exits, set Process.EnableRaisingEvents to true and register to the Process.Exited event
The WaitForExit() is "blocking" your interface from refreshing,the call just waits there for the process to exit. As an alternative if you need to do something when the process as exited do this:
private void button1_Click(object sender, EventArgs e)
{
Process pro = new Process();
pro.StartInfo.FileName = "notepad";
pro.StartInfo.Arguments = "";
//if you need to do something when the process exits do this:
pro.EnableRaisingEvents = true;
pro.Exited += new EventHandler(pro_Exited);
pro.Start();
//pro.WaitForExit();
}
void pro_Exited(object sender, EventArgs e)
{
//do what you need here...
}
Instead you could start the process with a BackGroundWorker.
pro.WaitForExit(); makes UI thread to freeze so it can't update.
To stop user from actions, you can disable some controls, while process is running. You can subscribe to process.Exited event and enable your controls, when user closes the process.
private void button1_Click(object sender, EventArgs e)
{
Process pro = new Process();
pro.StartInfo.FileName = "notepad";
pro.StartInfo.Arguments = "";
pro.Start();
button1.Enabled = false;
pro.EnableRaisingEvents = true;
pro.Exited += pro_Exited;
}
void pro_Exited(object sender, EventArgs e)
{
button1.Invoke((MethodInvoker) delegate { button1.Enabled = true; });
}
Update
As another answer suggested you should set EnableRaisingEvents property to true.
Also pro_Exited method will run in a different thread, so you need to use Control.Invoke method to change UI.
Update 2
If can't delete pro.WaitForExit(); you can use another timer, because System.Windows.Forms.Timer is running in UI thread and is blocked with it.
private System.Threading.Timer timer = new System.Threading.Timer(Callback);
public Form()
{
InitializeComponent();
timer.Change(0, 1000);
}
private void Callback(object state)
{
DateTime dtime = DateTime.Now;
string date_time = dtime.ToString("dd/MM/yyyy HH:mm:ss");
button1.Invoke((MethodInvoker)delegate { textBox1.Text = date_time; });
}
It will not update the textBox, when process is opened, but the timer will run and can do some work.
Update 3
In case of multiple processes you can count them and check number of active processes in pro_Exited method.
private volatile int activeProcessCount = 0;
private void pro_Exited(object sender, EventArgs e)
{
activeProcessCount--;
if (activeProcessCount == 0)
{
button1.Invoke((MethodInvoker) delegate { button1.Enabled = true; });
}
}
private void button1_Click(object sender, EventArgs e)
{
//code
activeProcessCount = 2;
pro1.Start();
pro2.Start();
}
Related
private void button1_Click_1(object sender, EventArgs e)
{
lbl_startingTest.Text = "Flashing DUT..";
Process fls1 = new Process();
fls1.StartInfo.UseShellExecute = false;
//fls1.StartInfo.RedirectStandardOutput = true;
fls1.StartInfo.FileName = "C:\\test\\test\\bin\\Debug\\flash.bat";
fls1.StartInfo.CreateNoWindow = true;
fls1.Start();
//
fls1.WaitForExit();
}
Tried different methods, but no result achieved. Please suggest. I am trying to update the label "Flashing DUT..." to "Flashing Complete" when fls1 process completes execution.
fsl1 process is a Batch file named flash.bat
How can I say if a winform whas closed do ...?
bool isRunning = false;
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Contains("Notepad"))
{
isRunning = true;
break;
}
}
The code above always checks if the process exists but the code is slow for what I want it to do.So is there a way to check if the Notepad process was actually closed instead of always looping to see if its there?
You can use Win32_ProcessStopTrace which indicates that a process is terminated.
ManagementEventWatcher watcher;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
watcher = new ManagementEventWatcher("Select * From Win32_ProcessStopTrace");
watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
watcher.Start();
}
void watcher_EventArrived(object sender, EventArrivedEventArgs e)
{
if ((string)e.NewEvent["ProcessName"] == "notepad.exe")
MessageBox.Show("Notepad closed");
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
watcher.Stop();
watcher.Dispose();
base.OnFormClosed(e);
}
Don't forget to add a reference to System.Management and add using System.Management;
Note
If you want to monitor closing of an specific instance of notepad which you know, you can use such criteria:
if ((UInt32)e.NewEvent["ProcessID"]==knownProcessId)
If you want to check if any instance of notepad is open, you can use such criteria:
if (System.Diagnostics.Process.GetProcessesByName("notepad").Any())
The EventArrived will raise in a different thread than UI thread and if you need to manipulate UI, you need to use Invoke.
Above method notifies you about closing of all processes, regardless of the time they are opened, before or after your application run. If you don't want to notified about the processes which may be opened after your application starts, you can get existing notepad processes and subscribe to their Exited event:
private void Form1_Load(object sender, EventArgs e)
{
System.Diagnostics.Process.GetProcessesByName("notepad").ToList()
.ForEach(p => {
p.EnableRaisingEvents = true;
p.Exited += p_Exited;
});
}
void p_Exited(object sender, EventArgs e)
{
MessageBox.Show("Notepad closed");
}
This should do the trick. It will create a event for you when the process dies. No need to loop through all the process.
public static event EventHandler ProcessDied;
public void CheckForProcess()
{
InitializeComponent();
ProcessDied += new EventHandler(Process_Died);
AttachProcessDiedEvent("notepad", ProcessDied);
}
private void AttachProcessDiedEvent( string processName,EventHandler e )
{
Process isSelectedProcess=null;
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Contains(processName))
{
isSelectedProcess = clsProcess;
break;
}
}
if(isSelectedProcess!=null)
{
isSelectedProcess.WaitForExit();
}
if(e!=null)
{
e.Invoke(processName, new EventArgs());
}
}
private void Process_Died(object sender, EventArgs e)
{
//Do Your work
}
Let me know if there are any issues.
you can do it without looping but dont know if its much faster :
bool isRunning = Process.GetProcessesByName("NotePad").FirstOrDefault() != null;
or
bool isRunning = Process.GetProcessesByName("notepad").Any();
I got this from here Check if a specific exe file is running
So my main form has a button that I enable when this really long copy process is complete. This copy process happens every 10 min and checks for updates etc. I'm stuck on how to get the process to notify the mainform that it's finished copying. Here's what I have so far:
public partial class mainForm : Form
{
....//initialize some stuff
private void TimerEventProcessor(object sender, EventArgs e)
{
....
copy.GetNewCopy();
}
}
class Copy
{
private bool IsCopyComplete;
....
public void GetNewCopy()
{
Process proc = new Process();
IsCopyComplete = false;
proc.EnableRaisingEvents = true;
proc.Exited += new EventHandler(myProcess_Exited);
proc.Start();
}
private void myProcess_Exited(object sender, System.EventArgs e)
{
IsCopyComplete = true;
// how to trigger mainform that process is complete?
}
}
You can call Invoke:
private void myProcess_Exited(object sender, System.EventArgs e)
{
IsCopyComplete = true;
button1.Invoke(new Action(() => button1.Enabled = true));
}
Here's an article on making thread-safe calls to the UI.
There are several ways to create a thread. My favorite, due to its built-in functionality for updating the UI, is the BackgroundWorker Thread. (Though it's not the right tool for every situation).
You could pass a reference of the form to the class, though that usually seems wrong somehow. The class shouldn't be aware of your form's UI components.
What I'd do is create an EventHandler that the Exited event can publish to. (You can attach to multiple events, so if you still need to set IsCopyComplete = true, then just leave that event too.) When the Exited event fires, it'll call the "ProcessExited" EventHandler without knowing whether anything else subscribed to it.
class Copy
{
public event EventHandler ProcessExited;
private bool IsCopyComplete;
....
public void GetNewCopy()
{
Process proc = new Process();
IsCopyComplete = false;
proc.EnableRaisingEvents = true;
proc.Exited += ProcessExited;
proc.Exited += new EventHandler(myProcess_Exited);
proc.StartInfo = new ProcessStartInfo("cmd.exe"); // specify your process - replace cmd.exe with whatever's appropriate
proc.Start();
}
private void myProcess_Exited(object sender, System.EventArgs e)
{
IsCopyComplete = true;
}
}
Then in your main form, you can subscribe to the event.
public partial class mainForm : Form
{
....//initialize some stuff
public class mainForm()
{
...
// not sure where you're instantiating `copy` - you may have to move this
copy.ProcessExited += (s, a) =>
button1.Invoke(new Action(() => button1.Enabled = true));
...
}
private void TimerEventProcessor(object sender, EventArgs e)
{
copy.GetNewCopy();
}
}
There are many ways to do this. Here is one
Declare the Handler method in your Mainform and then sunscribe as below
proc.Exited += new EventHandler(MainForm1.myProcess_Exited);
This case is using C# WPF. I want to instantly disable a button after clicking it to prevent clicking it twice in short succession. I disabled the button in OnClick_Event but still clickable.
Part of source is as below.
private void Button_Click_UpdateBurndownChart(object sender, RoutedEventArgs e)
{
if(threadNotWorking)
{
updateButton.IsEnabled = false;
startWorkThread();
}
}
private void startWorkThread()
{
... ...
//after finish required process
updateButton.IsEnabled = true;
}
Is there any way to accomplish this?
you may want to use a dispatcher, there is probably a threading problem (callback function running on seperate thread and trying to access ui which runs on another thread). try this . .
updateButton.Dispatcher.BeginInvoke(
new ThreadStart(() => updateButton.IsEnabled = false),
System.Windows.Threading.DispatcherPriority.Input, null);
instead of
updateButton.IsEnabled = false;
What happens if you were instead to change the order of your events from:
updateButton.IsEnabled = false;
startWorkThread();
To
startWorkThread();
updateButton.IsEnabled = false;
Let me know how this goes.
What it looks like is that you are starting your thread then immediatly enabling your button before your thread has finished. You would be better off using a BackgroundWorker and enable your Button in the RunWorkerCompleted Event. Though you can do something similar by enabling your button using a BeginInvoke at the end of your Process.
public void doWork()
{
System.Threading.Thread.Sleep(10000); //Simulating your Process
Dispatcher.BeginInvoke(new System.Threading.ThreadStart(delegate() { updateButton.IsEnabled = true; }), System.Windows.Threading.DispatcherPriority.Background);
}
Example with BackgroundWorker
using System.ComponentModel;
public partial class MainWindow : Window
{
BackgroundWorker bgw;
public MainWindow()
{
InitializeComponent();
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
updateButton.IsEnabled = true;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(10000); //Simulating your work
}
private void startWorkThread()
{
bgw.RunWorkerAsync();
}
private void updateButton_Click(object sender, RoutedEventArgs e)
{
if (bgw.IsBusy != true)
{
updateButton.IsEnabled = false;
startWorkThread();
}
}
}
I have windows application in which i need to save data into database and after saving data load crystal report to show report,all this happens on click of save button.
I have button named btn_Submit on click of this data is saved and display report, while saving it takes time so i want to show progress bar for mean time so that user get known that data is in process.how i can do with this windows application.
I gone through this link http://www.codeproject.com/Tips/83317/BackgroundWorker-and-ProgressBar-demo but don't get it exactly I want.
I am aware of background worker but never used it.
I have used background worker and progress bar as given in above link but progress bar does not stop at all once it started.
Can any one help me?can u give any example or link that demonstrate scenario?.
This code i added on Dowork();
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
PrintData arg = (PrintData)e.Argument;
SalesMaster sm = arg.SalesData;
BrokerMaster bm = arg.Broker;
CustomerMaster ctm = arg.Customer;
CompanyMaster cm = arg.Company;
ArrayList hb = arg.Arrardata;
int totunit = arg.totunit;
decimal globalamt = arg.golbamt;
SalesReport sreport = new SalesReport(sm, ctm, cm, bm, hb, totunit, glb_totalamt);
sreport .MdiParent = arg.parentf;
sreport .WindowState = FormWindowState.Maximized;
sreport .Show();
}
i get error on this line sreport .MdiParent = arg.parentf;
This error:
Cross-thread operation not valid: Control 'frmParent' accessed from a thread other than the thread it was created on.
what should be done here?
Suscribe to DoWork and RunWorkerCompleted events and
void btn_Submit_Click(object sender, EventArgs e)
{
btn_Submit.Enabled = false; // disable button while saving report
lbl_Status.Text = "Please wait..";
backgroundWorker.RunWorkerAsync();
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// save report here
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btn_Submit.Enabled = true; // enable button
lbl_Status.Text = "Report saved";
}
Instead of using label, you can show PictureBox with spinner wait image. I don't like to see progress bar, which does not show percentage of task - I expect that when progress bar will be filled, task will be completed. If you really want to use progress bar, then, I'd go with timer component (set timer's interval to desired refresh rate):
void btn_Submit_Click(object sender, EventArgs e)
{
btn_Submit.Enabled = false; // disable button while saving report
timer.Start();
progressBar.Visible = true;
// backgroundWorker.RunWorkerAsync(new object[] { "Foo", 42 });
// backgroundWorker.RunWorkerAsync(new CustomType("Foo", 42));
backgroundWorker.RunWorkerAsync(new { Foo = "Foo", Bar = 42 }););
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// object[] args = (object[])e.Argument;
// CustomType arg = (CustomType)e.Argument;
dynamic arg = (dynamic)e.Argument;
string foo = arg.Foo;
int bar = arg.Bar;
// save report here
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btn_Submit.Enabled = true; // enable button
timer.Stop();
progressBar.Visible = false;
}
void timer_Tick(object sender, EventArgs e)
{
if (progressBar.Value == progressBar.Maximum)
{
progressBar.Value = progressBar.Minimum;
return;
}
progressBar.PerformStep();
}
Try to reset the ProgressBar's Value property to 0, like in the code below:
Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker_RunWorkerCompleted);
Implemented event handler:
void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Value = 0;
}
To stop or hide the progressbar use background worker's completed event.