create Wpf user controls in other thread - c#

I am trying to create some UserControl(s) using another thread, and I am using code like this:
private void btnDemo_Click(object sender, RoutedEventArgs e)
{
Task tsk = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 3; i++)
{
MyControl sprite = new MyControl();
pnlTest.Children.Add(sprite);
}
});
}
But I am getting this exception in the UserControl constructor:
The calling thread must be STA, because many UI components require this.
I am not sure that I am using the right approach to do this. Please, Can you help me with this.
thanks.

The creating of the controls can be done on any Thread but Adding them to the GUI needs to be synchronized to the main Thread.
In this case, just 3 controls, forget about Tasks and just do it directly, single-threaded.

You can dispatch the operation of adding controls to the Children collection to the UI thread using Dispatcher:
private void btnDemo_Click(object sender, RoutedEventArgs e)
{
Task tsk = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 3; i++)
{
Dispatcher.BeginInvoke(new Action(() => {
MyControl sprite = new MyControl();
pnlTest.Children.Add(sprite);
}));
}
});
}
By calling BeginInvoke on Dispatcher you basically adding the operation to the queue to execute on the UI thread.

Related

Get values from UI thread from cross thread

I am trying to add threading into my program such that I don't freeze the entire main UI thread while doing "expensive" computational work.
Currently, my program runs an async Task pointing to a function named startWork() when a button is pressed like so:
async void startParse_Click(object sender, EventArgs e)
{
await Task.Run(() => startWork());
}
Normally for setting values I do the following:
niceButton.BeginInvoke(new MethodInvoker(() =>
{
niceButton.Text = "new text";
}));
However, for grabbing data from controls and using that data outside of the MethodInvoker, I'm having a bit of trouble. My goal is execute a foreach loop around my listView1.Items, outside of the UI thread.
Here are the contents of startWork():
void startWork()
{
// Naturally I cannot reference my listView1 control because it is in a
// different thread and is blocked the the "illegal" cross-thread check
int overallProgress = 0;
ListView.ListViewItemCollection items = null;
// This unfortunately doesn't work (MethodInvoker is in a different scope?)
listView1.BeginInvoke( new MethodInvoker(() => {
items = listView1.Items;
}));
int totalItems = items.Count; // "items" isn't recognized
foreach (ListViewItem files in items )
{
// slowwww work
}
}
I have also tried passing the ListView.ListViewItemCollection as an argument to the function with no avail.
Continuing to get Cross-thread operation not valid: accessed from a thread other than the thread it was created on
Note: The target framework is .NET 4.7 -- perhaps there is a better/more efficient method in newer versions of .NET?
I may just lack fundamental understanding of async/tasks, but I presume I am overlooking something important.
UI elements, including ListView.ListViewItemCollection and ListViewItem are "thread affine." This means they can only be accessed on the UI thread.
To do background work, you should only pass non-thread-affine objects. E.g., a List<string>, not a ListViewItemCollection or a List<ListViewItem>.
async void startParse_Click(object sender, EventArgs e)
{
var items = listView1.Items;
var data = /* extract List<string> from items */
await Task.Run(() => startWork(data));
}
void startWork(List<string> files)
{
int overallProgress = 0;
foreach (var file in files)
{
// slowwww work
}
}
Also, you shouldn't be using BeginInvoke. Use IProgress<T> with Progress<T> instead.
You don't have to iterate over items in a worker thread, as switching from one item in collection to another is pretty fast and do not freezes UI. Just move you "expensive" computational work to worker thread:
private async void StartParseButtonClick(object sender, EventArgs e)
{
// disable button (we are on UI thread)
var startParseButton = sender as Button;
startParseButton.Enabled = false;
try
{
// copy just in case if someone will add new item while we iterating over
var items = listView1.Items.OfType<ListViewItem>().ToList();
foreach (var item in items)
await Parse(item); // this will be invoked in worker thread
}
finally
{
// enable button finally (we are on UI thread)
startParseButton.Enabled = true;
}
}
private async Task Parse(ListViewItem item)
{
// slowwww work (we are on worker thread)
await Task.Delay(500);
}

