Async task
-
In the past, I did most of my work with parallel objects using Rx, but that was made a bit cumbersome in the recent project. So I was following the code from Brian Lagunas, I made some tweaks to it to see how it all worked.
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace WpfAsynTaskAwait
{
public class MainViewModel:BindableBase
{#region "ctor" public MainViewModel() { Text = "1: Main UI thread on: " + Thread.CurrentThread.ManagedThreadId.ToString(); Task.Run(() => DoSomething(MethodCallback)).Await(CompletedCallback, ErrorCallback); } #endregion async Task DoSomething(Action methodCallback) { await Task.Delay(1500); methodCallback?.Invoke("2: Task starts on thread: " + Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Delay(1500); //throw new Exception("Aborted exception on thread " + Thread.CurrentThread.ManagedThreadId.ToString()); methodCallback?.Invoke("3: Task runs on thread: " + Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Delay(1500); } #region "Variables" private string \_text = "Default value"; public string Text { get { return \_text; } set { SetProperty(ref \_text, value); } } #endregion #region "Callback methods" private void CompletedCallback() { Text = "4: Method completed called from thread: " + Thread.CurrentThread.ManagedThreadId.ToString(); } private void MethodCallback(string message) { Text = message + Environment.NewLine + "Task callback from thread: " + Thread.CurrentThread.ManagedThreadId.ToString(); } private void ErrorCallback(Exception ex) { Text = ex.Message; } #endregion } public static class TaskExtensions { public async static void Await(this Task task, Action completedCallback, Action errorCallback) { try { await
-
In the past, I did most of my work with parallel objects using Rx, but that was made a bit cumbersome in the recent project. So I was following the code from Brian Lagunas, I made some tweaks to it to see how it all worked.
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace WpfAsynTaskAwait
{
public class MainViewModel:BindableBase
{#region "ctor" public MainViewModel() { Text = "1: Main UI thread on: " + Thread.CurrentThread.ManagedThreadId.ToString(); Task.Run(() => DoSomething(MethodCallback)).Await(CompletedCallback, ErrorCallback); } #endregion async Task DoSomething(Action methodCallback) { await Task.Delay(1500); methodCallback?.Invoke("2: Task starts on thread: " + Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Delay(1500); //throw new Exception("Aborted exception on thread " + Thread.CurrentThread.ManagedThreadId.ToString()); methodCallback?.Invoke("3: Task runs on thread: " + Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Delay(1500); } #region "Variables" private string \_text = "Default value"; public string Text { get { return \_text; } set { SetProperty(ref \_text, value); } } #endregion #region "Callback methods" private void CompletedCallback() { Text = "4: Method completed called from thread: " + Thread.CurrentThread.ManagedThreadId.ToString(); } private void MethodCallback(string message) { Text = message + Environment.NewLine + "Task callback from thread: " + Thread.CurrentThread.ManagedThreadId.ToString(); } private void ErrorCallback(Exception ex) { Text = ex.Message; } #endregion } public static class TaskExtensions { public async static void Await(this Task task, Action completedCallback, Action errorCallback) { try { await
Aren't callbacks just executed on the main thread, instead of the invoking one? > "Task callback from thread: " + Thread.CurrentThread.ManagedThreadId.ToString() Aigt, Convert.ToString(Thread.CurrentThread.ManagedThreadId) to avoid a Null-exception. What did your logs say? And if updating directly, instead of binding, does it show the same result?
Bastard Programmer from Hell :suss: "If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
-
Aren't callbacks just executed on the main thread, instead of the invoking one? > "Task callback from thread: " + Thread.CurrentThread.ManagedThreadId.ToString() Aigt, Convert.ToString(Thread.CurrentThread.ManagedThreadId) to avoid a Null-exception. What did your logs say? And if updating directly, instead of binding, does it show the same result?
Bastard Programmer from Hell :suss: "If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
It's WPF stuff with PRISM, so I don't really have access to the textbox in the GUI directly. But it says that the application changes the property Text from the background thread. The text is thus updated on the UI via the binding and INotifiedPropertyChange interaction.
-
It's WPF stuff with PRISM, so I don't really have access to the textbox in the GUI directly. But it says that the application changes the property Text from the background thread. The text is thus updated on the UI via the binding and INotifiedPropertyChange interaction.
If you modify a bound property in your view model from a non-UI thread, WPF automatically marshals the UI update changes for that property to the UI thread. It works for all scalar types, but not collections. You have to create your own collection type that does the marshalling.
Asking questions is a skill CodeProject Forum Guidelines Google: C# How to debug code Seriously, go read these articles.
Dave Kreskowiak -
If you modify a bound property in your view model from a non-UI thread, WPF automatically marshals the UI update changes for that property to the UI thread. It works for all scalar types, but not collections. You have to create your own collection type that does the marshalling.
Asking questions is a skill CodeProject Forum Guidelines Google: C# How to debug code Seriously, go read these articles.
Dave KreskowiakThank you for the answer Dave. So I modified the code in the MethodCallback and got exactly what you said :thumbsup: :
This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
Now I have some more research to go further. The change in the code is just banal, Binding to a TextBox and Listview's itemssource directly will throw the exception:
public class MainViewModel:BindableBase
{// ObservableCollection MainCollection = new ObservableCollection(); #region "ctor" public MainViewModel() { Text = "1: Main UI thread on: " + Thread.CurrentThread.ManagedThreadId.ToString(); TextCollection.Add("1: Main UI thread on: " + Thread.CurrentThread.ManagedThreadId.ToString()); Task.Run(() => DoSomething(MethodCallback)).Await(CompletedCallback, ErrorCallback); } #endregion async Task DoSomething(Action methodCallback) { await Task.Delay(1500); methodCallback?.Invoke("2: Task starts on thread: " + Thread.CurrentThread.ManagedThreadId.ToString()); TextCollection.Add("2,5: Task starts on thread: " + Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Delay(1500); //throw new Exception("Aborted exception on thread " + Thread.CurrentThread.ManagedThreadId.ToString()); methodCallback?.Invoke("3: Task runs on thread: " + Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Delay(1500); } #region "Variables" private ObservableCollection \_textCollection = new ObservableCollection(); public ObservableCollection TextCollection { get { return \_textCollection; } set { SetProperty(ref \_textCollection, value); } } private string \_text = "Default value"; public string Text { get { return \_text; } set { SetProperty(ref \_text, value); } } #endregion #region "Callback methods" private void CompletedCallback() { Text = "4: Method completed called from thread: " + Thread.CurrentThread.ManagedThreadId.ToString(); TextCollection.Add("4: Method completed called from thread: " +
-
Thank you for the answer Dave. So I modified the code in the MethodCallback and got exactly what you said :thumbsup: :
This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
Now I have some more research to go further. The change in the code is just banal, Binding to a TextBox and Listview's itemssource directly will throw the exception:
public class MainViewModel:BindableBase
{// ObservableCollection MainCollection = new ObservableCollection(); #region "ctor" public MainViewModel() { Text = "1: Main UI thread on: " + Thread.CurrentThread.ManagedThreadId.ToString(); TextCollection.Add("1: Main UI thread on: " + Thread.CurrentThread.ManagedThreadId.ToString()); Task.Run(() => DoSomething(MethodCallback)).Await(CompletedCallback, ErrorCallback); } #endregion async Task DoSomething(Action methodCallback) { await Task.Delay(1500); methodCallback?.Invoke("2: Task starts on thread: " + Thread.CurrentThread.ManagedThreadId.ToString()); TextCollection.Add("2,5: Task starts on thread: " + Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Delay(1500); //throw new Exception("Aborted exception on thread " + Thread.CurrentThread.ManagedThreadId.ToString()); methodCallback?.Invoke("3: Task runs on thread: " + Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Delay(1500); } #region "Variables" private ObservableCollection \_textCollection = new ObservableCollection(); public ObservableCollection TextCollection { get { return \_textCollection; } set { SetProperty(ref \_textCollection, value); } } private string \_text = "Default value"; public string Text { get { return \_text; } set { SetProperty(ref \_text, value); } } #endregion #region "Callback methods" private void CompletedCallback() { Text = "4: Method completed called from thread: " + Thread.CurrentThread.ManagedThreadId.ToString(); TextCollection.Add("4: Method completed called from thread: " +
From .NET 4.5 onwards, you can enable collection updates from a background thread fairly simply: WPF 4.5: Observable Collection Cross-Thread Change Notification - Pete Brown's 10rem.net[^] BindingOperations.EnableCollectionSynchronization Method (System.Windows.Data) | Microsoft Docs[^]
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
If you modify a bound property in your view model from a non-UI thread, WPF automatically marshals the UI update changes for that property to the UI thread. It works for all scalar types, but not collections. You have to create your own collection type that does the marshalling.
Asking questions is a skill CodeProject Forum Guidelines Google: C# How to debug code Seriously, go read these articles.
Dave KreskowiakDave Kreskowiak wrote:
You have to create your own collection type that does the marshalling.
Or, if you're using .NET 4.5 or later, use the built-in method. :) BindingOperations.EnableCollectionSynchronization Method (System.Windows.Data) | Microsoft Docs[^]
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
Dave Kreskowiak wrote:
You have to create your own collection type that does the marshalling.
Or, if you're using .NET 4.5 or later, use the built-in method. :) BindingOperations.EnableCollectionSynchronization Method (System.Windows.Data) | Microsoft Docs[^]
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
Huh, I didn't know know about that. That makes life a little easier. :)
Asking questions is a skill CodeProject Forum Guidelines Google: C# How to debug code Seriously, go read these articles.
Dave Kreskowiak -
From .NET 4.5 onwards, you can enable collection updates from a background thread fairly simply: WPF 4.5: Observable Collection Cross-Thread Change Notification - Pete Brown's 10rem.net[^] BindingOperations.EnableCollectionSynchronization Method (System.Windows.Data) | Microsoft Docs[^]
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
I like this. Not a lot of backbreaking coding work :-\ Literally just added two lines of code and all works well.
private object \_TextCollectionLock = new object(); public MainViewModel() { BindingOperations.EnableCollectionSynchronization(TextCollection, \_TextCollectionLock); ... }