Asynchronously updating ObservableCollection elements

I am new to multithreading and WPF.

I have an ObservableCollection<RSSFeed> , when adding applications to this collection they are added from the UI thread. RSSFeed properties are associated with the WPF ListView. Later I want to update each RSSFeed asynchronously. So I'm going to implement something like RSSFeed.FetchAsync() and raise the PropertyChanged property in its updated properties.

I know that ObservableCollection does not support updates from threads other than the UI thread, it throws a NotSupportedException. But since I am not manipulating the ObservableCollection itself, but rather updating the properties of my elements, can I expect this to work and see the updated ListView elements? Or would it still rule out due to PropertyChanged?

Edit: code

RSSFeed.cs

 public class RSSFeed { public String Title { get; set; } public String Summary { get; set; } public String Uri { get; set; } public String Encoding { get; set; } public List<FeedItem> Posts { get; set; } public bool FetchedSuccessfully { get; protected set; } public RSSFeed() { Posts = new List<FeedItem>(); } public RSSFeed(String uri) { Posts = new List<FeedItem>(); Uri = uri; Fetch(); } public void FetchAsync() { // call Fetch asynchronously } public void Fetch() { if (Uri != "") { try { MyWebClient client = new MyWebClient(); String str = client.DownloadString(Uri); str = Regex.Replace(str, "<!--.*?-->", String.Empty, RegexOptions.Singleline); FeedXmlReader reader = new FeedXmlReader(); RSSFeed feed = reader.Load(str, new Uri(Uri)); if (feed.Title != null) Title = feed.Title; if (feed.Encoding != null) Encoding = feed.Encoding; if (feed.Summary != null) Summary = feed.Summary; if (feed.Posts != null) Posts = feed.Posts; FetchedSuccessfully = true; } catch { FetchedSuccessfully = false; } } } 

UserProfile.cs

 public class UserProfile : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public event CollectionChangeEventHandler CollectionChanged; private ObservableCollection<RSSFeed> feeds; public ObservableCollection<RSSFeed> Feeds { get { return feeds; } set { feeds = value; OnPropertyChanged("Feeds"); } } public UserProfile() { feeds = new ObservableCollection<RSSFeed>(); } protected void OnPropertyChanged(string name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } protected void OnCollectionChanged(RSSFeed feed) { CollectionChangeEventHandler handler = CollectionChanged; if (handler != null) { handler(this, new CollectionChangeEventArgs(CollectionChangeAction.Add, feed)); } } } 

MainWindow.xaml.cs

 public partial class MainWindow : Window, INotifyPropertyChanged { // My ListView is bound to this // ItemsSource="{Binding Posts} public List<FeedItem> Posts { get { if (listBoxChannels.SelectedItem != null) return ((RSSFeed)listBoxChannels.SelectedItem).Posts; else return null; } } private void Window_Loaded(object sender, RoutedEventArgs e) { // here I load cached feeds // called from UI thread // now I want to update the feeds // since network operations are involved, // I need to do this asynchronously to prevent blocking the UI thread } } 

Thanks.

+6
source share
5 answers

For this type of application, I usually use BackgroundWorker with the ReportsProgress parameter set to True. You can then pass one object for each call as the userState parameter in the ReportProgress method. The ProgressChanged event will be fired in the user interface thread, so you can add an object to the ObservableCollection in an event handler.

Otherwise, updating properties from the background thread will work, but if you filter or sort the ObservableCollection, then the filter will not be reapplied unless any collection change notification event is raised.

You can cause filters and sorts to be reapplied by finding the index of the item in the collection (for example, reporting it as progresspercentage) and setting list.item (i) = e.userstate, i.e. replacing the item in the list by itself in the ProgressChanged event. This way, the SelectedItem of any controls associated with the collection will be saved, and the filter and sorting will respect any changed values ​​in the element.

+3
source

With .Net 4.5, you can add support for updating background threads in an ObservableCollection using BindingOperations.EnableCollectionSynchronization. This works great with MVVM.

See: BindingOperations.EnableCollectionSynchronization () equivalent for .net 4.0

+5
source

If you use WPF, you are allowed to update the properties of individual related items and raise the PropertyChanged from the background thread. WPF data binding mechanisms (unlike the WinForms equivalent) detect this and marshal the user interface flow for you. Of course, there is a cost to this, using an automatic mechanism, each individual property update will trigger a marshalling event, so if you change many properties, performance may suffer, and you should think about the user interface sorting itself as a single batch operation.

You are not allowed to manipulate collections (add / remove items), so if your RSS feeds contain nested collections that you want to bind to you, you need to push the entire update in advance to the user interface stream.

+3
source

You can see the ConcurrentCollections namespace in .Net.

http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx

Here is another question that may be helpful.

ObservableCollection and threading

+1
source

I had a similar script, and I met this “ObservableCollection does not support updates from threads other than the user interface thread”, finally it was solved by referring to this implementation of AsyncObservableCollection in Thomas Levesque's blog, I think this may be useful for you.

In its update version, SynchronizationContext is used to solve this problem. You can refer to the MSDN SynchronizationContext page.

0
source

Source: https://habr.com/ru/post/915294/


All Articles