Dispatcher.BeginInvoke() not running asynchronously

Here's a simplified version of what I want to do:
onClick a button, a aNewMethod() would run asynchronously to keep UI responsive. That's it!
I've read some answers and here's what i could come up with :
private async void button_Click(object sender, RoutedEventArgs e)
{
Task task = Task.Run(() => aNewMethod());
await task;
}
private void aNewMethod()
{
if (progress.Value == 0)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Value = 100;
}
}
As you may have thought, this throws a System.InvalidOperationException at if(progress.Value== 0) saying :
The calling thread cannot access this object because a different
thread owns it.
after some Googling, I've read that I need a Dispatcher.BeginInvoke() method to update/use UI controls, so I did this :
private void aNewMethod()
{
Dispatcher.BeginInvoke((Action)delegate {
if (progress.Value == 0)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Value = 100;
}
});
}
This solved the System.InvalidOperationException but it's not running asynchronously as the UI still freezes at for loop
So the question is : How to run the aNewMethod(); asynchronously and still update and interact with UI controls ?
The Dispatcher runs in the UI thread. It handles your UI, and executes actions you pass to it via BeginInvoke etc.
But it can only handle one thing at a time; when it's busy handling your action, it won't update the UI in the meantime, so the UI freezes in the meantime.
If you want to keep your UI responsive, you'd need to run the heavy load functions in a seperate, non-UI thread. Within those functions running on another thread, you can call the dispatcher whenever you need access to the UI - ideally, only very briefly for the purpose of updating UI elements.
So in other words, you'd want to be running your sleep function in a seperate thread, and then just make a call to the Dispatcher from your own thread when you need to set the progress value. Something like
private void button_Click(object sender, RoutedEventArgs e)
{
Task task = Task.Run(() => aNewMethod()); // will call aNewMethod on the thread pool
}
private void aNewMethod()
{
double progressValue = 0;
Dispatcher.Invoke(() => progressValue = progress.Value);
if (progressValue == 0)
{
Thread.Sleep(3000); // still executes on the threadpool (see above), so not blocking UI
Dispatcher.Invoke(() => progress.Value = 100 ); // call the dispatcher to access UI
}
}
With you current implementation, there is no need to use Thread.Start, as in that method you are just sleeping it for some time and accessing UI thread objects there which is not allowed
.In your scenario a better way of doing is that you should not use Thread.Sleep instead of that you should be doing it with Task.Delay like:
private async void button_Click(object sender, RoutedEventArgs e)
{
if (progress.Value == 0)
{
await Task.Delay(3000);
progress.Value = 100;
}
}
Now you don't need Dispatcher.Invoke, and credit goes to async and await keywords as statements after await will be executing in the same calling synchronization context from where we did async call which is UI thread in this case.
onClick a button, a aNewMethod() would run asynchronously to keep UI responsive.
Actually, it's running synchronously on a background thread. Task.Run is perfectly appropriate for this.
after some Googling, I've read that I need a Dispatcher.BeginInvoke() method to update/use UI controls
Unfortunately, this is one area where Google will certainly mislead you. Dispatcher is not the best solution here.
Instead, you should use IProgress<T>/Progress<T>, as such:
private async void button_Click(object sender, RoutedEventArgs e)
{
var progress = new Progress<int>(value => { this.progress.Value = value; });
await Task.Run(() => aNewMethod(progress));
}
private void aNewMethod(IProgress<int> progress)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Report(100);
}

Making a progress bar update in real time in wpf

