could you tell a beginner why this small WPF-application is not closing as intended after the WorkflowTerminated event fires? The used workflow just terminates immediately. (using a WPF application, .Net Framework 3.5)
public partial class MainWindow : Window
{
private WorkflowRuntime wfRuntime = new WorkflowRuntime();
public MainWindow()
{
InitializeComponent();
wfRuntime.WorkflowTerminated += (se, ev) => this.Close(); // this doesn't close the window
wfRuntime.WorkflowCompleted += (se, ev) => this.Close();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
WorkflowInstance launcherWorkflow = wfRuntime.CreateWorkflow(typeof(InstallerWorkflow));
launcherWorkflow.Start();
}
}
Probably because the callback is on another thread. A basic workaround is to terminate the application altogether using Environment.Exit(1);
To call the close function on the UI thread you should use:
wfRuntime.WorkflowTerminated += (se, ev) => {
// call back to the window to do the UI-manipulation
this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate()
{
this.Close();
}));
};
Related
this is the simplified plan for a solution:
for some reasons i need to run a windows form through a backgroundworker that is runnig by another backgroundworker, when the new windows form loads, the older backgroundworker must pause. i write the code like this :
creating a class with name : temp
public class temp
{
static public BackgroundWorker backgroundWorker1 = new BackgroundWorker() { WorkerSupportsCancellation = true };
static public EventWaitHandle ew = new EventWaitHandle(false, EventResetMode.ManualReset);
static public BackgroundWorker back = new BackgroundWorker() { WorkerSupportsCancellation = true };
}
the codes for form1 are :
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
temp.backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
temp.back.DoWork += new DoWorkEventHandler(back_DoWork);
}
void back_DoWork(object sender, DoWorkEventArgs e)
{
Form2 f = new Form2();
f.Show();
}
private void button1_Click(object sender, EventArgs e)
{
temp.backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
temp.back.RunWorkerAsync();
if (temp.backgroundWorker1.CancellationPending)
temp.ew.WaitOne();
}
}
}
and the codes of form2 goes here :
namespace WindowsFormsApplication1
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
temp.backgroundWorker1.CancelAsync();
temp.ew.Reset();
}
}
}
by clicking the button1 from form1 the temp.backgroundworker1 runs and then in the DoWork of temp.backgroundworker1, the temp.back runs and then FORM2 LOADS BUT THE FORM2 HANGS AND BECOMES USELESS AND YOU CANNOT USE THAT ANY MORE.
where did i wrong ?
the whole plan that i'm going to execute is :
we have a For loop that processes every row of a DataGridView.
each time in a certain point, another windowsform opens
and it stops the loop until the user inserts the information and then click on OK button, the windowsform closes and the loop keep on working. i dont know what to do.......
even if i dont cancel working of the temp.backgroundworker in form2load like the code below, the Form2 is useless
private void Form2_Load(object sender, EventArgs e)
{
}
Do not use any UI operation in the work thread (DoWork method). Maybe that's why you set the CheckForIllegalCrossThreadCalls property, but your app will not work properly just suppresses the error when the debugger is attached.
See my answer here for the correct usage of the BackgroundWorker (that is about canceling but you can see the operations in UI and worker thread).
In this particular case what you can use a similar volatile bool to sign the UI thread that the form can be shown. Or, if you want to send different messages between the threads, use a ConcurrentQueue<T> to write and read messages:
public partial class Form1 : Form
{
private enum Message
{
ShowForm2,
SuspendWork,
ResumeWork,
FinishWorker1
// ... and whatever you want
}
private Timer timer;
private ConcurrentQueue<Message> messagesToUI = new ConcurrentQueue<Message>();
private ConcurrentQueue<Message> messagesToWorker = new ConcurrentQueue<Message>();
public Form1()
{
InitializeComponent();
timer = new Timer(this);
timer.Interval = 10;
timer.Tick += PollUIMessages;
timer.Enabled = true;
}
void PollUIMessages(object sender, EventArgs e)
{
// do we have a new message?
Message message;
if (messagesToUI.TryDequeue(out message))
{
switch (message)
{
case Message.ShowForm2:
Form2 f = new Form2();
f.Show();
// todo: in Form2.Close add a Resume message to the messagesToWorker
break;
// ... process other messages
}
}
}
void back_DoWork(object sender, DoWorkEventArgs e)
{
// Here you are in the worker thread. You can send a message to the
// UI thread like this:
messagesToUI.Enqueue(Message.ShowForm2);
bool isWorking = true;
// and here you can poll the messages to the worker thread
while (true)
{
Message message;
if (!messagesToWorker.TryDequeue(out message))
{
// no message: idle or work
if (isWorking)
DoSomeWork(); // do whatever you want
else
Thread.CurrentThread.Sleep(10);
continue;
}
switch (message)
{
case Message.FinishWorker1:
// finishing the worker: jumping out
return;
case Message.SuspendWork:
isWorking = false;
break;
case Message.ResumeWork:
isWorking = true;
break;
}
}
}
I have created a Form for long-running Tasks, it's purpose is to loop a Progress Bar until the Thread completes. I display this Form as a Dialog so that my main application is Waiting until this Form returns.
Unfortunately, my RunWorkerCompleted is being called before the Thread actually finishes its task.
What is an effective method of Waiting for a Thread to Finish, WITHOUT Joining?
public partial class FormProcessing : Form
{
public ThreadStart StartTaskFunc { get; set; }
public FormProcessing()
{
InitializeComponent();
bgWorker.DoWork += bgWorker_DoWork;
bgWorker.ProgressChanged += bgWorker_ProgressChanged;
bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
Shown += (s, e) =>
{
bgWorker.RunWorkerAsync();
};
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Close();
}
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
var thread = new Thread(() => StartTaskFunc());
thread.Start();
int i = 0;
while (thread.IsAlive)
{
if (i == 26)
i = 0;
bgWorker.ReportProgress(i);
Task.Delay(200).Wait();
i++;
}
}
}
You are creating another "Thread" in "bgWorker_DoWork" method.
If you create another thread, the "bgWorker_DoWork" is completed as soon as the new thread is created.
I try to use mouse leave event handler but i can not work it.
private void Form1_MouseLeave(object sender, EventArgs e)
{
this.Close();
}
I don't want to fade. It should be closed immediately when the mouse cursor goes out of the form screen.
To close the whole application use
Environment.Exit(0);
The MouseLeave event will fire if the mouse enters a child control, which is probably not what you want.
Try using a timer:
private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
private bool mouseEntered = false;
public Form1() {
InitializeComponent();
timer.Tick += timer_Tick;
timer.Start();
}
protected override void OnMouseEnter(EventArgs e) {
mouseEntered = true;
base.OnMouseEnter(e);
}
void timer_Tick(object sender, EventArgs e) {
if (mouseEntered && !this.Bounds.Contains(Cursor.Position)) {
this.Close();
}
}
If you have several forms I prefer Application.Exit().
Form.Close() only works for a single form application.
If there is only one form in that application you are using, then Application.Exit will close the application itself.
If there is one more form that you are navigating to from the main form, then in the new form, use Form2.Close(), this will take you to the back to the main application that you are using.
If you want to close both of them, then first give Form2.Close() and then give Application.Exit(). That would suffice.
I made a little test program to recreate your problem.
namespace OddFormTest
{
using System;
using System.Windows.Forms;
public class OddForm : Form
{
public OddForm()
{
this.Leave += Leaver;
}
[STAThread]
internal static void Main()
{
Application.Run(new OddForm);
}
private void Leave(object sender, EventArgs e)
{
this.Close()
}
}
}
As you infer in the question. The Leave event doesn't fire after you leave your application.
Let's imagine that we have two Forms: MainForm and WaitingForm. I want to pass, from MainForm, to the WaitingForm the method to run in background using BackgroundWorker.
Now, I'm doing things that way:
MainForm.cs:
public partial class MainForm: Form
{
private void btnImport_Click(object sender, EventArgs e)
{
var waitingFrm = new WaitingForm();
waitingFrm.DoWork = (o, args) => this.LongRunningOperation(this, new DoWorkEventArgs("foo bar"));
waitingFrm.OnWorkCompleted = (o, args) => MessageBox.Show("Finished!");
waitingFrm.Show();
waitingFrm.Run(); // should execute LongRunningOperation, method below.
}
private void LongRunningOperation(object sender, DoWorkEventArgs e)
{
MessageBox.Show("Running long operation!....");
// some long running stuff here;
}
}
WaitingForm.cs
public partial class WaitingForm: Form
{
private BackgroundWorker worker = new BackgroundWorker();
public DoWorkEventHandler DoWork { get; set; }
public RunWorkerCompletedEventHandler OnWorkCompleted { get; set; }
public WaitingForm()
{
this.worker.DoWork += DoWork;
this.worker.RunWorkerCompleted += OnWorkCompleted;
InitializeComponent();
}
public void Run()
{
this.worker.RunWorkerAsync();
}
}
But after waitingFrm.Run(); my LongRunningOperation is not executed.
In your WaitingForm I'd do:
public event DoWorkEventHandler DoWork {
add { worker.DoWork += value; }
remove { worker.DoWork += value; }
}
(Instead of the get;set; property).
And then in your main window btnImport_Click handler just:
waitingFrm.DoWork += LongRunnignOperation;
And the same for completed handler. Your syntax seems overly complicated. This is just a clean way to expose an event (in this case on your waitingform) and the pass event handler through to the real handler (in this case worker.DoWork). It is equivalent to
waitingFrm.worker.DoWork += LongRunnignOperation;
which would do just as well.
I want to pass, from MainForm, to the WaitingForm the method to run
in background using BackgroundWorker
I would in this case
declare an event in WaitingForm
before Form1 shows WaitingForm subscribes to that event
when long running operation has to be runned WaitingForm raise an event, Form1 gets it and
Form1 runs its method in other thread.
Hope this helps.
In this particular case you want all of the work to happen in MainForm and it looks like WaitingForm is just a display for the user. If that's the case then I would just put the BackgroundWorker in the MainForm and use the event to call into WaitingForm
public partial class MainForm: Form
{
private void btnImport_Click(object sender, EventArgs e) {
var waitingForm = new WaitingForm();
waitingForm.Show();
var worker = new BackgroundWorker();
worker.DoWork += (o, args) => this.LogRunningOperation(o, args);
worker.OnWorkComplete += (o, args) => {
waitingForm.Close();
worker.Dispose();
};
worker.RunWorkerAsync();
}
private void LongRunningOperation(object sender, DoWorkEventArgs e) {
MessageBox.Show("Running long operation!....");
// some long running stuff here;
}
}
So, the simple answer is that. Your code is not working because the mainform is not seeing the BackgroundWorker object instance events. Instead of doing:
this.worker.DoWork += DoWork;
this.worker.RunWorkerCompleted += OnWorkCompleted;
in WaitingForm - InitializeComponent(), do this instead in mainForm like this:
waitingFrm.worker.DoWork += waitingFrm.DoWork;
waitingFrm.worker.RunWorkerCompleted += waitingFrm.OnWorkCompleted;
[EDIT]
I edited my question with complete code and explanation and hope something can give me clearer explanation.
I have the following Class that has a backgroundworker to track the percentage progress of a loop and update the percentage progress on a Label on ProgressWin's XAML. The following code works perfectly. (My question is far below, after the code.)
public partial class ProgressWin : Window
{
BackgroundWorker backgroundWorker1 = new BackgroundWorker();
public ProgressWin()
{
InitializeComponent();
InitializeBackgroundWorker();
startAsync();
}
// Set up the BackgroundWorker object by
// attaching event handlers.
private void InitializeBackgroundWorker()
{
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
private void startAsync()
{
backgroundWorker1.RunWorkerAsync();
}
public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; i <= 10; i++)
{
System.Threading.Thread.Sleep(500);
worker.ReportProgress(i * 10);
}
}
// This event handler updates the progress.
public void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Content = (e.ProgressPercentage.ToString() + "%");
}
// This event handler deals with the results of the background operation.
public void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
resultLabel.Content = "Done!";
}
}
Here comes my problem, now instead of tracking the loop within the ProgressWin, I need to track the loop in my MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
for (int i = 1; i <= 10; i++)
{
System.Threading.Thread.Sleep(500);
????.ReportProgress(i * 10);
}
}
}
And I have no idea where to go from here. I tried instantiating an object from ProgressWin and call the DoWork but I end up frozen the ProgressWin window.
Due to the fact that the question was rewritten, i also rewrote my whole answer.
To get this to work your MainWindowhas a ProgressWindow and should use it like a background worker:
public partial class MainWindow : Window
{
private ProgressWindow _Progress;
public MainWindow()
{
InitializeComponent();
_Progress = new ProgressWindow();
_Progress.ProgressChanged += OnProgressChanged;
}
private void OnProgressChanged(object sender, ProjectChangedEventArgs e)
{
//ToDo: Update whatever you like in your MainWindow
}
}
To accomplish this your ProgressWindow should simply subscribe to the worker event and rethrow it:
public partial class ProgressWin : Window
{
// Add this to your class above in your question
public event ProgressChangedEventHandler ProgressChanged;
// Change or merge this with your existing function
private void backgroundWorker1_ProgressChanged(object sender, ProjectChangedEventArgs e)
{
var temp = ProgressChanged;
if(temp !=null)
temp(this, e);
}
}
You can simply call method of another class by doing
backgroudnWorker.DoWork += new DoWorkEventHandler(SomeClass.SomeStaticMethod);
or
backgroudnWorker.DoWork += new DoWorkEventHandler(SomeClassInstance.SomeMethod);
for calling a method of MainWindow class from another class ProgressScreen you should have reference of instance of MainWindow class in ProgressScreen then using that instance you can call any public method of MainWindow from ProgressScreen class
and as Oliver Said, you will need the instance of backgroundworker to send the progress updates from other method.