I want to show other form with loading gif while executing something async. Any ideas why first TowerPinsVisu is executing and after that the ShowDialog?
private void RunTasks()
{
var tskShowLoadingForm=new Task(() => {
loadingForm.ShowDialog("OPTIMIZING...");
});
tskShowLoadingForm.Start();
var tskVisualizeTowerPins = new Task(() => TowerPinsVisu());
tskVisualizeTowerPins.Start();
}
public void ShowDialog(string text)
{
lblLoadingInfo.Dispatcher.Invoke(() => lblLoadingInfo.Content = text);
this.Dispatcher.Invoke(()=> ShowDialog());
}
Without changing your flow, you have to run TowerPinsVisu()on a background thread using Task.Run. While this thread is running you can then show the dialog.
You can attach a continuation to the Task, which will be executed once the Task has run to completion. By passing the UI thread's SynchronizationContext to the contuation, you can force this continuation to execute on the UI thread (which is necessary to close the dialog:
// If this method executes on the UI thread...
private void RunTasks()
{
// Start a background thread and continue immediately to show the modal screen.
Task.Run(TowerPinsVisu)
.ContinueWith(task => loadingForm.Close(), TaskScheduler.FromCurrentSynchronizationContext());
loadingForm.ShowDialog("OPTIMIZING...");
}
// ...then this method will too => No need to use a Dispatcher at this point
public void ShowDialog(string text)
{
lblLoadingInfo.Content = text;
ShowDialog();
}
Related
I have a console application where in some instances a user interface needs to be presented. This user interface needs to remain responsive as it will contain a loading gif, progress bar, cancel button etc. I have the following sample code:
class Program
{
static void Main(string[] args)
{
DoWork().GetAwaiter().GetResult();
}
private static async Task DoWork()
{
TestForm form = new TestForm();
form.Show();
string s = await Task.Run(() =>
{
System.Threading.Thread.Sleep(5000);
return "Plop";
});
if (s == "Plop")
{
form.Close();
}
}
}
I would expect from the code above for the TestForm to be displayed for approximately 5 seconds before being closed due to the value of the string being "Plop", however all that happens is the Task is run and the if statement is never reached. Furthermore the UI of the TestForm does not remain responsive. What is wrong with this code?
So I've managed to hack together a dirty solution for this. It is not a clean solution so I'm still open to suggestions but for what I need it works fine
private static void DoWork()
{
TestForm form = new TestForm();
Task formTask = Task.Run(() => form.ShowDialog());
Task<string> testTask = Task.Run(() =>
{
for (int i = 1; i < 10; i++)
{
Thread.Sleep(1000);
Console.WriteLine(i.ToString());
}
Console.WriteLine("Background task finished");
return "Plop";
});
Console.WriteLine("Waiting for background task");
testTask.Wait();
if (testTask.Result == "Plop")
{
Dispatcher.CurrentDispatcher.InvokeAsync(() => form.Close());
}
Console.WriteLine("App finished");
}
This outputs 'Waiting for background task' first, followed by the number count of the Task and then outputs 'Background task finished' when the long process is complete, as well as closes the responsive UI form
Its a classic deadlock.When your code hit await ,control goes back to main thread which is a blocking wait for DoWork GetResult(); When Task.Run thread is finished controls tries to go back to main thread but its waiting for DoWork to be finished. That is the reason last If statement never executes.
But apart from deadlock ,there is also one more issue in your code which will make your UI freeze.Its the form.Show() method.If you remove everything related to async-await and only use form ,it will still freeze.The problem is Show method expects a windows message loop which will be provided if you create a Windows.Forms application but here you are launching form from console application which doesnt have a message loop. One solution would be to use form.ShowDialog which will create its own message loop. Another solution is to use System.Windows.Forms.Application.Run method which provides a win messages loop to the form created through thread pool thread. I can give you one possible solution here but its up to you how you structure your code as the root cause is identified.
static void Main(string[] args)
{
TestForm form = new TestForm();
form.Load += Form_Load;
Application.Run(form);
}
private static async void Form_Load(object sender, EventArgs e)
{
var form = sender as Form;
string s = await Task.Run(() =>
{
System.Threading.Thread.Sleep(5000);
return "Plop";
});
if (s == "Plop")
{
form?.Close();
}
}
Ok I did mark my first answer to be deleted, since what I put there works for WPF and not for you require, BUT in this one is doing what you asked, I did try it and opens the WinForm then closes after 5 seconds, here is the code:
static void Main(string[] args)
{
MethodToRun();
}
private static async void MethodToRun()
{
var windowToOpen = new TestForm();
var stringValue = String.Empty;
Task.Run(new Action(() =>
{
Dispatcher.CurrentDispatcher.InvokeAsync(() =>
{
windowToOpen.Show();
}).Wait();
System.Threading.Thread.Sleep(5000);
stringValue = "Plop";
Dispatcher.CurrentDispatcher.InvokeAsync(() =>
{
if (String.Equals(stringValue, "Plop"))
{
windowToOpen.Close();
}
}).Wait();
})).Wait();
}
I am having difficulty updating the UI using Xamarin. The objective is to make reactive UI so the user knows that application is thinking. Below are my attempts.
Attempt 1
private void BeginProcess(string fileName)
{
Device.BeginInvokeOnMainThread(() => {
iContent.Text = "UI Successfully updated.";
});
Device.BeginInvokeOnMainThread(() => {
ProccessFolder testMethod = new ProccessFolder.Initialize(fileName);
});
}
Attempt 2
private void UpdateUI () {
iContent.Text = "UI Successfully updated.";
}
private void BeginProcess(string fileName)
{
System.Threading.Thread t = new System.Threading.Thread(UpdateUI);
t.Priority = System.Threading.ThreadPriority.Highest;
t.IsBackground = false;
t.Start();
Device.BeginInvokeOnMainThread(() => {
ProccessFolder testMethod = new ProccessFolder.Initialize(fileName);
});
}
Attempt 3
private void BeginProcess(string fileName)
{
Device.BeginInvokeOnMainThread(() => {
iContent.Text = "UI Successfully updated.";
});
Task.Delay(5000);
Device.BeginInvokeOnMainThread(() => {
ProccessFolder testMethod = new ProccessFolder.Initialize(fileName);
});
}
Unfortunately none of these work. What does work is if I place the ProcessFolder method in a background thread and invoke the UI changes on the main thread. However the completion time of the ProcessFolder method is slower.
Any suggestion on how I can update the UI while still executing ProcessFolder on the main thread?
Sometimes when your try update something on the main ui from within a method, depending on the way you've written (and/or structured) it can mean that the main dispatcher waits for the method to complete before updating the main ui.
If you were to try the following example it would successfully complete a UI update after each foreach iteration because when it completes it's initial task it wants to return out of the for loop and hits our main thread invoke, which HAS to complete before the loop can iterate again.
private void BeginProcess()
{
Task.Run(()=>{
for (int i = 0; i < 100; i++)
{
// Perform a task
BeginInvokeOnMainThread(() => {
iContent.Text = "UI Successfully updated: " + i " times out of 100";
});
}
})
}
You can sometimes replicate this kind of effect by using:
NSRunLoop.Current.RunUntil(1000);
to allow the UI to catch up when you call the 'BeginInvokeOnMainThread' delegate.
First of all, all the UI updates must be done in the main thread.
For your particular problem maybe you could use async/await (https://msdn.microsoft.com/library/hh191443(vs.110).aspx)
You could do something like in the main thread:
ProccessFolder testMethod = await ProccessFolder.Initialize(fileName);
iContent.Text = "UI Successfully updated.";
You have to make the Initialize method async and to return a task
I have a form that shows a data grid. I also have a method running on a different thread that updates only the displayed cells of the grid. To do this, this method calls a function on the form that returns the displayed cells.
The problem I have is that sometimes while the form has been closed and disposed the method on the other thread is still calling this function which results in an objectdisposed exception. Is there a way (other then making sure the methode on the other thread is finished) to prevent this?
So I need a thread safe method to kill the background task when the form is closed.
private delegate List<foo> GetShownCellsDelegate();
public List<foo> GetShownCells()
{
if (this.InvokeRequired)
{
GetShownCellsDelegate getShownCellsDelegate = new GetShownCellsDelegate(GetShownCells);
return (List<foo>)this.Invoke(getShownCellsDelegate);
}
else
{
//do stuff
}
}
I tries using the IsDisposed property of the form:
if (!IsDisposed)
{
return (List<foo>)this.Invoke(getShownCellsDelegate);
}
But apparently the form can be dispossed after the if statement because I still get the isdisposed exception.
This is how I use the function on the other thread:
private CancellationTokenSource cts = new CancellationTokenSource();
public void CancelUpdate()
{
cts.Cancel();
}
public void ReadDataFromDevice()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ReadAllDataThreadPoolMethod));
}
private void ReadAllDataThreadPoolMethod(Object stateInfo)
{
if (!cts.IsCancellationRequested)
{
//do stuff
}
}
The CancelUpdate method is called from the IsClosing event on the form. But I still get the isdisposed exception sometimes.
To cancel the long running operation you can use a CancellationToken, which is specifically designed for cooperative cancellation.
Have the main form create a CancellationTokenSource when starting the background thread, pass the CacellationToken generated by the CTS to the backround thread, cancel the CTS when your form closes, and then have the background thread check the token to see if it is cancelled before trying to invoke back to the main thread.
public void Foo()
{
var cts = new CancellationTokenSource();
var task = Task.Run(() => DoWork(cts.Token));
FormClosing += (s, args) =>
{
cts.Cancel();
if (!task.IsCompleted)
{
args.Cancel = true;
task.ContinueWith(t => Close());
}
};
}
private void DoWork(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
//Do some work
}
}
To be absolutely sure that the background thread doesn't pass the cancellation check, then yield to the UI thread to have it cancel the token and dispose of the form, before the work is done, you'll also need to ensure that the background thread has time to run to completion after being cancelled, before the form closes. This can be done through a simple Thread.Join call in the closing handler.
this.FormClosed += new FormClosedEventHandler(form1_FormClosed);
void form1_FormClosed(object sender, FormClosedEventArgs e)
{
//close thread
}
This will be executed whenever your form is being closed.
I'm using a custom TaskScheduler to execute a task queue in serial. The task is supposed to display a window and then block until the window closes itself. Unfortunately calling Window.ShowDialog() doesn't seem to block so the task completes and the window never displays.
If I put a breakpoint after the call to ShowDialog I can see the form has opened but under normal execution the Task seems to end so quickly you cant see it.
My TaskScheduler implementation taken from a previous question:
public sealed class StaTaskScheduler : TaskScheduler, IDisposable
{
private readonly List<Thread> threads;
private BlockingCollection<Task> tasks;
public override int MaximumConcurrencyLevel
{
get { return threads.Count; }
}
public StaTaskScheduler(int concurrencyLevel)
{
if (concurrencyLevel < 1) throw new ArgumentOutOfRangeException("concurrencyLevel");
this.tasks = new BlockingCollection<Task>();
this.threads = Enumerable.Range(0, concurrencyLevel).Select(i =>
{
var thread = new Thread(() =>
{
foreach (var t in this.tasks.GetConsumingEnumerable())
{
this.TryExecuteTask(t);
}
});
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
return thread;
}).ToList();
this.threads.ForEach(t => t.Start());
}
protected override void QueueTask(Task task)
{
tasks.Add(task);
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return tasks.ToArray();
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return Thread.CurrentThread.GetApartmentState() == ApartmentState.STA && TryExecuteTask(task);
}
public void Dispose()
{
if (tasks != null)
{
tasks.CompleteAdding();
foreach (var thread in threads) thread.Join();
tasks.Dispose();
tasks = null;
}
}
}
My Application Code:
private StaTaskScheduler taskScheduler;
...
this.taskScheduler = new StaTaskScheduler(1);
Task.Factory.StartNew(() =>
{
WarningWindow window = new WarningWindow(
ProcessControl.Properties.Settings.Default.WarningHeader,
ProcessControl.Properties.Settings.Default.WarningMessage,
processName,
ProcessControl.Properties.Settings.Default.WarningFooter,
ProcessControl.Properties.Settings.Default.WarningTimeout * 1000);
window.ShowDialog();
}, CancellationToken.None, TaskCreationOptions.None, this.taskScheduler);
Nothing obviously wrong. Except what is missing, you are not doing anything to ensure that an exception that's raised in the task is reported. The way you wrote it, such an exception will never be reported and you'll just see code failing to run. Like a dialog that just disappears. You'll need to write something like this:
Task.Factory.StartNew(() => {
// Your code here
//...
}, CancellationToken.None, TaskCreationOptions.None, taskScheduler)
.ContinueWith((t) => {
MessageBox.Show(t.Exception.ToString());
}, TaskContinuationOptions.OnlyOnFaulted);
With good odds that you'll now see an InvalidOperationException reported. Further diagnose it with Debug + Exceptions, tick the Thrown checkbox for CLR exceptions.
Do beware that this task scheduler doesn't magically makes your code thread-safe or fit to run another UI thread. It wasn't made for that, it should only be used to keep single-threaded COM components happy. You must honor the sometimes draconian consequences of running UI on another thread. In other words, don't touch properties of UI on the main thread. And the dialog not acting like a dialog at all since it doesn't have an owner. And it thus randomly disappearing behind another window or accidentally getting closed by the user because he was clicking away and never counted on a window appearing from no-where.
And last but not least the long-lasting misery caused by the SystemEvents class. Which needs to guess which thread is the UI thread, it will pick the first STA thread. If that's your dialog then you'll have very hard to diagnose threading problems later.
Don't do it.
Apparently, you're using Stephen Toub's StaTaskScheduler. It is not intended to run tasks involving the UI. Essentially, you're trying to display a modal window with window.ShowDialog() on a background thread which has nothing to do with the main UI thread. I suspect the window.ShowDialog() instantly finishes with an error, and so does the task. Await the task and observe the errors:
try
{
await Task.Factory.StartNew(() =>
{
WarningWindow window = new WarningWindow(
ProcessControl.Properties.Settings.Default.WarningHeader,
ProcessControl.Properties.Settings.Default.WarningMessage,
processName,
ProcessControl.Properties.Settings.Default.WarningFooter,
ProcessControl.Properties.Settings.Default.WarningTimeout * 1000);
window.ShowDialog();
}, CancellationToken.None, TaskCreationOptions.None, this.taskScheduler);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message)
}
If you really want to show a window from a background STA thread, you need to run a Dispatcher loop:
var task = Task.Factory.StartNew(() =>
{
System.Windows.Threading.Dispatcher.InvokeAsync(() =>
{
WarningWindow window = new WarningWindow(
ProcessControl.Properties.Settings.Default.WarningHeader,
ProcessControl.Properties.Settings.Default.WarningMessage,
processName,
ProcessControl.Properties.Settings.Default.WarningFooter,
ProcessControl.Properties.Settings.Default.WarningTimeout * 1000);
window.Closed += (s, e) =>
window.Dispatcher.InvokeShutdown();
window.Show();
});
System.Windows.Threading.Dispatcher.Run();
}, CancellationToken.None, TaskCreationOptions.None, this.taskScheduler);
Note however, this window will not be modal as to any main UI thread window. Moreover, you'd block the StaTaskScheduler loop, so its other scheduled tasks won't run until the window is closed and Dispatcher.Run() exits.
I had a similar problem where on start up I could not manage to block execution via a ShowDialog. Eventually I discovered that inside the class properties were Command Line Arguments that was used to automatically log in with appropriate credentials. This saved time during development not to enter a username and password every time you compile. As soon as I removed that the Dialog behaved as expected so it's worth while to search for processes that might interfere with the normal execution path.
I connect to a webserive. While the webservice is connected i want to have a waiting form with an animated gif inside of it. The waiting form is correctly displayed but the animated give is not animated it is fixed.
Can anybody help me. I have already tried : DoEvents but the gif is still not animated.
// Create the new thread object
Thread NewThread = new Thread(new ThreadStart(RunThread));
// Start the new thread.
NewThread.Start();
// Inform everybody that the main thread is waiting
FRM_Wait waitingDialog = new FRM_Wait();
waitingDialog.Show();
waitingDialog.Activate();
Application.DoEvents();
// Wait for NewThread to terminate.
NewThread.Join();
// And it's done.
waitingDialog.Close();
MessageBox.Show("Upload erfolgreich erledigt.", "Upload Erfolgreich",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
public void RunThread()
{
mfsportservicedev.ServiceSoapClient servicedev = new mfsportservicedev.ServiceSoapClient();
int status = servicedev.addEvent(videosNames, videos);
}
Don't call Join on a thread from within the UI thread. Instead, disable any controls you don't want to act on until the task has completed (e.g. buttons) and then call back into the UI thread when the operation has completed - so move the "And it's done" code into a new method which is invoked at the end of the operation. If you're using .NET 4, I'd suggest using the TPL for this, as it makes it easier to represent "a task which is in progress" and to add a continuation to it. (It's also a good start for what will become the idiomatic way of doing async operations in .NET 4.5.)
The problem is coming from your join. join is synchronous, so basically you are making your UI wait till the thread finishes its work.
You want to use a callback function to come back to your UI.
Edit : ive been skeetified
You problem is here:
NewThread.Join();
This blocks the UI thread until NewThread ends.
Here's one way to do it:
private myDelegate;
// ...
myDelegate = new Action(RunThread);
myDelegate.BeginInvoke(new AsyncCallback(MyCallback),null);
// You RunThread method is now running on a separate thread
// Open your wait form here
// ...
// This callback function will be called when you delegate ends
private void MyCallback(IAsyncResult ar)
{
myDelegate.EndInvoke(ar);
// Note this is still not the UI thread, so if you want to do something with the UI you'll need to do it on the UI thread.
// using either Control.Invoke (for WinForms) or Dispatcher.Invoke (for WPF)
}
Thread.Join is a blocking call that does not pump messages so that is your problem. It is typically advised to avoid calling any kind of synchronization mechanism that causes the UI thread to block.
Here is a solution using the Task class and the Invoke marshaling technique.
private void async InitiateWebService_Click(object sender, EventArgs args)
{
FRM_Wait waitingDialog = new FRM_Wait();
waitingDialog.Show();
Task.Factory.StartNew(
() =>
{
mfsportservicedev.ServiceSoapClient servicedev = new mfsportservicedev.ServiceSoapClient();
int status = servicedev.addEvent(videosNames, videos);
waitingDialog.Invoke(
(Action)(() =>
{
waitingDialog.Close();
}));
});
}
Here is a solution using a raw Thread.
private void async InitiateWebService_Click(object sender, EventArgs args)
{
FRM_Wait waitingDialog = new FRM_Wait();
waitingDialog.Show();
var thread = new Thread(
() =>
{
mfsportservicedev.ServiceSoapClient servicedev = new mfsportservicedev.ServiceSoapClient();
int status = servicedev.addEvent(videosNames, videos);
waitingDialog.Invoke(
(Action)(() =>
{
waitingDialog.Close();
}));
});
thread.Start();
}
C# 5.0 makes this kind of pattern even easier with its new async and await keywords1.
private void async InitiateWebService_Click(object sender, EventArgs args)
{
FRM_Wait waitingDialog = new FRM_Wait();
waitingDialog.Show();
await Task.Run(
() =>
{
mfsportservicedev.ServiceSoapClient servicedev = new mfsportservicedev.ServiceSoapClient();
int status = servicedev.addEvent(videosNames, videos);
});
waitingDialog.Close();
}
1Not yet released.