Async / await, create a new UI element error [duplicate] - c#

This question already has answers here:
The calling thread must be STA, because many UI components require this error In WPF. On form.show()
(3 answers)
Closed 6 years ago.
I have the following issue with my simplified code (WPF) below:
System.InvalidOperationException' in PresentationCore.dll
The calling thread must be STA, because many UI components require this
Would be so kind to help me to correct my code.
void CrearBtnNews()
{
KinectTileButton botontest = new KinectTileButton
{
Style = FindResource("KinectTileButtonStyle1") as Style,
Content = "WeB",
Height = 265,
Width = 450,
Background = null,
BorderBrush = null
};
botontest.Click +=
async (o, args) =>
{
await Task.Run(()=> BrowserAsync());
};
}
private void BrowserAsync()
{
Grid gridx = new Grid();//////// ERROR in this line ///////////////
System.Threading.Thread.Sleep(8000);
MessageBox.Show("working 8 seg");
}

All UI-related things must be done in the main UI thread. You are trying to create an UI element in a background thread, which is a no-go.
If you want to do some long calculations etc. in the background, you should do only that, and then return the data to the main thread and create the UI controls there.
Something like this: (ResultStruct is madeup)
button.Click += async(o,args) =>
{
ResultStruct data = await Task.Run(() => Browser());
Grid gridx = new Grid();
// set the data to the grid
};
private ResultStruct Browser()
{
// calculations, working ...
return data;
}
Also, method BrowserAsync is not actually async, you are just calling it in an async task, so I renamed it to just Browser.

There's no need to wrap the entire method in Task.Run. Instead, you should only be wrapping 'the work' in Task.Run, and handle the creation of UI components on the UI thread. Though, if the creation of components is a lot, you can also wrap that in an async called (as shown in the example).
Note: The reason why you are getting the error is because you are trying to create a UI component (the Grid) on a separate thread. All UI specific stuff must be created on a Dispatcher thread.
void CrearBtnNews()
{
KinectTileButton botontest = new KinectTileButton
{
Style = FindResource("KinectTileButtonStyle1") as Style,
Content = "WeB",
Height = 265,
Width = 450,
Background = null,
BorderBrush = null
};
botontest.Click += async (o, args) =>
{
Grid gridx = new Grid();
await BrowserAsync();
MessageBox.Show("working 8 seg");
};
}
private async Task BrowserAsync()
{
// Do work here
//
await Task.Run(() =>
{
System.Threading.Thread.Sleep(8000);
});
}

Related

Run Async/Await Task in BackgroundWorker's RunWorkerCompleted