I'm having some trouble making the progress bar show the updates in real time.
This is my code right now
for (int i = 0; i < 100; i++)
{
progressbar1.Value = i;
Thread.Sleep(100);
}
But for some reason the progress bar shows empty when the function runs, and then nothing until the function finishes running. Can someone explain to me how this can be done? I'm new to C#/WPF so I'm not 100% sure on how I would implement a Dispatcher on a different thread (as seen on some other posts) to fix this problem.
To clarify, my program has a button which when press, grabs the value from a textbox, and uses an API to retrieve info, and create labels based on it. I want the progress bar to update after every row of data is finished processing.
This is what I have right now:
private async void search(object sender, RoutedEventArgs e)
{
var progress = new Progress<int>(value => progressbar1.Value = value);
await Task.Run(() =>
{
this.Dispatcher.Invoke((Action)(() =>
{
some pre-processing before the actual for loop occur
for (int i = 0; i < numberofRows; i++)
{
label creation + adding
((IProgress<int>)progress).Report(i);
}
}));
});
}
Thank you!
If you are using .NET 4.5 or later, you can use async/await:
var progress = new Progress<int>(value => progressBar.Value = value);
await Task.Run(() =>
{
for (int i = 0; i < 100; i++)
{
((IProgress<int>)progress).Report(i);
Thread.Sleep(100);
}
});
You need to mark your method with async keyword to be able to use await, for example:
private async void Button_Click(object sender, RoutedEventArgs e)
Managed to make it work. All I needed to do is instead of making it just
progressBar1.value = i;
I just had to do
progressbar1.Dispatcher.Invoke(() => progressbar1.Value = i, DispatcherPriority.Background);
You should use BackgroundWorker included in .NET, which provides you with methods for reporting the progress of a background thread in an event. The thread which created the BackGroundWorker automatically calls this event.
The BackgroundWorker.ProgressChanged can be used to report the progress of an asynchronous operation to the user.
// This event handler updates the progress bar.
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
Refer to MSDN for more information about using this.

Update UI control in Task

Does it right code, if I update my UI controls in task like this?
Or it's wrong, and I need use smth like Control.Invoke?
private async void button1_Click(object sender, EventArgs e)
{
textBox1.Text = await Task<string>.Factory.StartNew(() =>
{
Foo();
return "Completed";
});
}
private void Foo()
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
Thread.Sleep(1000);
}
}
The BCL addresses this specific scenario with the IProgress interface, implemented in the Progress class, to provide rich asynchronous progress reporting. This is available in .NET 4.5, or .NET 4 with the BCL Portability Nuget package. A lot of BCL classes accept an IProgress parameter for progress reporting.
Servy's answer addresses the immediate problem of how to update the UI after an asynchronous operation, but that forces you to mix UI code inside the long running operation. IProgress allows you to make an OnReport call with your report data without any concerns about marshalling the call to the proper thread, synchronization contexts, UI specific calls etc.
Your code can be as simple as this:
private async void button1_Click(object sender, EventArgs e)
{
var progress=new Progress<string>(msg=>textBox1.Text = msg);
await Task<string>.Factory.StartNew(() => Foo(progress));
}
private void Foo(IProgress<string> progress)
{
for (int i = 0; i < 100; i++)
{
progress.OnReport(i.ToString());
Thread.Sleep(1000);
}
progress.OnReport("Finished");
}
Or you can use a more complex progress type, eg
class MyProgressData
{
public string Message{get;set;}
public int Iteration {get;set;}
public MyProgressData(string message,int iteration) ...
}
private async void button1_Click(object sender, EventArgs e)
{
var progress=new Progress<MyProgressData>(msg=>{
textBox1.Text = msg.Message;
textBox2.Text=msg.Iteration.ToString();
});
await Task<string>.Factory.StartNew(() => Foo(progress));
}
private void Foo(IProgress<MyProgressData> progress)
{
for (int i = 0; i < 100; i++)
{
progress.OnReport(new MyProgressData("Hi",i));
Thread.Sleep(1000);
}
progress.OnReport("Finished");
}
The beauty of this is that you can completely decouple processing from reporting. You can put the processing code in a completely different class or even project from your UI.
You can just run the code to see that it won't work, and that your program will crash, as you are touching the UI from a non-UI thread.
You should instead update the UI as a continuation to the task that is scheduled to run in the UI thread. await does all of this for you, making the code pretty trivial.
You also shouldn't create a thread pool thread just to have it sit there and do nothing while you sleep for a fixed amount of time. Use Task.Delay instead.
private async void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
await Task.Delay(1000);
}
textBox1.Text = "Completed";
}
Your Foo method will throw, since you're trying to update a UI control from a background thread.
If Foo did something arbitrary, which doesn't evolve any UI controls, then your textBox1.Text = await Task.Factory.StartNew would be fine, since you use await and the SynchronizationContext would be implicitly captured and used once the await completes, which would make the assignment on the UI thread.
This doesn't make too much sense, but if you wanted to update a control from inside the Task, you'd have to use Control.Invoke.

