I have a control in my project that provides a busy indicator (rotating circle). I'd like it to run when a user selects a file to load data into a data grid. The busy indicator does not show up until my data grid is populated though. How can I get my busy indicator to show while the data is being retrieved? I believe I'm supposed to use a thread, but am not too knowledgeable with them yet and am trying to learn. I've tried many different ways and below is my latest attempt, which I do not know if I am anywhere close.
public void DoWork()
{
this.StartProgressBar();
Task.Factory.StartNew(() =>
{
UIDispatcher.Current.BeginInvoke(() =>
{
if (fileCompleted != null && !string.IsNullOrEmpty(fileCompleted.SelectedFile))
{
this.TestResults.Clear();
LoadedFileInfo info = this.Model.OpenTestCompleted(fileCompleted.SelectedFile);
foreach (var model in info.Sequence.Models)
{
foreach (var details in model.Tests)
{
this.TestResults.Add(new TestResultsModel(details, model.Name.Substring(0, model.Name.IndexOf('.'))));
}
}
}
});
});
}
private void StartProgressBar()
{
TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();
CancellationToken cancelationToken = new CancellationToken();
Task.Factory.StartNew(() => this.StopProgressBar()).ContinueWith(
m =>
{
this.ToggleProgressBar = true;
},
cancelationToken,
TaskContinuationOptions.None,
scheduler);
}
private void StopProgressBar()
{
this.ToggleProgressBar = false;
}
I really agree with #Ben, you should research how to use Tasks. You are creating background threads, and doing work on the UI thread in them anyway, which inevitably hold the UI thread. Try something simpler and see if it works. As far as your cancellation token goes, I don't see how and were you'd be able to reset it, as it is not a property in your class, so here's a sample without it..
How about something like this:
public void DoWork()
{
//done on the UI thread
this.ToggleProgressBar = true;
//done on the background thread, releasing UI, hence should show the progress bar
Task.Factory.StartNew(() =>
{
if (fileCompleted != null && !string.IsNullOrEmpty(fileCompleted.SelectedFile))
{
this.TestResults.Clear();
LoadedFileInfo info = this.Model.OpenTestCompleted(fileCompleted.SelectedFile);
foreach (var model in info.Sequence.Models)
foreach (var details in model.Tests)
this.TestResults.Add(new TestResultsModel(details, model.Name.Substring(0, model.Name.IndexOf('.'))));
}
//after all that (assumingly heavy work is done on the background thread,
//use UI thread to notify UI
UIDispatcher.Current.BeginInvoke(() =>
{
this.ToggleProgresBar = false;
}
});
}
Related
I have the following case:
A window is shown the the reference to that is stored in a utility class.
Later a modal dialog needs to appear above that window; so I am doing the following:
OptionalMessageBox message = new OptionalMessageBox(title, errorMessage.ToString(), MessageImage.Warning);
if (UIUtilities.TopWindow != null)
{
UIUtilities.TopWindow.Dispatcher.Invoke(() => message.Owner = UIUtilities.TopWindow);
UIUtilities.TopWindow.Dispatcher.Invoke(() => message.ShowDialog());
}
else
{
message.ShowDialog();
}
However this is give the classic 'The calling thread cannot access this object because a different thread owns it' though I don't understand why as I am using the dispatcher for the TopWindow variable. As a note (and out of desperation) I tried putting the calls on the message variable I just created - that didn't work either but I didn't expect that to be the problem as how can I now own it if I have just made it!
Any advice would be greatly appreciated.
Try this:
if (UIUtilities.TopWindow != null)
{
UIUtilities.TopWindow.Dispatcher.Invoke(() =>
{
var message = new OptionalMessageBox(title, errorMessage.ToString(), MessageImage.Warning);
message.Owner = UIUtilities.TopWindow;
message.ShowDialog();
});
}
else
{
var message = new OptionalMessageBox(title, errorMessage.ToString(), MessageImage.Warning);
message.ShowDialog();
}
You can use this
App.Current.Dispatcher.Invoke(() => {
OptionalMessageBox message = new OptionalMessageBox(title, errorMessage.ToString(), MessageImage.Warning);
message.Owner = App.Current.MainWindow;
message.ShowDialog();
});
I am using wpf busy indicator and setting its Isbusy property from viewModel. Aftersetting Isbusy property I want to filter my ICollectionview and push it on UI. This Filter operation I have put in
IsBusy = true;
await Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new System.Action(() =>
{
this.MyCollectionView.Filter = item =>
{
Iitem IdName = item as Iitem;
return Regex.IsMatch(IdName.Name, SearchText, RegexOptions.IgnoreCase);
};
}));
Workaround: If I put Task.Delay(200).Wait(); after setting IsBusy, busy indicator will be displayed for some cases.
Filter always runs on the UI thread, keeping it too busy to actually update the UI to reflect the IsBusy state.
In most cases, Filter functions should run fast enough that they don't need any special handling. However, if you're sure that you need to run a time-consuming filter, then the best way to do it is to split into two different collections: MyCollection and MyFilteredCollection. Then, any time MyCollection (or the filter) changes, do something like this:
IsBusy = true;
var items = MyCollection.ToList();
var filter = SearchText;
MyFilteredCollection = await Task.Run(() =>
items.Where(x => Regex.IsMatch(x.Name, filter, RegexOptions.IgnoreCase)));
IsBusy = false;
(this assumes that IsBusy will prevent MyCollection and SearchText from changing).
<form>
...
<button ng-click...>Save</button>
</form>
<overlay ng-if="saving" style="background-color: #ff0000">
<pindicator size="2em" color="red"></pindicator>
</overlay>
void handleOnSaveClicked(final html.Event e) {
_logger.info("Ready to save:",_tempJob);
saving = true;
// Angular starts to register Listener
_proxy.save(tempJob).then((_) {
// Template/Component has already received "detach"
_logger.info("Job saved!");
editable = false;
saving = false;
});
}
...
void detach() {
_logger.info("Detached!");
}
proxy.save saves tempJob into a list, this list is observed by Angular.
If this list changes Angular updates all the UI-Elements attached to the list.
saving=true should show a message but if proxy.save is so fast that Angular has no chance to register a Listener for "saving" (https://github.com/angular/angular.dart/blob/master/lib/core/scope.dart - Line 352). the whole UI-Block is remove while Angular tries to register the Listener.
This leads to
JobEdit: (15:19:43.096) Ready to save:
JobEdit: (15:19:43.119) Job saved!
JobEdit: (15:19:43.123) Detached!
'package:angular/core/scope.dart': Failed assertion: line 352 pos 12: 'isAttached' is not true.
Any hints to avoid this kind of "race condition"?
I haven't tried it but I think to remember that you should use VMTurnZone to execute async operations that affect the model.
class YourComponent implements ScopeAware {
Scope scope;
VmTurnZone zone;
YourComponent(this.zone);
void handleOnSaveClicked(final html.Event e) {
_logger.info("Ready to save:",_tempJob);
saving = true;
// Angular starts to register Listener
zone.run(() {
return _proxy.save(tempJob).then((_) {
// Template/Component has already received "detach"
_logger.info("Job saved!");
editable = false;
saving = false;
});
});
}
}
The main problem is replacing the list item with a new instance of your class (new object)
Angular tests the objects hashCode if you replace a list item.
If the hashCode is different it deletes the according component, but if the hashCode is the same
it updates the vars in your html-file.
Here it also deletes the component and replaces it with the new device:
...
// hashCode of devices[42] changes
devices[42] = new Device(new Location(40.123,10.567));
But this updates! your component:
...
// hashCode of devices[42] stays the same
final Device device = devices[42];
device.location = new Location(40.123,10.567);
Im using task process and and during the process I want to invoke the mouse
courser ,how should I do that ?
Task.Factory.StartNew(() =>
{
try
{
_isEnabled = false;
_canBack = false;
....
I've tried with the following which is not work...
System.Windows.Input.Cursors.Wait;
Set the window cursor on entry and exit of task. Since you can access window object only on UI thread so for accessing cursor property you have to delegate it on UI thread.
Task.Factory.StartNew(() =>
{
Application.Current.Dispatcher.Invoke(new Action(() =>
Cursor = Cursors.Wait));
Thread.Sleep(5000); // Some time consuming operation here.
Application.Current.Dispatcher.Invoke(new Action(() =>
Cursor = Cursors.Arrow));
});
You can try like following:
Application.Current.Dispatcher.Invoke((Action) (() => { Mouse.OverrideCursor = Cursors.Wait; }));
Good luck
I tried below snippet:
public Task RunUiTask(Action action)
{
var task = Task.Factory.StartNew(() =>
{
Dispatcher.Invoke(DispatcherPriority.Background, action);
});
return task;
}
private void OnCreateTask(object sender, RoutedEventArgs e)
{
var task = RunUiTask(() =>
{
for(int i=0;i<10;i++)
{
ResultTextBlock.Text += i.ToString();
}
});
task.Wait(); //(a) Program stopped here
ResultTextBlock.Text += "Finished"; //(b) Never called;
}
I couldn't understand why, when OnCreateTask (a button click event handler) is called, the program halts at (a), and (b) is never called.
Note: I know I can use Dispatcher.BeginInvoke to make program responsive, but this is not my concern here.
Can any body tell why the program halts at (a), and why (b) is never called? Thanks.
The call of
Dispatcher.Invoke(DispatcherPriority.Background, action);
will execute Action in your UI Thread and will return after Action is executed.
The Problem is, that your UI Thead is blocked because of the task.Wait() in your OnCreateTask, so the Action will never be executed and you have a Deadlock.
EDIT
Instead of your task.Wait() you should use a Continuation and Update ResultTextBlock.Text
task.ContinueWith(t=>{
ResultTextBlock.Text += "Finished";
}, TaskScheduler.FromCurrentSynchronizationContext());