异步更新的ObservableCollection项目 [英] Asynchronous update to ObservableCollection items

查看:789
本文介绍了异步更新的ObservableCollection项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新来的多线程和WPF。

I'm new to multithreading and WPF.

我有一个的ObservableCollection<&RSSFeed中GT; ,在应用程序启动项添加到此集合从UI线程。 RSSFeed中的属性绑定到WPF的ListView。后来,我想异步更新每个RSSFeed中。所以我想实现像 RSSFeed.FetchAsync()并在其更​​新的属性提高的PropertyChanged的。

I have an ObservableCollection<RSSFeed>, at app startup items are added to this collection from UI thread. Properties of RSSFeed are bind to WPF ListView. Later, I want to update each RSSFeed asynchronously. So I'm thinking of implementing something like RSSFeed.FetchAsync() and raising PropertyChanged on its updated properties.

我知道的ObservableCollection不从非UI线程其他线程支持更新,它会引发NotSupportedException。但是,由于我没有操纵的ObservableCollection本身,而是它的项目,而更新的属性,我可以期待这个工作,看到的ListView项目更新?还是会反正抛出一个异常,由于PropertyChanged的?

I know that ObservableCollection doesn't support updates from threads other than the UI thread, it throws NotSupportedException. But since I'm not manipulating the ObservableCollection itself but rather updating properties on its items, can I expect this to work and see ListView items updated? Or would it threw an exception anyway due to PropertyChanged?

编辑: code

RSSFeed.cs

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

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

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
    }

}

感谢。

推荐答案

对于这种应用,我通常使用一个BackgroundWorker与ReportsProgress设置为True。那么你可以传递一个对象每次调用作为ReportProgress方法userState参数。该ProgressChanged事件将在UI线程上运行,这样你就可以在事件处理程序中的对象添加到的ObservableCollection。

For this kind of application, I usually use a BackgroundWorker with ReportsProgress set to True. Then you can pass one object for each call as the userState parameter in the ReportProgress method. The ProgressChanged event will run on the UI thread, so you can add the object to the ObservableCollection in the event handler.

另外,从后台线程更新性质的工作,但如果你要过滤或排序的ObservableCollection,那么过滤器将不会重新应用,除非一些集合更改通知事件已经提高。

Otherwise, updating the properties from a background thread will work, but if you are filtering or sorting the ObservableCollection, then the filter will not be reapplied unless some collection change notification event has been raised.

可以导致过滤器和排序,以通过查找项的索引集合中(例如通过报告为progresspercentage)和设置list.item(ⅰ)= e.userstate,即替换在该项目重新应用在ProgressChanged事件列表本身。这样一来,任何控件绑定到集合的SelectedItem将是preserved,而过滤和排序将在项目方面的任何改变的值。

You can cause filters and sorts to be reapplied by finding the index of the item in the collection (e.g. by reporting it as progresspercentage) and setting the 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 bound to the collection will be preserved, while filter and sorting will respect any changed values in the item.

这篇关于异步更新的ObservableCollection项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