2012-05-09 34 views
6

Tôi mới sử dụng đa luồng và WPF.Cập nhật không đồng bộ cho các mục ObservableCollection

Tôi có một ObservableCollection<RSSFeed>, tại mục khởi động ứng dụng được thêm vào bộ sưu tập này từ chuỗi giao diện người dùng. Các thuộc tính của RSSFeed được liên kết với ListView WPF. Sau đó, tôi muốn cập nhật từng RSSFeed một cách không đồng bộ. Vì vậy, tôi đang nghĩ đến việc triển khai một cái gì đó như RSSFeed.FetchAsync() và nâng cao PropertyChanged trên các thuộc tính được cập nhật của nó.

Tôi biết rằng ObservableCollection không hỗ trợ cập nhật từ các chủ đề khác ngoài chuỗi giao diện người dùng, nó ném NotSupportedException. Nhưng kể từ khi tôi không thao tác ObservableCollection chính nó nhưng thay vì cập nhật tài sản trên các mặt hàng của nó, tôi có thể mong đợi điều này để làm việc và xem các mục ListView cập nhật? Hoặc nó sẽ ném một ngoại lệ anyway do PropertyChanged?

Edit: đang

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 
    } 

} 

Cảm ơn.

Trả lời

3

Đối với loại ứng dụng, tôi thường sử dụng một BackgroundWorker với ReportsProgress thiết lập là True. Sau đó, bạn có thể truyền một đối tượng cho mỗi cuộc gọi như tham số userState trong phương thức ReportProgress. Sự kiện ProgressChanged sẽ chạy trên chuỗi giao diện người dùng, vì vậy bạn có thể thêm đối tượng vào ObservableCollection trong trình xử lý sự kiện.

Nếu không, việc cập nhật thuộc tính từ chuỗi nền sẽ hoạt động, nhưng nếu bạn lọc hoặc sắp xếp ObservableCollection, thì bộ lọc sẽ không được áp dụng lại trừ khi một số sự kiện thông báo thay đổi bộ sưu tập được nâng lên.

Bạn có thể khiến bộ lọc và loại được áp dụng lại bằng cách tìm chỉ mục của mục trong bộ sưu tập (ví dụ bằng cách báo cáo nó là lũy tiến) và thiết lập list.item (i) = e.userstate, tức là thay thế mục trong danh sách của chính nó trong sự kiện ProgressChanged.Bằng cách này, SelectedItem của bất kỳ điều khiển nào liên kết với bộ sưu tập sẽ được giữ nguyên, trong khi bộ lọc và sắp xếp sẽ tôn trọng mọi giá trị đã thay đổi trong mục.

3

Nếu bạn đang sử dụng WPF, bạn được phép cập nhật các thuộc tính trên các mục bị ràng buộc riêng lẻ và tăng PropertyChanged từ một chuỗi nền. Các cơ chế ràng buộc dữ liệu WPF (không giống như WinForms tương đương) phát hiện điều này và so sánh với luồng giao diện người dùng cho bạn. Có một chi phí cho khóa học này, sử dụng cơ chế tự động, mỗi bản cập nhật thuộc tính cá nhân sẽ gây ra một sự kiện marshalling, vì vậy nếu bạn thay đổi rất nhiều đặc tính hiệu suất có thể bị ảnh hưởng, và bạn nên xem xét việc thực hiện giao diện người dùng. .

Bạn không được phép thao tác các bộ sưu tập (thêm/xóa mục), vì vậy nếu nguồn cấp dữ liệu RSS của bạn chứa các bộ sưu tập lồng nhau mà bạn muốn liên kết với bạn cần nâng toàn bộ cập nhật lên chuỗi giao diện người dùng trước thời hạn.

+0

Cảm ơn. Tôi có một bộ sưu tập lồng nhau trong lớp RSSFeed của tôi. Nó sẽ là một soulution tốt nếu RSSFeed.FetchAsync() nêu ra một sự kiện về hoàn thành của nó và trả về mới (cập nhật) RSSFeed dụ thông qua EventArgs? Sau đó tôi sẽ cập nhật mục tương ứng trong bộ sưu tập từ chuỗi giao diện người dùng. – Martin

+0

Đó là một giải pháp khả thi. Nếu chúng ta có thể thấy một số mã tôi có thể cung cấp cho bạn một câu trả lời cụ thể hơn. –

+0

@malymato thường Các phương pháp Async cung cấp gọi lại khi hoàn tất. Bạn đang sử dụng loại triển khai nào? –

0

Tôi đã có một kịch bản tương tự và gặp gỡ này "ObservableCollection không hỗ trợ cập nhật từ chủ đề khác với thread UI", cuối cùng nó đã được giải quyết bằng cách tham khảo AsyncObservableCollection implement này vào diễn đàn Thomas Levesque 's , Tôi nghĩ rằng nó có thể hữu ích cho bạn.

Trong phiên bản Cập nhật, SynchronizationContext được sử dụng để giải quyết vấn đề này. Bạn có thể tham khảo MSDN page of SynchronizationContext

Các vấn đề liên quan