C# · 12月 27, 2021

c# – 对ObservableCollection项的异步更新

我是多线程和 WPF的新手.

我有一个ObservableCollection< RSSFeed&gt ;,应用程序启动项从UI线程添加到此集合中. RSSFeed的属性绑定到WPF ListView.后来,我想异步更新每个RSSFeed.所以我正在考虑实现像RSSFeed.FetchAsync()这样的东西,并在其更新的属性上提升PropertyChanged. 我知道ObservableCollection不支持来自UI线程以外的线程的更新,它会抛出NotSupportedException.但是因为我没有操纵ObservableCollection本身而是更新其项目的属性,我可以期待这个工作并看到ListView项目更新吗?或者由于PropertyChanged它会抛出异常吗? 编辑:代码 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 (list@R_946_2419@Channels.SelectedItem != null) return ((RSSFeed)list@R_946_2419@Channels.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 }}

谢谢.

解决方法 对于这种应用程序,我通常使用BackgroundWorker并将ReportsProgress设置为True.然后,您可以将每个调用的一个对象作为ReportProgress方法中的userState参数传递. ProgressChanged事件将在UI线程上运行,因此您可以将对象添加到事件处理程序中的ObservableCollection.

否则,从后台线程更新属性将起作用,但如果您正在过滤或排序ObservableCollection,则除非引发了某些集合更改通知事件,否则不会重新应用过滤器.

您可以通过查找集合中项目的索引(例如,通过将其报告为progresspercentage)并设置list.item(i)= e.userstate来重新应用过滤器和排序,即单独替换列表中的项目在ProgressChanged事件中.这样,将保留绑定到集合的任何控件的SelectedItem,而过滤器和排序将遵循项中任何更改的值.