Ответ 1
У меня есть тип NotifyTaskCompletion
в моей библиотеке AsyncEx, который по существу является оберткой INotifyPropertyChanged
для Task
/Task<T>
. AFAIK в настоящее время доступно немного информации о async
в сочетании с MVVM, поэтому дайте мне знать, если вы найдете какие-либо другие подходы.
В любом случае подход NotifyTaskCompletion
работает лучше всего, если ваши задачи возвращают результаты. I.e., из вашего текущего примера кода выглядит, что GetFeedArticles
устанавливает свойства, связанные с данными, как побочный эффект, а не возвращает статьи. Если вы сделаете этот возврат Task<T>
вместо этого, вы можете получить код следующим образом:
private Feed selectedFeed;
public Feed SelectedFeed
{
get
{
return this.selectedFeed;
}
set
{
if (this.selectedFeed == value)
return;
this.selectedFeed = value;
RaisePropertyChanged();
Articles = NotifyTaskCompletion.Create(GetFeedArticlesAsync(value.Id));
}
}
private INotifyTaskCompletion<List<Article>> articles;
public INotifyTaskCompletion<List<Article>> Articles
{
get { return this.articles; }
set
{
if (this.articles == value)
return;
this.articles = value;
RaisePropertyChanged();
}
}
private async Task<List<Article>> GetFeedArticlesAsync(int id)
{
...
}
Затем ваша привязка данных может использовать Articles.Result
для перехода к полученной коллекции (которая находится в null
до завершения GetFeedArticlesAsync
). Вы можете использовать NotifyTaskCompletion
"из коробки" для привязки данных к ошибкам (например, Articles.ErrorMessage
) и имеет несколько логических удобных свойств (IsSuccessfullyCompleted
, IsFaulted
) для обработки переключений видимости.
Обратите внимание, что это будет корректно обрабатывать операции, завершающиеся не по порядку. Поскольку Articles
фактически представляет собой асинхронную операцию (вместо непосредственных результатов), она немедленно обновляется при запуске новой операции. Таким образом, вы никогда не увидите устаревшие результаты.
Вам не нужно использовать привязку данных для обработки ошибок. Вы можете сделать любую семантику, которая вам нужна, изменив GetFeedArticlesAsync
; например, для обработки исключений, передав их в MessengerInstance
:
private async Task<List<Article>> GetFeedArticlesAsync(int id)
{
try
{
...
}
catch (Exception ex)
{
MessengerInstance.Send<string>("Error description", "DisplayErrorNotification");
return null;
}
}
Аналогично, нет понятия автоматической отмены встроенного, но опять же легко добавить в GetFeedArticlesAsync
:
private CancellationTokenSource getFeedArticlesCts;
private async Task<List<Article>> GetFeedArticlesAsync(int id)
{
if (getFeedArticlesCts != null)
getFeedArticlesCts.Cancel();
using (getFeedArticlesCts = new CancellationTokenSource())
{
...
}
}
Это область текущей разработки, поэтому, пожалуйста, сделайте улучшения или предложения API!