Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous update to ObservableCollection items

I'm new to multithreading and WPF.

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.

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?

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.

like image 278
Martin Avatar asked May 09 '12 15:05

Martin


1 Answers

With .Net 4.5, you can add support for background thread updates to an ObservableCollection by using BindingOperations.EnableCollectionSynchronization. This works great with MVVM.

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

like image 139
KTCO Avatar answered Oct 12 '22 17:10

KTCO