I have developed a windows forms c# application, i just want update items in a Listbox in the main form by spin offing another thread without blocking the GUI form.
Since threads cannot access form entities like listbox, i thought of using delegates.
Following code in the below shows how i used a delegate to do that task, but it blocks the GUI form. so i just want to convert it to an asynchronous delegate which updates list box without blocking the GUI Form
delegate declaration
delegate void monitoringServiceDel();
calling the delegate
new monitoringServiceDel(monitoringService).BeginInvoke(null, null);
delegate method implementation
private void monitoringService()
{
this.listEvents.Invoke(new MethodInvoker(delegate()
{
int i = 0 ;
while (i<50)
{
listEvents.Items.Add("count :" + count++);
Thread.Sleep(1000);
i ++;
}
}));
}
For Win Forms you'll need to use the Control's Invoke method:
Executes the specified delegate on the thread that owns the control's
underlying window handle
The basic scenario is:
Do the heavy lifting work with a BackgroundWorker to retrieve all of your items on a non UI blocking thread.
On the BackgroundWorker.RunWorkerCompleted Event, use the Control's Invoke method to add the items to the Control (ListBox in your case).
Something along the lines of:
var bw = new BackgroundWorker();
bw.DoWork += (sender, args) => MethodToDoWork;
bw.RunWorkerCompleted += (sender, args) => MethodToUpdateControl;
bw.RunWorkerAsync();
This should get you going in the right direction.
Edit: working sample
public List<string> MyList { get; set; }
private void button1_Click( object sender, EventArgs e )
{
MyList = new List<string>();
var bw = new BackgroundWorker();
bw.DoWork += ( o, args ) => MethodToDoWork();
bw.RunWorkerCompleted += ( o, args ) => MethodToUpdateControl();
bw.RunWorkerAsync();
}
private void MethodToDoWork()
{
for( int i = 0; i < 10; i++ )
{
MyList.Add( string.Format( "item {0}", i ) );
System.Threading.Thread.Sleep( 100 );
}
}
private void MethodToUpdateControl()
{
// since the BackgroundWorker is designed to use
// the form's UI thread on the RunWorkerCompleted
// event, you should just be able to add the items
// to the list box:
listBox1.Items.AddRange( MyList.ToArray() );
// the above should not block the UI, if it does
// due to some other code, then use the ListBox's
// Invoke method:
// listBox1.Invoke( new Action( () => listBox1.Items.AddRange( MyList.ToArray() ) ) );
}
If you are modifying a UI element, then you are going to HAVE to block the UI thread. If the items come in bursts or require processing between adding each one, then you might want to think about running the processing behind the scenes (via a backgroundworker or a Task). But, if you are just taking data and populating the list, then you are required to use the UI thread.
The easiest solution would be to use the BackgroundWorker control, combined with two Panels. The idea is to have one panel on the foreground Visible when the form loads, and have an ImageBox inside of it that plays a simple loading gif. The ListBox will be inside the other panel that won't be visible by default and will be right behind the first panel.
Once the form is loaded, start your BackgroundWorker and accomplish whatever Data retrieving or updating that you have to do and once the Task is complete, set the data inside your ListBox and simply bring the ListBox panel and make it visible.
That way you'll have a Semi Asynchronous loading of your ListBox, while it's not updated after every item being added. You can use this technique anytime you want, not simply on form load!
Here is a code example:
namespace AsyncForm
{
public partial class Form1 : Form
{
private List<String> collectionItems = new List<String>();
public Form1()
{
InitializeComponent();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 20; i++)
{
((List<String>)e.Argument).Add("Something " + i);
System.Threading.Thread.Sleep(200);
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
listBox1.Items.AddRange(collectionItems.ToArray());
listBox1.Visible = true;
pictureBox1.Visible = false;
}
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync(collectionItems);
}
}
}
You should separate function to update UI and long-time process.
To handle UI logic..
private void UpdateUI(string item)
{
if (Thread.CurrentThread.IsBackground)
{
listEvents.Dispatcher.Invoke(new Action(() => //dispatch to UI Thread
{
listEvents.Items.Add(item);
}));
}
else
{
listEvents.Items.Add(item);
}
}
To do asynchronous process using TaskParallel
private void Dowork()
{
Task task = Task.Factory.StartNew(() =>
{
int i = 0;
while (i < 10)
{
Thread.Sleep(1000);
UpdateUI(i.ToString());
i++;
}
});
}
Related
I have 2 form (formA & formB) in my project c#, i want to run some process in backgroundworker when i click a button in formA.
can i update from backgroundworker to label in formB?
here's the code in formA
private void button1_Click_1(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Stimulus stimulus = new Stimulus();
Stopwatch watch = new Stopwatch();
stimulus.Show();
stimulus.Invoke((MethodInvoker)delegate { stimulus.perbaharuiStimulus("+"); });
watch.Start();
do
{
} while (watch.Elapsed.Seconds != 2);
watch.Restart();
stimulus.Invoke((MethodInvoker)delegate { stimulus.perbaharuiStimulus("MAJU"); });
do
{
} while (watch.Elapsed.Seconds != 6);
watch.Restart();
stimulus.Invoke((MethodInvoker)delegate { stimulus.perbaharuiStimulus(""); });
do
{
} while (watch.Elapsed.Seconds != 2);
watch.Stop();
stimulus.Close();
}
and heres code in formB
public Stimulus()
{
InitializeComponent();
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
}
public void perbaharuiStimulus(string stimulus)
{
this.Invoke((MethodInvoker)delegate
{
lbStimulus.Text = stimulus;
});
}
thankyou for attention..
You can change your code like below and it'll work fine.
Change perbaharuiStimulus code to
lbStimulus.Text = stimulus;
Change WorkerReportsProgress to True
Change backgroundWorker1_DoWork to below
Stimulus stimulus = new Stimulus();
Stopwatch watch = new Stopwatch();
backgroundWorker1.ReportProgress(1, stimulus);
watch.Start();
do
{
} while (watch.Elapsed.Seconds != 2);
watch.Restart();
backgroundWorker1.ReportProgress(2, stimulus);
do
{
} while (watch.Elapsed.Seconds != 6);
watch.Restart();
backgroundWorker1.ReportProgress(3, stimulus);
do
{
} while (watch.Elapsed.Seconds != 2);
watch.Stop();
stimulus.Invoke((MethodInvoker)delegate { stimulus.Close(); });
Add the backgroundWorker1_ProgressChanged event and put below code in it
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Stimulus stimulus = ( Stimulus)e.UserState;
if(e.ProgressPercentage==1)
stimulus.perbaharuiStimulus("+");
if (e.ProgressPercentage == 2)
stimulus.perbaharuiStimulus("MAJU");
if (e.ProgressPercentage == 3)
stimulus.perbaharuiStimulus("");
stimulus.Show();
}
I hope this help you!
You shouldn't be creating the form in the background thread. Doing so will assign the form to that thread instead of the UI thread, meaning the form is now on a different thread than the message pump.
What you can do to fix this is to invoke the instantiation and viewing of the form on the UI thread, then your following Invoke calls should work.
Stimulus stimulus;
this.Invoke((MethodInvoker)delegate
{
stimulus = new Stimulus();
stimulus.Show();
});
//No need to invoke this since perbaharuiStimulus() calls Invoke() as well.
stimulus.perbaharuiStimulus("+");
//The rest of your code...
Apart from that you're doing everything correctly.
You can update a label in any form from a background worker using Invoke(...) like you did it. (Assuming stimulus is a field).
It is enough to call Invoke once. Stimulus.Invoke executes the delegate on the control thread of the stimulus form. So you can decide, whee you dispatch the thread. I'd recommend to do this in perbarauiStimulus, since that would reduce the chance that someone forgets to dispatch the call.
But there is one potential issues with your code:
Don't use exact comparison for elapsed time. Prefer using '>='. Since you are dealing with seconds this will rarely be an actual problem, but it may result in an infinite loop.
If stimulus isn't a field you have to create an instance of Stimulus outside of the background worker, because if you create it inside the worker Method, the form will run its message loop on the background workers thread. This eliminates the use of a background worker since the operation runs now synchronously from sytimulus view.
Scenario: I have a DataGrid in my application in which onclikcing each row, the values get populated in the texboxes below. I'll update the values and when clicking save the process is taking more time to complete.
So i have written a backgroundworker to make the process run asynchronously. When each row is clikced an instance of a backgroundworker is created and the process is accomplished. During that update the user will select the second row of the grid and update that values. So this will create another instance and the process will run in background.
Now when both the update process is running if the user selects the first row of the grid there should be a message showing "The process is still running".
//Code:
' OnClick of the event
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += delegate {
SaveDetails();
};
bw.RunWorkerCompleted += delegate {
MessageBox.Show("Completed");
};
bw.RunWorkerAsync();
'Save method
public void SaveDetails()
{
for (int i = 0; i < 10;i++ )
{
System.Threading.Thread.Sleep(5000);
MessageBox.Show("Hi");
}
}
How can i access the previously created backgroundworker instance and check the status of the process and display the messgage?
Note: There may be many process running simultaneously, so i should be able to access any process.
Is this possible?
You can store any BackgrounWorker you create in a List to refer to them at any moment, but alternatively you can create a tasks Queue and store there any pending process, so you have only one BackgroundWorker at any time.
Storing the BackgroundWorkers in a List
Create a List(Of BackgroundWorker) which can be accesed anywhere you need in your code, for example. Every time you create a new BackgroundWorker, add it to the List:
mylist.Add(bw)
You a lot of options to access the correct backgroundworker later on. The easiest one is to create your own class which will have an identificator (the row of the DataGrid, for example) and the backgroundworker. This way, your list will be of this class instead of BackgroundWorkers:
myClass.BackgroundWorkerProperty = bw
myClass.id = myId
myList.Add(myClass)
Using a Queue to run the tasks
Create a Queue with a type which has the information to run the task. For example, the row of the DataGrid, if that's enought, that will be type Integer (the index), then add it everytime the backgroundworker is running:
myQueue.Add(myRow)
Everytime the backgroundworker finish, check the Queue and run the next task stored.
You can use the Tag property of the DataGridViewRow:
var bw = new BackgroundWorker();
row.Tag = bw;
So you can access it.
If you are using .NET 4.5 i would suggest to use Async/Await rather than Background thread to solve your problem, because its programming style is easy and efficient as given below:
public partial class Form1 : Form
{
List<Task<bool>> taskList = new List<Task<bool>>();
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
Task<bool> task = Task.Run(() => SaveDetails());
MessageBox.Show(task.Id + " started.");
taskList.Add(task);
var isSuccess = await task;
}
public bool SaveDetails()
{
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(5000);
//MessageBox.Show("Finishing.");
}
return true;
}
private void button2_Click(object sender, EventArgs e)
{
foreach (var task in taskList)
{
if (task.IsCompleted == true)
MessageBox.Show(task.Id + " Completed.");
}
}
}
i have created a second button click event to access and display all currently completed tasks.
Also, please make a note that you can use task.Id as the row identifier of your grid row.
Hope that helps.
well, that's rather easy. put the BackgroundWorker as a member in your form and then you can access it from everywhere:
BackgroundWorker bg;
public Form1()
{
bg = new BackgroundWorker();
}
private void button1_Click(object sender, EventArgs e)
{
if(bg.IsBusy)
{
// show the message
}
else
{
bw.WorkerReportsProgress = true;
bw.DoWork += delegate {
SaveDetails();
};
bw.RunWorkerCompleted += delegate {
MessageBox.Show("Completed");
};
bw.RunWorkerAsync();
}
}
I have a program which is updating a table in sqlserver, I have a form which I want to show the progress of this, the progress bar is incrementing but this is not being displayed. do i need to use background worker for this? example of what im doing
public void updateTable(string tableName)
{
// con is an instance of my form to access progressbar
con.progressBar1.Minimum = 1;
con.progressBar1.Step = 1;
string dbQuery = "select summet from someting"
con.progressBar1.Maximum = address.Tables[0].Rows.Count;
MessageBox.Show("progress bar max " + con.progressBar1.Maximum);
foreach (DataRow LonLat in address.Tables[0].Rows)
{
con.progressBar1.PerformStep();
MessageBox.Show(con.progressBar1.Value.ToString()); // this is incrementing
//plus updating table
}
}
Short answer, yes. If you do it from the main thread you won't see the progress bar update.
Long answer:
Whenever you modify the user interface from a piece of your code that's translated into a "message" into the window message queue that will get that message and update the UI accordingly in the main thread.
However, since the main thread is busy handling your piece of code it doesn't have the "time" to actually update the user interface, and only when your process is done, it's free to update the user interface. That's why you see the progress bar going from 0% to 100% without any intermediate steps.
What you should do:
What you wanna do is put the work into a background worker, that way the main thread is free to attend UI update requests... that's, by the way, a standard practice if you wanna keep the UI responsive.
Yes you should use Background Worker, however it might not help, we would need to see bindings and everything else, to use background worker use something like this:
Window.Dispatcher.BeginInvoke((Action)(() =>
{
con.progressBar1.PerformStep();
}
yes you have to use background worker
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (o, ea) =>
{
// your code goes here
}
worker.RunWorkerCompleted += (q, ea) =>
{
}
worker.RunWorkerAsync();
That happens if we cannot use a BackgroundWorker. I have a form with a progress bar whose only job is to accept a command to PerformStep, from some other form that opens it, and the form must simply do that. Both forms are in the GUI thread.
Nevertheless I don't get a consistent behavior from the progressbar. In the code below the progressbar is always one step behind than what its Value Property Signifies:
public partial class Form1 : Form
{
private readonly int m_numOfSteps;
public Form1(int numOfSteps)
{
m_numOfSteps = numOfSteps;
InitializeComponent();
}
private void Form1_Load( object sender, EventArgs e )
{
progressBar1.Step = (int)Math.Ceiling( 100.0 / m_numOfSteps );
}
public void DoStep(string msg)
{
progressBar1.PerformStep();
label1.Text = msg;
label2.Text = String.Format( "{0}%", progressBar1.Value );
label1.Refresh();
label2.Refresh();
}
}
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void button1_Click( object sender, EventArgs e )
{
using( Form1 f = new Form1( 3 ) )
{
f.Show();
f.Refresh();
Thread.Sleep( 1000 );
f.DoStep( "AAAA" );
Thread.Sleep( 1000 );
f.DoStep( "BBBB" );
Thread.Sleep( 1000 );
f.DoStep( "CCCC" );
Thread.Sleep( 1000 );
}
}
}
I'm using a BackgroundWorker to periodically check a hardware switch. Due to it is connected via a slow RS485 network, I have to delay the next status update.
On switch Status change I want to update an OK/nOK Picture Box. This is realized as a green OK pictureBox over a nOK pictureBox. No real work is done here.
For expandability I decided to use the Backgroundworker. Finally I want to have a hidden worker, which
provides globally the Status of three switches and
updates on StatusChange the PictureBoxes.
Problem description
Once the BackgroundWorker is started, it works as expected. However the GUI freezes.
What did I try?
The MSDN BackgroundWorker Class Note 1
says, that GUI should be updated via ProgressChanged. I tried to raise this Event by Worker_Switch.ReportProgress(fakeProgress++) and failed. The PictureBox wasn't updated anymore.
Snippet from designer
this.Worker_Switch = new System.ComponentModel.BackgroundWorker();
//
// Worker_Switch
//
this.Worker_Switch.WorkerSupportsCancellation = true;
this.Worker_Switch.DoWork += new System.ComponentModel.DoWorkEventHandler(this.Worker_Switch_DoWork);
Snippet from Main Form
delegate void SetEventCallback(object sender, DoWorkEventArgs e); // Threadsafe calls for DoWork
private void btnBackgroundworker_Click(object sender, EventArgs e)
{
if (!Worker_Switch.IsBusy)
{
Worker_Switch.RunWorkerAsync();
}
}
private void Worker_Switch_DoWork(object sender, DoWorkEventArgs e)
{
// Worker Thread has no permission to change PictureBox "pictureBoxSwitchrightOK"
// Therefore this method calls itsself in the MainThread, if necessary.
while (!Worker_Switch.CancellationPending)
{
if (this.pictureBoxSwitchrightOK.InvokeRequired) // Worker Thread
{
System.Threading.Thread.Sleep(400);
SetEventCallback myCall = new SetEventCallback(Worker_Switch_DoWork);
this.Invoke(myCall, new object[] { sender, e });
}
else // Main Thread
{
// Turns OK Picture Box invisible, if nOk State (Switch pushed)
pictureBoxSwitchrightOK.Visible = SwitchOK("right"); // true: OK (green)
this.Refresh();
}
}
private bool SwitchOK(string rightOrLeft) // select one of the switches
{ (...)} // gets hardware switch status
Edit: Special Thanks to laszlokiss88 (3 possibilities) and JMK (for simplicity with System.Windows.Forms Timer from toolbox)
This alternative from Toolbox also worked:
this.timer_Switch.Enabled = true;
this.timer_Switch.Interval = 400;
this.timer_Switch.Tick += new System.EventHandler(this.timer_Switch_Tick);
private void timer_Switch_Tick(object sender, EventArgs e)
{
motorSwitchControl.Init(); // globally available Switch status
SwitchRight = SwitchOK("right");
SwitchRightOK.Visible = SwitchRight;
SwitchLeft = SwitchOK("left"); // globally available Switch status
SwitchLeftOK.Visible = SwitchLeft;
SwitchAllOK = SwitchRight & SwitchLeft;
this.Refresh();
}
a) Is it correct, that the Sleep() actually happens in the Worker Thread?
- no Main Thread
b) What is going wrong, if I manipulate user interface objects in DoWork? (Contrary to MSDN Note)
- works in Main Thread?
c) What is the correct way to periodically update a PictureBox? DoWork, ProgressChanged, RunWorkerCompleted...?
- Three possibilities from laszlokiss88 answer.
You can update the UI from the DoWork event via the Dispatcher, or Control.Begininvoke(winforms), or you can do it via the ProgressChanged event of the BackgroundWorker:
public MainWindow()
{
InitializeComponent();
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerAsync();
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// You are in the main thread
// Update the UI here
string data = (string)e.UserState;
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// You are in a worker thread
(sender as BackgroundWorker).ReportProgress(0, "right");
}
For a start you should almost never have a need to put an active background thead to sleep. I am also not sure why you are constructing/defining the delegate this way, try some thing like
public delegate void UpdatePictureBox();
myDelegate = new UpdatePictureBox(UpdatePictureboxMethod);
then you have a method UpdatePictureBoxMethod
private void UpdatePictureBoxMethod()
{
this.pictureBox1.Image = Properties.Resources.SomeImage;
}
or something simalar, where you pass in the image to update to.
Alternatively you could use the (bgWorker as BackgroundWorker).ReportProgress(progress, object); method. So from the background thread you call
(bgWorker as BackgroundWorker).ReportProgress(progressBarValue, infoBall);
where here class IfoBall will hold all your important information
class InfoBall
{
public int nProgressBar { get; set; }
public int nMaxProgressBar { get; set; }
public Image image { get; set; }
}
then you can pass this object back to the UI thread and do your updates
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// On UI thread.
InfoBall someBall = (InfoBall)e.UserState;
this.pictureBox1.Image = someBall.image;
// etc...
}
I hope this helps.
I have a MainFrame window with imageViewer control on it. Also there is my dll which calculates changes for the image all was working fine before I decided to add ProgressDialog.(( The Idea was - firstly I am loading the image via dll to main frame (this still OK). Then if user clicks button then show ProgressDialog and in worker.DoWork create new image via the same dllwrapper class (I am using "new")
All seems to be ok but when i am trying to set my currentImage property of imageviewer control (this is nothing more then setter for Image)it show me this error!
This is the code of my userButtonClickHandler from where I am launching ProgressDialog:
void OnThumbnailClick(object sender, RoutedEventArgs e)
{
pd = new ProgressDlg();
pd.Cancel += CancelProcess;
int max = 1000;
System.Windows.Threading.Dispatcher pdDispatcher = pd.Dispatcher;
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
LibWrap lwrap = new LibWrap();//!NEW instance for dll wrapper!
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//ERROR IS HERE!!!//The calling thread cannot access this object because a different thread owns it.
//what process??
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
pd.Close();
};
worker.RunWorkerAsync();
pd.ShowDialog();
}
There is function from the same MainFrame class for canceling (There is OK too)
void CancelProcess(object sender, EventArgs e)
{
worker.CancelAsync();
}
This is class for ProgressDlg (it has nothing more then progress bar and cancel button):
public partial class ProgressDlg : Window
{
public ProgressDlg()
{
InitializeComponent();
}
public string ProgressText
{
set
{
this.lblProgress.Content = value;
}
}
public int ProgressValue
{
set
{
this.progress.Value = value;
}
}
public event EventHandler Cancel = delegate { };
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
Cancel(sender, e);
}
}
}
I am working with this problem for (almost) two days and still couldn't find the solution. Help me please if you have an idea.
1 UPDATE
It seems to me that you was right about this threads - when I am trying to load previously loaded(initial) image (from the main thread) -it loads OK but if I am trying libWrap it fails due to processes conflict!
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
imageViewer.Width = 1000;//work!
imageViewer.CurrentImage = MyPrj.App.draggedImage;//Work!
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//Fail =(!
}
2 UPDATE
I have tried this construction OnThumbnailClick
Application.Current.MainWindow.Dispatcher.BeginInvoke(new Action(() =>
{
imaeViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(FXPhotoStudio.App.draggedImage));
}
This caused same error/ Perhaps it will be correct to pass this value in MainThread (UI)? But I have no idea how.( I couldnot use serializers - becouse it is rapidly calling operation and this images are temporary/
WPF cannot alter items that were created on another thread.
So if you create an ImageViewer on one thread, you cannot alter it's properties on another thread.
Instead, use the Dispatcher, which is WPF's internal message queue for the main UI thread, to update your objects.
Or, use Henk's Answer to do your work on another thread, but return the result to the main thread so it can update your ImageViewer's properties
You need at least these changes:
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
args.Result = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
if (args.Error != null)
{ ... } // handle error
else if (args.Cancelled)
{ ... } // handle Cancel
else
{
imageViewer.CurrentImage = args.Result;
}
pd.Close();
}
I'm not sure if it's enough but try again.
The imageViewer was created on the main thread of the application (which is appropriate because it is a UI control). UI controls can ONLY be accessed by the thread which created it, and that thread must have its own dispatcher (by which I mean message loop).
Remove the threading code, and it will work.
If you want this to popup the window and then show the image when the conversion completes, you will have to store the returned image in a variable until you return to the main thread, and then make the assignment to the imageViewer.