Ho cercato di utilizzare Rx in un framework MVVM. L'idea è di utilizzare query LINQ "live" su dataset in-memory per proiettare i dati in View Models da associare.Utilizzo di IObservable (Rx) come sostituzione di INotifyCollectionChanged per MVVM?
In precedenza questo era possibile con l'uso di INotifyPropertyChanged/INotifyCollectionChanged e una libreria open source denominata CLINQ. Il potenziale con Rx e IObservable è quello di passare a un ViewModel molto più dichiarativo usando le classi Subject per propagare gli eventi modificati dal modello sorgente alla View. Per l'ultimo passaggio sarebbe necessaria una conversione da IObservable alle normali interfacce di associazione dati.
Il problema è che Rx non sembra supportare la notifica che un'entità è stata rimossa dal flusso. Esempio di seguito.
Il codice mostra un POCO che utilizza la classe BehaviorSubject per lo stato del campo. Il codice va avanti per creare una raccolta di queste entità e utilizzare Concat per unire i flussi di filtro insieme. Ciò significa che qualsiasi modifica ai POCO viene segnalata a un singolo flusso.
Un filtro per questo flusso è impostato per filtrare per Rating == 0. L'abbonamento emette semplicemente il risultato nella finestra di debug quando si verifica un evento pari.
Impostazioni Valutazione = 0 su qualsiasi elemento attiverà l'evento. Tuttavia, l'impostazione di Valutazione su 5 non vedrà alcun evento.
Nel caso di CLINQ l'output della query supporterà INotifyCollectionChanged - in modo che gli elementi aggiunti e rimossi dal risultato della query generino l'evento corretto per indicare che il risultato della query è stato modificato (un elemento aggiunto o rimosso).
L'unico modo in cui posso pensare a questo indirizzo è impostare due flussi con query oppossite (doppie). Un elemento aggiunto al flusso opposto implica la rimozione dal set di risultati. In caso contrario, potrei semplicemente utilizzare FromEvent e non rendere nessuno dei modelli di entità osservabili, il che rende Rx più di un semplice Aggregatore di eventi. Qualche indicazione?
using System;
using System.ComponentModel;
using System.Linq;
using System.Collections.Generic;
namespace RxTest
{
public class TestEntity : Subject<TestEntity>, INotifyPropertyChanged
{
public IObservable<string> FileObservable { get; set; }
public IObservable<int> RatingObservable { get; set; }
public string File
{
get { return FileObservable.First(); }
set { (FileObservable as IObserver<string>).OnNext(value); }
}
public int Rating
{
get { return RatingObservable.First(); }
set { (RatingObservable as IObserver<int>).OnNext(value); }
}
public event PropertyChangedEventHandler PropertyChanged;
public TestEntity()
{
this.FileObservable = new BehaviorSubject<string>(string.Empty);
this.RatingObservable = new BehaviorSubject<int>(0);
this.FileObservable.Subscribe(f => { OnNotifyPropertyChanged("File"); });
this.RatingObservable.Subscribe(f => { OnNotifyPropertyChanged("Rating"); });
}
private void OnNotifyPropertyChanged(string property)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(property));
// update the class Observable
OnNext(this);
}
}
public class TestModel
{
private List<TestEntity> collection { get; set; }
private IDisposable sub;
public TestModel()
{
this.collection = new List<TestEntity>() {
new TestEntity() { File = "MySong.mp3", Rating = 5 },
new TestEntity() { File = "Heart.mp3", Rating = 5 },
new TestEntity() { File = "KarmaPolice.mp3", Rating = 5 }};
var observableCollection = Observable.Concat<TestEntity>(this.collection.Cast<IObservable<TestEntity>>());
var filteredCollection = from entity in observableCollection
where entity.Rating==0
select entity;
this.sub = filteredCollection.Subscribe(entity =>
{
System.Diagnostics.Debug.WriteLine("Added :" + entity.File);
}
);
this.collection[0].Rating = 0;
this.collection[0].Rating = 5;
}
};
}
"Il problema è che Rx non sembra supportare la notifica che un'entità è stata rimossa dal flusso" - questo perché IObservable non rappresenta una raccolta persistente, ma solo un flusso asincrono di valori. –