WPF - UI not updating from Command - c#

I have a problem with my UI not updating while executing a Command.
I've got an indeterminate ProgressBar which has it's visibility bound to the IsBusyIndicator-property in the ViewModel. A Command should now excecute a method and show the ProgressBar while computing, as shown in the codesnippett below. However, this doesn't work as I'd expect. The property is set to the correct value but the UI doesn't update to show the ProgressBar.
It works fine if I just set the IsBusyIndicator to true and do nothing else in the Command, so INotifyPropertyChanged and the Binding are working correctly.
void CommandExecute()
{
IsBusyIndicator = true;
// Do stuff that takes long
IsBusyIndicator = false;
}
It seems to me that the UI waits for the Command to finish before it updates the View. Is there a way to force the UI to update right away?
Thanks in advance, may the force be with you.

You can try the following:
private async void CommandExecute()
{
IsBusyIndicator = true;
await Task.Run(() => DoWork());
IsBusyIndicator = false;
}
This makes DoWork() run on a separate thread. Generally, async void methods are to be avoided, but they're OK if they're handling a UI event.

As others said here, it doesn't work since you're probably doing your work on the UI thread.
Here are the technical stuff:
When PropertyChanged of INotifyPropertyChanged is raised, the binding is scheduling a handler by invoking it in the dispatcher with BeginInvoke, not immediately.
Since the UI thread is still running your long operation, handling the property change remains waiting on the dispatcher queue.
That's why the change happens only when the command is done.

Related

c# spawning process after Form Shown

When my main form loads I want to do some processing like loading the last client viewed. This extra processing causing the drawing of the form to be pretty ugly. I am doing it on form shown so I would expect that the form would be done painting.
I have used PostMessage... Is there a modern day version of PostMessage? During this processing I would like to set the hourglass cursor.
I think what you are trying to do is background processing. So you can use a BackgroundWorker class. When your form loads, you would start the background worker and when the worker is finished, it will notify your form through the RunWorkerCompleted event. You can even get progress updates from the worker by implementing ProgressChanged event.
How about using Task + ContinueWith. (If you upgrade to .Net 4.5 async/await would be a better choice).
private void Form_Load(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(5000); //Some long Running Jobs
return "Finished";
})
.ContinueWith(t =>
{
this.Text = t.Result; //Update GUI
},
TaskScheduler.FromCurrentSynchronizationContext());
}
I am not sure why would you want to use PostMessage. If you post a message using PostMessage it will eventually be obtained by GetMessage and unless the message is posted from a different thread whole exercise seems a little futile. Am I missing something here? As for BackgroundWorker please refer to the solution for Label is not set until method is done.

c# winforms events