I have a BackgroundWorker that runs a job generating a large amount of text.
When it's complete, I need it to execute an Async/Await Task Method, which writes and colorizes the text in a RichTextBox.
The Async/Await Task is to prevent the MainWindow UI thread from freezing while work is being calculated, such as searching and colorizing, for the RichTextBox.
Error
Exception: "The calling thread cannot access this object because a differnt thread owns it."
I get this error unless I put the Async/Await code inside a Dispatcher.Invoke.
But using a Dispatcher.Invoke seems to negate the Async/Await and cause the MainWindow UI thread to freeze.
C#
public void Generate()
{
// Background Worker
//
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(delegate (object o, DoWorkEventArgs args)
{
BackgroundWorker b = o as BackgroundWorker;
// Generate some text
// ...
});
// When Background Worker Completes Job
//
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate (object o, RunWorkerCompletedEventArgs args)
{
// Write and Colorize text in RichTextBox
Task<int> task = Display();
bw.CancelAsync();
bw.Dispose();
});
bw.RunWorkerAsync();
}
// Method that Writes and Colorizes text in RichTextBox in MainWindow UI
//
public async Task<int> Display()
{
int count = 0;
await Task.Run(() =>
{
// Problem here, it will only work inside a Disptacher
//Dispatcher.Invoke(new Action(delegate {
// Write text
Paragraph paragraph = new Paragraph();
richTextBox1.Document = new FlowDocument(paragraph);
richTextBox1.BeginChange();
paragraph.Inlines.Add(new Run("Test"));
richTextBox1.EndChange();
// Colorize Text here...
// Is a loop that takes some time.
// MainWindow UI freezes until it's complete.
//}));
});
return count;
}
I agree with others that the code would be cleaner once you replace BackgroundWorker with Task.Run. Among other things, it's much easier to compose with await (by "compose", I mean "do this thing; then do this other thing"). Note that you should use await instead of ContinueWith.
So your original code would end up looking something like this once the BGW is converted to Task.Run:
public string GenerateSomeText(CancellationToken token)
{
// Generate some text
}
public async Task GenerateAsync()
{
var cts = new CancellationTokenSource();
var result = await Task.Run(() => GenerateSomeText(cts.Token));
await DisplayAsync(result);
}
So, on to the issue that prompted this question in the first place: how to do lots of UI work without blocking the UI? Well, there isn't a great solution, because the work is UI work. So it can't be put on a background thread. If you have tons of UI work to do, the only real options are:
Virtualize your data. This way you only need to process the amount of UI you are displaying. This is the best solution to this problem.
If you don't want to put in the work to virtualize your data, then you can put in a hack where your code periodically pauses the UI work so that the UI remains responsive.
I do not believe the WPF RichTextBox supports virtualization, so you may need to go third-party for that. If you wanted to do the pause hack instead, you could do something like this:
public async Task<int> DisplayAsync(string text)
{
int count = 0;
// Write text
Paragraph paragraph = new Paragraph();
richTextBox1.Document = new FlowDocument(paragraph);
richTextBox1.BeginChange();
paragraph.Inlines.Add(new Run(text));
richTextBox1.EndChange();
// Colorize Text here...
// Is a loop that takes some time.
for (loop)
{
... // Colorize piece of text.
await Task.Delay(TimeSpan.FromMilliseconds(20));
}
return count;
}

The UI thread freezes when trying to update a control with Application.Current.Dispatcher.BeginInvoke

I've looked at dozens of different questions related to this issue and everyone seems to recommend what I'm already doing. I'm trying to figure what I'm doing wrong.
Here's the code, it's really simple:
new Thread(() =>
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
for (int i = 0; i < 50000; i++)
{
Rectangle rectangle = new Rectangle()
{
Fill = new SolidColorBrush(Colors.Gray),
Width = 200,
Height = 290,
Margin = new Thickness(5, 0, 5, 5)
};
Others.Children.Add(rectangle);
}
}));
}).Start();
Others is a WrapPanel.
<WrapPanel Name="Others" Orientation="Horizontal" />
based on the other threads I've seen, the UI thread should remain responsive as the rectangles are being created and added to the WrapPanel. But it doesn't happen. The UI hangs.
Any ideas on what I'm doing wrong?
After some testing, I got your above scenario working without blocking the UI.
Note that I was able to achieve this using async/await and Tasks. This allows the adding of children to be put into the ThreadPool of the application, allowing work to be executed once a thread is available. During this time, the message pumping still occurs, causing the UI thread not to block.
If you wrap your WrapPanel in a ScrollViewer, you can see that you are still able to scroll while new Rectangles are being added.
private async void OnMainWindowLoaded(object sender, RoutedEventArgs e)
{
for (var i = 0; i < 50000; i++)
{
var rect = new Rectangle
{
Fill = new SolidColorBrush(Colors.Gray),
Width = 200,
Height = 290,
Margin = new Thickness(5,0,5,5)
};
await Task.Run(async ()=> await AddChildAsync(rect));
}
}
private async Task AddChildAsync(Rectangle rect)
{
await Application.Current.Dispatcher.InvokeAsync(()=> Others.Children.Add(rect));
}
Another approach would be to not use Tasks all together, but instead, allow the Dispatcher to switch control to process events.
private async void OnMainWindowLoaded(object sender, RoutedEventArgs e)
{
for (var i = 0; i < 50000; i++)
{
var rect = new Rectangle
{
Fill = new SolidColorBrush(Colors.Gray),
Width = 200,
Height = 290,
Margin = new Thickness(5,0,5,5)
};
Others.Children.Add(rect);
await System.Windows.Threading.Dispatcher.Yield();
}
}
The new thread isn't really accomplishing anything useful. Because the thread does nothing other than to immediately call BeginInvoke(), all of the real work just winds up back on the dispatcher thread, where it blocks that thread until it's done.
You could, as one comment suggests refactor the loop so that the loop itself is in the thread, while the actual UI operations are not. That might look something like this:
new Thread(() =>
{
for (int i = 0; i < 50000; i++)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
Rectangle rectangle = new Rectangle()
{
Fill = new SolidColorBrush(Colors.Gray),
Width = 200,
Height = 290,
Margin = new Thickness(5, 0, 5, 5)
};
Others.Children.Add(rectangle);
}));
}
}).Start();
But I'm not sure that's really going to do what you want. It will queue up 50,000 separate invocations on the UI thread, all of which will definitely impede that thread's ability to handle other window messages, if not completely block any other work until they are all complete. The net effect will be very similar, if not identical, to what you're seeing now.
The other issue is that, this is WPF and you are apparently trying to create UI in code-behind.
It's not really clear how successful you're going to be getting 50,000 different Rectangle objects populated without some type of delay perceivable by the user. But you should definitely consider creating a view model type to represent the actual Rectangle objects, store them in an ObservableCollection<T>, or even just a plain List<T>, populate the collection in the background, and let WPF deal with creating the necessary Rectangle objects through a DataTemplate and an appropriate binding.
If you use that approach, using a List<T> you would just create the whole list, and then update a property exposing the list, which would avoid potential cross-thread issues when the collection is updated, as well as the per-rectangle cross-thread invocation cost.

