I'm having a problem when I' trying to show a progress bar until a external process ends which I started for the WPF window.
The code is like this:
private void Button1_Click(object sender, RoutedEventArgs e)
{
Button1.IsEnabled = false;
Button1.Content = "Please Wait";
ProgressBar1.Visibility = Visibility.Visible;
if (a == 1 && b == 0)
{
var processStartInfo = new ProcessStartInfo(#"External Process Path 1");
processStartInfo.Verb = "runas";
try
{
Process.Start(processStartInfo);
}
catch (Win32Exception ex)
{
MessageBox.Show(ex.ToString(), "Run As",
MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
if (b == 1 && a == 0)
{
var processStartInfo = new ProcessStartInfo(#"External Process Patch 2");
processStartInfo.Verb = "runas";
try
{
Process.Start(processStartInfo);
}
catch (Win32Exception ex)
{
MessageBox.Show(ex.ToString(), "Run As",
MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
Button2.IsEnabled = true;
ProgressBar1.Visibility = Visibility.Hidden; //This is what I want to toggle after process ends
}
I've tried Thread.Sleep(time) method and also for-loop but nothing seems to work.
I'm very new in WPF. So, please try to be a little brief.
Thanks,
D.K.
do you know how long the external process lasts? if you don't you can try to set the IsIndeterminate property to true on your progress bar. this will show a continuous animation. When your process returns you can set it to false again in order to stop the animation.
also, in your code, i think you're not waiting for the process to finish. you could do this by using the code below.
Process p = Process.Start("IExplore");
p.WaitForExit();
please note that WaitForExit() blocks the current thread. as a result the app will stop responding. to keep the UI responsive you might like to start your process on a different thread like below.
private void onClick_Handler(object sender, EventArgs e) {
//disable button here
Task.Factory.StartNew(() => {
Process p = Process.Start("IExplore");
p.WaitForExit();
//enable button here. make sure to do this on the UI thread
//since you're doing this in the code-behind you should have access
//to the dispatcher
Dispatcher.BeginInvoke((Action)OnUpdateUI);
});
}
private void OnUpdateUI(){
}
In the above code, you are starting a process but not waiting for it to end, so the debugger when executes your:
Process.Star("Add Some Task");
, it jumps on to the next statement which is
button2.IsEnabled = true;
and so on. As a result your ProgressBar1 is not visible to you.
Please wait for the Process to end first. Write
Process.WaitForExit();
just after your statement
Process.Start("Your already started task");
Also you can create an Asynchronous thread to run in parallel.
Example:
Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
taskA.Start();
taskA.Wait();
Also in your above code, you are only displaying ProgressBar but not updating its value with time. So as a result ProgressBar will be visible only with initial value.
For ProgressBar, do something like,
ProgressBar ProgressBar1 = new ProgressBar();
ProgressBar1.Maximum = 100;
ProgressBar1.Minimum = 0;
Task.Start();
if( Task.Status == "Running")
{
ProgressBar1.Value = 50;
}
if( Task.Status == "completed")
{
ProgressBar1.Value =100;
}
else
{
ProgressBar.Value=0;
Task.Wait();
}
The code above mentioned may not be syntactically right. So do look for correct syntax.
Related
I've created a thread (or I'm trying to) which will autosave every so many minutes. Whenever I click the button to start the thread, the program doesn't autosave as instructed. So I'm reaching out for help. Here's the code:
private Thread saver;
...
saver.SetApartmentState(System.Threading.ApartmentState.STA);
saver = new Thread(new ThreadStart(SaveRegularly));
saver.Start();
Here's my SaveRegularly method:
private bool stopAndDie = false;
private void SaveRegularly()
{
//DateTime saveDueAt = DateTime.Now.AddMinutes(0.25);
//do
//{
//Thread.Sleep(1000);
//if (DateTime.Now >= saveDueAt)
//{
if (SaveDoc.FileName != "") //ADDED THIS TODAY (24/09)
{
CreateWordDocument(FilePath, SaveDoc.FileName, pathImage);
MessageBox.Show("Updated");
return;
}
else
{
if (SaveDoc.ShowDialog() == DialogResult.OK)
{
CreateWordDocument(FilePath, SaveDoc.FileName, pathImage);
MessageBox.Show("New Save");
return;
}
}
timer1.Start();
}
This is for the FormClosing Event, so the thread stops.
stopAndDie = true;
saver.Join(2000);
timer1.Stop();
When I run the program and I click the save button, I receive an error at the SaveFileDialog line (if (SaveDoc.ShowDialog() == DialogResult.OK)).Here is the error I receive.
Set the thread to STA mode.
saver.SetApartmentState(System.Threading.ApartmentState.STA);
But why aren't you using timers if you want to schedule something periodically?
Something like this:
System.Timers.Timer timer = new System.Timers.Timer(5 * 60 * 1000);
timer.Elapsed += (s, e) =>
{
//Invoke your show dialog on the UI thread here
};
timer.Start();
And when you want it to stop just call timer.Stop();
I am reading line by line console of an external exe with the help of a backgroundworker, i am assigning each line of console to a label. the problem is the label is not updating with the console line. code is given below
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
int i = 0;
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.FileName = EXELOCATION;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = Program.path;
startInfo.RedirectStandardOutput = true;
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (exeProcess = Process.Start(startInfo))
{
using (StreamReader reader = exeProcess.StandardOutput)
{
string result;
while ((result = reader.ReadLine()) != null)
{
// object param = result;
e.Result = result;
bgWorker.ReportProgress(i++);
}
}
}
}
catch
{
// Log error.
}
}
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label.Text = e.ToString();
label.Refresh();
}
how can i solve this issue
try this:
label2.Invoke(new Action(() => { label2.Text = e.ToString(); }));
label2.Invoke(new Action(() => { label2.Refresh(); }));
That code probably doesn't work because you're trying to update an UI element from a non-UI thread (aka background thread).
If you're using WPF, you should use the Dispatcher to request that the label be changed in the UI thread. If you're using another framework, try that framework's equivalent class.
In your ProgressChanged method, try this instead:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
() => {
label.Text = e.ToString();
});
If this is in another thread (and you are in a winforms application), you might need to use the Control.InvokeRequired
public void UpdateProgress (int progress)
{
if (label.InvokeRequired)
{
this.Invoke(()=> UpdateProgress(progress));
}
else
{
label.Text = progress.ToString();
}
}
This method checks whether it's run on the UI thread, and if it is not, it calls itself on the UI thread. If it is already on the UI thread, it simply updates the label.
This is not throwing me any error but after executing first thread it is not executing second thread. Am I doing anything wrong?
Below is my code:
My button click Function:
private void ImportBothButtonclick(object sender, EventArgs e)
{
// Get the currently selected manufacturer from the combo box
var selected = comboBox.SelectedItem;
// Do we have one?
if (selected != null)
{
// Extract the combo record
var val= (ComboBoxItem)selected;
// Do we have one?
if (val != null)
{
// yes
// Make this on a seperate thread so that the UI continues to work
Invoke(new System.Action(() =>
{
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = false;
var thread = new Thread(DoFunction1);
thread.Start(val);
}));
Invoke(new System.Action(() =>
{
button1.Enabled = false;
button2.Enabled = false;
button3Enabled = false;
var thread = new Thread(DoFunction2);
thread.Start(val);
}));
}
}
}
Those Actions will not do a thing. The actions are Invoked on the same thread you're currently on.
The threads are currently running parallel. If you want those threads to run in serial but not on the gui thread, you could do something like this:
This is the NON-TASK version.
// not allowed on a non-gui thread.
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = false;
new Thread(() =>
{
DoFunction1();
DoFunction2();
// execute this on the gui thread. (winforms)
this.Invoke(new Action( delegate
{
button1.Enabled = true;
button2.Enabled = true;
button3.Enabled = true;
}));
}).Start();
If you want to run them parallel, but wait until they are done:
// not allowed on a non-gui thread.
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = false;
new Thread(() =>
{
ManualResetEvent wait1 = new ManualResetEvent(false);
ManualResetEvent wait2 = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem((state) =>
{
DoFunction1();
wait1.Set();
});
ThreadPool.QueueUserWorkItem((state) =>
{
DoFunction2();
wait2.Set();
});
ManualResetEvent.WaitAll(new WaitHandle[] { wait1, wait2 });
this.Invoke(new Action( delegate
{
// execute this on the gui thread. (winforms)
button1.Enabled = true;
button2.Enabled = true;
button3.Enabled = true;
}));
}).Start();
But this can be more easy using tasks. Task Parallelism (Task Parallel Library) http://msdn.microsoft.com/en-us/library/dd537609.aspx
Please clarify what is the exact problem you observe?
According to what you have currently stated, the problem lies in "the second thread is not run after the first thread".
So, let me answer to this.
Your code is almost OK. You have overlooked one important thing: your code "new thread / thread.start()" actually do start a new thread, and then it does not wait for that thread to execute nor complete.
lines:
new thread(f1).Start()
new thread(f2).Start()
will not "run F1 on thread1, then run F2 on thread2". Instead, they will "begin to run F1 on thread1 and immediately begin to run F2 on thread2".
To have the F2 executed only after F1 has fully finished, you must somehow 'chain' the two together:
you can create simple "agenda" method and run it instead:
private void doAllTasks()
{
f1();
f2();
}
new thread(doAllTasks).Start()
you can try "chaining" them on the fly via lambdas, which effectively is the same as above:
new thread(() => { f1(); f2(); ).Start()
you can actually run them both immediatelly, but have the F2 join [wait] until the F1's thread ends
var th1 = new thread(f1);
var th2 = new thread(() => {th1.Join(); f2();} )
th1.Start();
th2.Start();
// note that this code is NOT perfect, there's some error handling to do here, etc..
or, you can try some nice and pretty wrappers for all of that like the TPL framework, as seen in Sheridan's answer.
And of course you must be careful with touching UI elements from inside what those other new thread's are running. Sheridan's answer already covers it via TPL ways. Manually, you'd have to use Invoke/BeginInvoke to bounce the UI-related code back to UI thread. In your current code you already have it, but at that place, it is not necessary, since the _Click handler method obviously already runs in UI thread.
Therefore, your current example can be reduced to i.e.:
private void ImportBothButtonclick(object sender, EventArgs e)
{
var selected = comboBox.SelectedItem;
if (selected != null)
{
var val= (ComboBoxItem)selected;
if (val != null)
{
// no Invoke needed, "button_click" handlers
// are already on UI thread, so touching UI things is OK
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = false;
// starting a new thread also does not need Invoke
var thread = new Thread(DoAllFunctions);
thread.Start(val);
}
}
}
private void DoAllFunctions(object something)
{
DoFunction1(something);
DoFunction2(something);
// button1.Enabled = true; - cannot do it here because they are UI
// button2.Enabled = true; - and DoAll is run from other thread.
// button3.Enabled = true; - you must bounce that back to UI thread.
LetTheUIKnowJobsAreFinished(); // <- performed here
}
private void LetTheUIKnowJobsAreFinished()
{
Invoke(new Action(()=>{
button1.Enabled = true;
button2.Enabled = true;
button3.Enabled = true;
});
}
Also, as a final note, please go and look at the BackgroundWorker from System.ComponentModel. It has very nice set of events/callbacks that will make all the thread-crossing very easy.
(btw. let me say that again: this code is NOT usable. it is only a sketch. you will find typos, missing colons, missing try-catch-whatever etc!)
Maybe you could do something like this?:
Task.Factory.StartNew(new Action(() =>
{
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = false;
}), new CancellationToken(), TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith(new Action(() => DoFunction1)).
ContinueWith(new Action(() =>
{
button1.Enabled = false;
button2.Enabled = false;
button3Enabled = false;
}), TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith(new Action(() => DoFunction2));
I have a WPF application that validates a user. When that user is successfully authenticated, the interface changes and says hello to the user. I want that Welcome message to appear during 5 seconds and then change it with another content. This is my Welcome message that launches the BackgroundWorker:
LabelInsertCard.Content = Cultures.Resources.ATMRegisterOK + " " + user.Name;
ImageResult.Visibility = Visibility.Visible;
ImageResult.SetResourceReference(Image.SourceProperty, "Ok");
BackgroundWorker userRegisterOk = new BackgroundWorker
{
WorkerSupportsCancellation = true,
WorkerReportsProgress = true
};
userRegisterOk.DoWork += userRegisterOk_DoWork;
userRegisterOk.RunWorkerAsync();
And this is my BackgroundWorker with the five seconds delay:
void userRegisterOk_DoWork(object sender, DoWorkEventArgs e)
{
if (SynchronizationContext.Current != uiCurrent)
{
uiCurrent.Post(delegate { userRegisterOk_DoWork(sender, e); }, null);
}
else
{
Thread.Sleep(5000);
ImageResult.Visibility = Visibility.Hidden;
RotatoryCube.Visibility = Visibility.Visible;
LabelInsertCard.Content = Cultures.Resources.InsertCard;
}
}
But the Backgroundworker freezes my GUI for the five seconds. Obviously, what I want to do is launch the code inside the worker 5 seconds after the Welcome message.
Why is it freezing the GUI?
You're explicitly defeating the purpose of the background worker.
Your code switches back to the UI thread in the callback and does everything there.
Maybe this is what you intended:
void userRegisterOk_DoWork(object sender, DoWorkEventArgs e)
{
if (SynchronizationContext.Current != uiCurrent)
{
// Wait here - on the background thread
Thread.Sleep(5000);
uiCurrent.Post(delegate { userRegisterOk_DoWork(sender, e); }, null);
}
else
{
// This part is on the GUI thread!!
ImageResult.Visibility = Visibility.Hidden;
RotatoryCube.Visibility = Visibility.Visible;
LabelInsertCard.Content = Cultures.Resources.InsertCard;
}
}
I'm trying to execute some Python scripts from my WPF app. The scripts are generating the log files and the code in the Tick event is reading them and displaying that as it is in a textbox.
My issue here is that, that LaunchProcess fires successfully, but the UI freezes. I have an indefinite progress bar, which too does not start animating. I'm a beginner with WPF and there is something very small I have to do to get this code working. I'm not getting any error/warnings. The scripts run fine and in the end I get to know the result too. But during the run, the UI of my app freezes.
private void LaunchProcess(string paramStr)
{
Process myProcess = new Process();
StartProgressBar();
try
{
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 0);
dispatcherTimer.Start();
myProcess.StartInfo.UseShellExecute = false;
// You can start any process
myProcess.StartInfo.FileName = "C:\\Python32\\python.exe";
myProcess.StartInfo.Arguments = "\""+paramStr+"\"";
myProcess.StartInfo.CreateNoWindow = true;
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.StartInfo.RedirectStandardError = true;
myProcess.Start();
myProcess.WaitForExit();
// This code assumes the process you are starting will terminate itself.
// Given that is is started without a window so you cannot terminate it
// on the desktop, it must terminate itself or you can do it programmatically
// from this application using the Kill method.
dispatcherTimer.Stop();
}
catch
{
MessageBox.Show("Process Launch Failed!!", "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
//txtOutPut.Text = "";
txtOutPut.Text += "\n" + DateTime.Now.ToString();
if (File.Exists(scriptPath+"\\log.txt"))
{
//File.Copy("C:\\FlashAuto\\Execution_Logs\\log.txt", "C:\\FlashAuto\\Temp\\log.txt", true);
TextReader readLogs = new StreamReader(scriptPath + "\\log.txt");
string line = readLogs.ReadLine();
while (line != null)
{
txtOutPut.Text += "\n" + line;
line = readLogs.ReadLine();
txtOutPut.ScrollToEnd();
}
//CountLines = txtExecLog.LineCount - 1;
readLogs.Close();
// Forcing the CommandManager to raise the RequerySuggested event
txtOutPut.ScrollToEnd();
CommandManager.InvalidateRequerySuggested();
readLogs.Dispose();
}
else
{
txtOutPut.Text += "log file not found at: " + DateTime.Now.ToString();
}
}
In case you call LaunchProcess from the UI thread it will obviously be blocked at myProcess.WaitForExit().
You might simply remove the myProcess.WaitForExit() and dispatcherTimer.Stop() calls from the launch method and check if the process is still running in the timer Tick handler.
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
if (myProcess.WaitForExit(0)) // check with timeout zero
{
dispatcherTimer.Stop();
}
... // all your code
}
Calling LaunchProcess method asynchronously would resolve your UI Freeze Issue
public void LaunchProcessAsynchrousCall(string paramStr)
{
ThreadStart displayContentHandler = delegate()
{
LaunchProcess(paramStr)
};
Thread thread = new Thread(displayContentHandler);
thread.IsBackground = true;
thread.Start();
}