你好,如何在WPF和MVVM中实现异步操作是最简单的方法呢?比方说,如果用户在输入框内按下回车键,我想启动一个命令,并在后台执行某些搜索操作。然后等到线程完成后返回并更新属性,这样通知就可以更新绑定了。
谢谢!
你好,如何在WPF和MVVM中实现异步操作是最简单的方法呢?比方说,如果用户在输入框内按下回车键,我想启动一个命令,并在后台执行某些搜索操作。然后等到线程完成后返回并更新属性,这样通知就可以更新绑定了。
谢谢!
Rob Eisenberg在他的MIX10演讲中展示了一种非常干净的MVVM异步操作实现方式。他已经在他的博客上发布了源代码。
基本思想是将命令实现为返回IEnumerable并使用yield关键字返回结果。这里是他演讲中的一段代码片段,它作为后台任务执行搜索:
public IEnumerable<IResult> ExecuteSearch()
{
var search = new SearchGames
{
SearchText = SearchText
}.AsResult();
yield return Show.Busy();
yield return search;
var resultCount = search.Response.Count();
if (resultCount == 0)
SearchResults = _noResults.WithTitle(SearchText);
else if (resultCount == 1 && search.Response.First().Title == SearchText)
{
var getGame = new GetGame
{
Id = search.Response.First().Id
}.AsResult();
yield return getGame;
yield return Show.Screen<ExploreGameViewModel>()
.Configured(x => x.WithGame(getGame.Response));
}
else SearchResults = _results.With(search.Response);
yield return Show.NotBusy();
}
你可以考虑使用BackgroundWorker实例在虚拟机上调用命令。
更新: 忽略上面的建议... Jason Dolinger有一段关于MVVM的在线视频.. 我建议你去看一下。这是一种更简洁的方式,其中视图很薄/不包含任何线程代码。
总结:
_dispatcher.BeginInvoke( () => _results.AddRange( entries) )
以便正确更新UI。public interface IGameCatalog
{
void GetGames();
void GetGamesByGenre(string genre);
void SaveChanges();
event EventHandler<GameLoadingEventArgs> GameLoadingComplete;
event EventHandler<GameCatalogErrorEventArgs> GameLoadingError;
event EventHandler GameSavingComplete;
event EventHandler<GameCatalogErrorEventArgs> GameSavingError;
}
使用如下实现:
public class GameCatalog : IGameCatalog
{
Uri theServiceRoot;
GamesEntities theEntities;
const int MAX_RESULTS = 50;
public GameCatalog() : this(new Uri("/Games.svc", UriKind.Relative))
{
}
public GameCatalog(Uri serviceRoot)
{
theServiceRoot = serviceRoot;
}
public event EventHandler<GameLoadingEventArgs> GameLoadingComplete;
public event EventHandler<GameCatalogErrorEventArgs> GameLoadingError;
public event EventHandler GameSavingComplete;
public event EventHandler<GameCatalogErrorEventArgs> GameSavingError;
public void GetGames()
{
// Get all the games ordered by release date
var qry = (from g in Entities.Games
orderby g.ReleaseDate descending
select g).Take(MAX_RESULTS) as DataServiceQuery<Game>;
ExecuteGameQuery(qry);
}
public void GetGamesByGenre(string genre)
{
// Get all the games ordered by release date
var qry = (from g in Entities.Games
where g.Genre.ToLower() == genre.ToLower()
orderby g.ReleaseDate
select g).Take(MAX_RESULTS) as DataServiceQuery<Game>;
ExecuteGameQuery(qry);
}
public void SaveChanges()
{
// Save Not Yet Implemented
throw new NotImplementedException();
}
// Call the query asynchronously and add the results to the collection
void ExecuteGameQuery(DataServiceQuery<Game> qry)
{
// Execute the query
qry.BeginExecute(new AsyncCallback(a =>
{
try
{
IEnumerable<Game> results = qry.EndExecute(a);
if (GameLoadingComplete != null)
{
GameLoadingComplete(this, new GameLoadingEventArgs(results));
}
}
catch (Exception ex)
{
if (GameLoadingError != null)
{
GameLoadingError(this, new GameCatalogErrorEventArgs(ex));
}
}
}), null);
}
GamesEntities Entities
{
get
{
if (theEntities == null)
{
theEntities = new GamesEntities(theServiceRoot);
}
return theEntities;
}
}
}