In my c# winform app I have a button which when clicked, it performs some calculations which can tke some time.
I also have a label.visible = false which I would like to label.visible = true right after the button click so that the user can see the app working away.
The thing is even when label.visible = true is the first thing in the button1_Click(object sender, EventArgs e) method, it only toggles to visible at the very end, after the calculation is performed.
How can I show the label right after the button_click?
If you need your application to remain responsive whilst your app is processing, take a look at Background Worker
You can force your form to update & process any background messages by calling:
Application.DoEvents();
Just after you have changed your label - although this is probably a bit of a hacky solution.
Your calculation is being performed on the UI thread. This means that it is blocking the UI from refresshing after you have set the visibility of the label.
You should consider doing the calculation on another thread using a Task. This will allow the UI to be responsive during what it seems is a long operation that can run in the background.
e.g.
var taskCalc = Task.Factory.StartNew(() => //Do Calculation );
There are several ways to tackle this.
One way (the simplest) is to call
this.Refresh();
right after you set the Label to visible.
Another is to do your calculations in a background thread. The easiest way to do that is to use a BackgroundWorker. Then your main thread can just continue serving the UI (refreshing the form, responding to buttons etc.) while the background worker thread performs the computation.
See http://www.dotnetperls.com/backgroundworker for more on background worker threads.
Like others have said: You are performing your work on the UI Thread. I am showing you another valid way of achieving what you need. You can move your work to a separate thread using an anonymous delegate.
(new System.Threading.Thread(() =>
{
dowork(); // What ever work you need put here.
})).Start();

Start long process after modal dialog popup

I have a modal dialog with a cancel button only which pops up when the user clicks on a button. Aftre the modal dialog pops up, I would like to start a long process which monitors external event. If the event happens, then the dialog will be closed automatically. The user can cancel the monitoring process by clicking the cancel button.
I assigned the process start to the Shown event
private void ProceedForm_Shown(object sender, System.EventArgs e)
{
controller.StartSwiping();
}
The process itself is a loop
public void StartSwiping()
{
Status status;
do
{
status = CallForFeedback();
} while (status == Status.Pending);
form.DialogResult = DialogResult.OK;
form.Close();
}
The process starts fine, but the dialog does not pop up, so the user can non cancel the process. I also tried to assign the start to the Load event, but nothing changed.
Is there any way to Show the dialog and after that start the process?
Thanks
Your problem is that you are doing everything in the UI thread. You need to put you status monitoring loop in a separate thread so that the UI thread can remain responsive.
There are several ways you can do this, but one easy place to start is with the BackgroundWorker class
Use a Task to do your LongRunning events:
CancellationTokenSource _cancelationTokenSource = new CancellationTokenSource();
new Task(() =>
{
//Do LongRunning task
}, _cancelationTokenSource.Token, TaskCreationOptions.LongRunning).Start();
Use the _cancelationTokenSource to cancel the task when needed.
I would move the long running code onto a background thread as you are blocking the UI thread, which is why the UI never displays.
Use a background worker class for the controller functionality http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
When the work is completed on the background worker (i.e. the event is received) then you can use the following mechanism to callback onto the UI thread:
http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
Note: the article says you can turn off the crossthreadexception this would be considered bad practice, instead handle it the correct way using the InvokeRequired check and then invoke method on the windows form.
Others have suggested using a BackgroundWorker, or some other sort of background thread. While in many cases this is appropriate here, there is likely an even better solution. You're not just doing some long running task, you're waiting for something to happen. Rather than constantly polling...whatever it is, you should be using events. There should be an event that is triggered when you are done, and you should subscribe to that event to do whatever you need to do (i.e. close the dialog) when the correct conditions are met.

C#: Can I minimize app in BackgroundWorker running & update UI

I have a backgroundWorker thread running. At the end I update the UI, minimize the app & change the icon.
I tried to minimize the app in middle, but then the UI wasn't updated, nor the icon was changed. Thread was executed but this part wasn't done.
Can't the UI get updated when the app is minimized ? If can, where I may be going wrong.
UPDATED :
#Daniel, YesI am aware of it. And when the task is done I call ProgressChanged is called and finally RunWorkerCompleted is also called. BUT when I minimize the app in middle, the components like Label's aren't changed. Button text is changed, but it hangs/stucks - button doesn't work any longer. MY point is : If app is minimized during executing DoWork, then when ProgresChanged & RunCompleted is called or not. If called then why certain components aren't changed. NOTE: If I don't minimize in middle, then everything works perfectly fine.
You can only update the UI from the UI thread. The ProgressChanged and RunWorkerCompleted events are run on the UI thread, the DoWork event is not. This means, if you want to update your UI from a BackgroundWorker while it is running, you need to do it in the ProgressChanged event handler.
Are you using WPF or Winforms ?
In any case, if you want to do something with the UI, you will have to come back into the UI thread.
To do so, in WPF, you can use the dispatcher object, and invoke on it, so the method will be executed by the UI thread, and not by your worker thread.
In Winforms, you will have to maintain a reference to some UI control, and invoke on this control.
Consider the following code snippet (WPF) :
if (!myCheckBox.Dispatcher.CheckAccess())
{
myCheckBox.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
myCheckBox.IsChecked = true;
}
));
}
else
{
myCheckBox.IsChecked = true;
}
Here is another code snippet I use in Winforms, to raise my events from my UI thread
Friend Shared Sub RaiseUiEvent(ByVal hnd As EventHandler, ByVal sender As Object, ByVal e As EventArgs)
Dim uiRef = GlobalManager.GetInstance().UI
Try
uiRef.BeginInvoke(hnd, sender, e)
Catch ex As InvalidOperationException
''if UiRef is null, then we simply drop the event
Logger.AddTextToLog(ex)
Debug.WriteLine("Invalid operation in raiseUiEvent")
Catch ex As NullReferenceException
Logger.AddTextToLog(ex)
Debug.WriteLine("Null Reference in RaiseUiEvents")
End Try
End Sub

How do I "Thread.Join" a BackgroundWorker?

Say I'm showing the user a form, and using a BackgroundWorker to do some work behind the scenes.
When the user clicks OK, I cannot continue until the BackgroundWorker has completed.
If it hasn't finished when the user clicks Ok, I want to show a WaitCursor until it has, and then continue.
What's the best way to implement this?
I know I could use a plain old Thread, and then do Thread.Join, but I like BackgroundWorker.
Can it be done nicely?
You could use this code, instead of BW
var resetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(5000);//LongRunning task, async call
resetEvent.Set();
});
resetEvent.WaitOne();// blocking call, when user clicks OK
Ideally, you should disable all the relevant controls on the form, then re-enable them when the BackgroundWorker completes. That way you won't get a locked UI - and you can have a cancel button etc.
If you wanted to actually block until it had finished, you could just run the task synchronously to start with.
You could check is the BackgroundWorker is running with a check in the btnOK_Click event handler to see if bw.IsBusy is true. If so change the cursor and set a boolean flag that indicates that OK handler is waiting. When BackgroundWorker finishes its job check if ok had been waiting, if so, set the flag to false change the cursor and do the job of the OK button. :-)
I'd just use boolean flags and a lock.
object waitlock = new object();
bool isBackgroundWorkerCompleted = false;
bool isOKButtonPressed = false;
private void onPress(...)
{
lock(waitlock)
{
isOKButtonPressed = true;
if (isBackgroundWorkerCompleted) DoNextStep();
else cursor.Wait(); //psuedo-code - use whatever the actual call is...
}
}
Background thread does the same trick (just remember to BeginInvoke back to the main UI thread)

Categories