WPF open new window on another thread

In the code below, I call method that opens a custom new window. However when application is doing some long running task I wish to still be able to activate the window. Is it possible to do it on another thread or by using the Task class?
public static class CustomW
{
static Custom_Window_Chrome_Demo.ThemedWindow MsgBox(string Msgbx_TTL, string Msgbx_Contnt)
{
var w_mbx = new Custom_Window_Chrome_Demo.ThemedWindow();
w_mbx.Width = 950; w_mbx.Height = 159;
w_mbx.Title = Msgbx_TTL;
Grid g = new Grid();
StackPanel spM = new StackPanel();
TextBlock TblckErrMsg = new TextBlock();
//more settings......
}
}
This is how I tried to invoke it,
public void newMsgBoxShow(string Msgbx_TTL, string Msgbx_Contnt)
{
System.Threading.Thread s = new System.Threading.Thread(
()=>
CustomW.MsgBox(Msgbx_TTL, Msgbx_Contnt).Show()
);
}
but when I am using the new thread I am getting the following error.
The calling thread must be STA, because many UI components require this
What is the correct way to achieve the required result ?
Use this:
Task.Factory.StartNew(new Action(() =>
{
//your window code
}), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
When new thread is created with Current Synchronization context it will be able to update the UI.(when current thread is UI thread)
You can also use dispatcher to execute your code.
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(()=>
{
//your window code
}));
Take a look at Dispatcher
https://msdn.microsoft.com/cs-cz/library/system.windows.threading.dispatcher%28v=vs.110%29.aspx
Dispatcher.CurrentDispatcher.Invoke(delegate { /* CODE */ }, DispatcherPriority.Normal);

How to run codes on background thread in Windows Runtime