Parallel Programming: Can't access UI Parallel?

I'm trying to create parallel execution of a function in wpf c# which also runs actions on the UI. But when running there is always an exception at methods on UI Controls: The calling thread cannot access this object because a different thread owns it. The exception is always called on the second instance of the loop being run, so it isn't possible to manipulate the UI in two parallel running instances?
Is it possible to acces the UI in parallel?
Code:
do
{
if (listBox_Copy.SelectedIndex < listBox_Copy.Items.Count - 1)
{
listBox_Copy.SelectedIndex = listBox_Copy.SelectedIndex + 1;
listBox_Copy.ScrollIntoView(listBox_Copy.SelectedItem);
}
listBox_Copy.Focus();
huidigitem = listBox_Copy.SelectedItem as ListBoxItem;
currentitemindex = listBox_Copy.SelectedIndex;
currentitem = listBox_Copy.ItemContainerGenerator.ContainerFromIndex(currentitemindex) as ListBoxItem;
itemgrid = FindVisualChild<Grid>(currentitem);
senderbutton = (Button)sender;
Button playcues = itemgrid.FindName("Playbutton") as Button;
cuetrigger = itemgrid.FindName("cuetrigger") as TextBlock;
Jobs.Add(playcues);
} while (cuetrigger.Text != "go");
Parallel.ForEach(Jobs, playcues => { play(playcues, new RoutedEventArgs()); });
And then it crashes at the play function
private void play(object sender, System.Windows.RoutedEventArgs e)
{
Grid itemgrid = VisualTreeHelperExtensions.FindAncestor<Grid>(playcue);
...
}
It is not possible to access the UI from a background thread, all your updates must be on the main thread. You can do this by using the Dispatcher
Something like this
Action x = (Action)delegate {
//do my UI updating
};
Dispatcher.Invoke(x, new object[] { });
The trick is to use an IProgress<T> to report updates to the main thread. The IProgress<T> constructor accepts an anonymous function that will be run in the main thread and can thus update the UI.
Quoting from https://blog.stephencleary.com/2012/02/reporting-progress-from-async-tasks.html :
public async void StartProcessingButton_Click(object sender, EventArgs e)
{
// The Progress<T> constructor captures our UI context,
// so the lambda will be run on the UI thread.
var progress = new Progress<int>(percent =>
{
textBox1.Text = percent + "%";
});
// DoProcessing is run on the thread pool.
await Task.Run(() => DoProcessing(progress));
textBox1.Text = "Done!";
}
public void DoProcessing(IProgress<int> progress)
{
for (int i = 0; i != 100; ++i)
{
Thread.Sleep(100); // CPU-bound work
if (progress != null)
progress.Report(i);
}
}
Now, a little bit of self-promotion :) I created an IEnumerable<T> extension to run a parallel for with event callbacks that can directly modify the UI. You can have a look at it here:
https://github.com/jotaelesalinas/csharp-forallp
Hope it helps!

Categories