Jestem nowy w wielowątkowości i WPF.Asynchroniczna aktualizacja elementów ObservableCollection
Mam ObservableCollection<RSSFeed>
, przy elementach uruchamiania aplikacji są dodawane do tej kolekcji z wątku interfejsu użytkownika. Właściwości RSSFeed są związane z ListView WPF. Później chcę asynchronicznie zaktualizować każdy RSSFeed. Zastanawiam się więc nad implementacją czegoś takiego jak RSSFeed.FetchAsync()
i podniesieniem właściwości PropertyChanged na jej zaktualizowanych właściwościach.
Wiem, że ObservableCollection nie obsługuje aktualizacji z wątków innych niż wątek interfejsu użytkownika, zgłasza wyjątek NotSupportedException. Ale ponieważ nie manipuluję obserwowalną kolekcją, ale raczej aktualizuję właściwości jej elementów, czy mogę oczekiwać, że to zadziała i zaktualizuje elementy ListView? Czy też może wyrzucił wyjątek z powodu PropertyChanged?
EDIT: Kod
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
}
}
Dzięki.
Dzięki. Mam kolekcję zagnieżdżoną w mojej klasie RSSFeed. Czy byłby to dobry souluting, gdyby RSSFeed.FetchAsync() podniósł wydarzenie po jego zakończeniu i zwrócił nową (zaktualizowaną) instancję RSSFeed poprzez EventArgs? Później zaktualizowałbym odpowiedni element w kolekcji z wątku interfejsu użytkownika. – Martin
To możliwe rozwiązanie. Gdybyśmy mogli zobaczyć kod, mógłbym podać bardziej konkretną odpowiedź. –
@malymato Zwykle metody asynchroniczne zapewniają wywołanie zwrotne po zakończeniu. Jakiego rodzaju wdrożenia używasz? –