I'm using incremental loading to show a ListView items. I run LoadDetails method in the background thread using Task.Run(...) to not busy the UI thread.
But it still blocks the UI thread and it doesn't render UI elements until it finishes the task.
executing LoadDetails method takes around 3 seconds to complete.
private async void LoadItemCounts(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (args.Phase != 6)
{
throw new Exception("Not in phase 6");
}
var item = args.Item as ItemModel;
var templateRoot = (Grid)args.ItemContainer.ContentTemplateRoot;
var textBlock = (TextBlock)templateRoot.FindName("textBlock");
await Task.Run(() => LoadDetails(textBlock, item.Id));
}
private async Task LoadDetails(TextBlock textBlock, string id)
{
int count = await DataSource.GetItemCounts(id);
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
textBlock.Text = count.ToString();
});
}
How to fix this so it doesn't block the UI thread? thanks.
(It's a Windows Phone Runtime app)
It's not clear from your question how you are measuring the 3 second delay. Is it that the call to GetItemCounts() itself takes 3 seconds? If so, isn't that to be expected? The delay is why you would execute that asynchronously in the first place, isn't it?
The code you posted doesn't really seem quite right. Since your new Task doesn't await the call to LoadDetails(), that task will finish right away, without any synchronization with the actual work being done. Written differently, you could also avoid having to call through the Dispatcher directly.
I would have written it something more like this:
private async void LoadItemCounts(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (args.Phase != 6)
{
throw new Exception("Not in phase 6");
}
var item = args.Item as ItemModel;
var templateRoot = (Grid)args.ItemContainer.ContentTemplateRoot;
var textBlock = (TextBlock)templateRoot.FindName("textBlock");
await LoadDetails(textBlock, item.Id);
}
private async Task LoadDetails(TextBlock textBlock, string id)
{
int count = await DataSource.GetItemCounts(id);
textBlock.Text = count.ToString();
}
I.e. as long as you keep awaiting on the UI thread, you don't need to invoke via the Dispatcher. Note that the above assumes you need the LoadDetails() method, presumably because you call it from multiple places and some require this particular implementation for some reason. But note that you could have just written the LoadItemCounts() method like this, and left out the LoadDetails() method altogether:
private async void LoadItemCounts(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (args.Phase != 6)
{
throw new Exception("Not in phase 6");
}
var item = args.Item as ItemModel;
var templateRoot = (Grid)args.ItemContainer.ContentTemplateRoot;
var textBlock = (TextBlock)templateRoot.FindName("textBlock");
textBlock.Text = (await DataSource.GetItemCounts(id)).ToString();
}
It looks like your code is correctly not blocking the UI thread by using await, but since LoadItemDetails() is presumably being called on the UI thread, it won't finish until the method is finished doing its work.
To fix this, just omit the await on the call to Task.Run(), so something like
Task.Run(() => LoadDetails(textBlock, item.Id));
should make LoadItemDetails() return immediately.

Custom ProgressBar Indicator not showing up before time consuming action

I implemented the custom progressbar indicator in my Windows Phone 8 project. It works fine if I try to toggle the indicator with a button. But of course I want it to show up while I perform time consuming actions (filling a list with many items). But as it blocks the UI the progressbar indicator doesn't show up before the action but only afterwards. I tried .UpdateLayout() on the indicator itself and the whole page before performing modifications to the list but none of it worked.
customIndeterminateProgressBar.Visibility = System.Windows.Visibility.Visible;
// add ~100 list items
customIndeterminateProgressBar.Visibility = System.Windows.Visibility.Collapsed;
Is there any other way to do this?
You could offload your time consuming work to a new task and add a continuation to set progress bar visibility at the end. Here i'm using the Task Parallel Library to achieve this:
customIndeterminateProgressBar.Visibility = System.Windows.Visibility.Visible;
Task.Run(() =>
{
// Do CPU intensive work
}).ContinueWith(task =>
{
customIndeterminateProgressBar.Visibility = System.Windows.Visibility.Collapsed;
}, TaskScheduler.FromCurrentSynchronizationContext());
You should run your heavy job asynchronously (more about async at MSDN and at the Stephen Cleary Blog) - so that it won't block UI.
The very simple example where you have a ProgressBar and a heavy Task which will inform PBar about its progress can look like this: (I've subscribed the start of the method to Button Click)
private async void StartBtn_Click(object sender, RoutedEventArgs e)
{
var progress = new Progress<double>( (p) =>
{
progresPB.Value = p;
});
await DoSomething(progress); // start asynchronously Task with progress indication
}
private Task<bool> DoSomething(IProgress<double> progress)
{
TaskCompletionSource<bool> taskComplete = new TaskCompletionSource<bool>();
// run your heavy task asynchronous
Task.Run(async () =>
{
for (int i = 0; i < 10; i++) // work divided into parts
{
await Task.Delay(1000); // some heavy work
progress.Report((double)i / 10);
}
taskComplete.TrySetResult(true);
});
return taskComplete.Task;
}

Categories

Resources