2012-05-09 11 views
6

Soy nuevo en multihebra y WPF.Actualización asincrónica de los elementos de ObservableCollection

Tengo ObservableCollection<RSSFeed>, al inicio de la aplicación, los elementos se agregan a esta colección desde el hilo de la interfaz de usuario. Las propiedades de RSSFeed se unen a WPF ListView. Más tarde, quiero actualizar cada RSSFeed de forma asíncrona. Así que estoy pensando en implementar algo como RSSFeed.FetchAsync() y subir PropertyChanged en sus propiedades actualizadas.

Sé que ObservableCollection no admite actualizaciones de hilos que no sean el hilo de UI, arroja NotSupportedException. Pero como no estoy manipulando el ObservableCollection sino más bien actualizando las propiedades de sus elementos, ¿puedo esperar que esto funcione y ver los elementos de ListView actualizados? ¿O arrojaría una excepción de todos modos debido a PropertyChanged?

Editar: código

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 
    } 

} 

Gracias.

Respuesta

3

Para este tipo de aplicación, por lo general uso un BackgroundWorker con ReportsProgress establece en True. Luego puede pasar un objeto para cada llamada como el parámetro userState en el método ReportProgress. El evento ProgressChanged se ejecutará en el subproceso de interfaz de usuario, por lo que puede agregar el objeto a ObservableCollection en el controlador de eventos.

De lo contrario, actualizar las propiedades de un hilo de fondo funcionará, pero si está filtrando u ordenando el ObservableCollection, el filtro no se volverá a aplicar a menos que se haya producido algún evento de notificación de cambio de colección.

Puede hacer que los filtros y géneros se vuelvan a aplicar al encontrar el índice del elemento en la colección (por ejemplo, al informarlo como progresspercentage) y establecer list.item (i) = e.userstate, es decir, reemplazar el elemento en la lista solo en el evento ProgressChanged.De esta forma, se conservará el elemento seleccionado de los controles vinculados a la colección, mientras que el filtro y la clasificación respetarán los valores modificados en el elemento.

3

Si está utilizando WPF puede actualizar propiedades en elementos individuales enlazados y subir PropertyChanged desde una cadena de fondo. Los mecanismos de enlace de datos de WPF (a diferencia del equivalente de WinForms) detectan esto y obtienen el hilo de UI por usted. Hay un costo para esto: usando el mecanismo automático, cada actualización de propiedad individual causará un evento de clasificación, por lo que si cambias muchas propiedades, el rendimiento puede verse afectado, y deberías considerar hacer la interfaz de usuario como una sola operación por lotes. .

Sin embargo, no se permite manipular colecciones (agregar/eliminar elementos), por lo que si sus fuentes RSS contienen colecciones anidadas a las que desea enlazar, debe elevar toda la actualización al hilo de UI con anticipación.

+0

Gracias. Tengo una colección anidada en mi clase RSSFeed. ¿Sería una buena solución si RSSFeed.FetchAsync() generara un evento al completarse y devolviera una instancia de RSSFeed nueva (actualizada) a través de EventArgs? Más tarde, actualizaría el artículo correspondiente en la colección del hilo de la interfaz de usuario. – Martin

+0

Esa es una posible solución. Si pudiéramos ver algún código, podría darte una respuesta más concreta. –

+0

@malymato por lo general, los métodos Async proporcionan una devolución de llamada cuando se completan. ¿Qué tipo de implementación estás usando? –

0

que tenía un escenario similar y me encontré con este "ObservableCollection no soporta actualizaciones de temas que no sean el hilo de interfaz de usuario", que finalmente se resolvió haciendo referencia este AsyncObservableCollection implement en el blog de Thomas Levesque 's , Creo que puede ser útil para ti.

En su versión de actualización, SynchronizationContext se usa para resolver este problema. Puede consultar el MSDN page of SynchronizationContext

Cuestiones relacionadas