该应用程序使用Caliburn.Micro及其屏幕向用户显示标签。
我知道在屏幕可见之前调用的IScreen.OnInitialize()方法。我希望有一个代码触发屏幕对用户完全可见 - 代表屏幕的选项卡应该在那里,但不一定要被选中。
我想在屏幕完全初始化并且可见之后触发数据加载(并且大部分代码本地运行在UI线程上而不进行编组)。
这就是现在的样子。
public class MyViewModel :
Conductor<IScreen>.Collection.OneActive,
IHandleWithTask<DatabaseItemChangedMessage>
{
// This method is called by EventAggregator
public async Task Handle(DatabaseItemChangedMessage message)
{
await this.LoadData(true);
}
protected override void OnInitialize()
{
base.OnInitialize();
Task.Factory.StartNew(() => this.LoadData(false).Wait()).ContinueWith(
task =>
{
if (task.Exception != null)
{
Execute.OnUIThread(
() =>
{
App.HandleException(task.Exception);
this.TryClose();
});
}
});
}
protected async Task LoadData()
{
try
{
this.WorkStarted("Loading data...");
TLoadedData loadedData;
ISession transaction = null;
// using (var transaction = this.DataItemRepository.BeginTransaction())
// {
loadedData = await this.LoadDataInternal(transaction);
// transaction.Commit();
// }
Execute.OnUIThread(() => this.SetLoadedData(loadedData));
}
finally
{
this.WorkFinished();
}
}
protected virtual void SetLoadedData(TLoadedData loadedData)
{
// Updates CollectionView's source using loadedData
}
protected virtual async Task<TLoadedData> LoadDataInternal(ISession session)
{
// loads data from database and returns them
}
protected virtual void WorkStarted(string description)
{
// Sets properties using to display progress
}
protected virtual void WorkFinished()
{
// Unsets properties using to display progress
}
}
// Just an empty class for EventAggregator purposes
public class DatabaseItemChangedMessage
{
}
// Stores data loaded from the database
public class TLoadedData
{
}
答案 0 :(得分:0)
在我看来,您的代码的主要问题是您正在创建一个全新的Task
,以执行现有的异步操作。事实上,你应该做的只是启动该操作,等待它,并在必要时处理任何异常。
例如:
public class MyViewModel :
Conductor<IScreen>.Collection.OneActive,
IHandleWithTask<DatabaseItemChangedMessage>
{
// Ideally, async methods return a Task, but here you have to
// match the base method signature, so you're stuck with void
protected override async void OnInitialize()
{
base.OnInitialize();
try
{
await this.LoadData(false);
}
catch (Exception e)
{
App.HandleException(e);
this.TryClose();
}
}
// I added the "flag" parameter, so that it matched the calls from
// your other code
protected async Task LoadData(bool flag)
{
try
{
this.WorkStarted("Loading data...");
TLoadedData loadedData;
ISession transaction = null;
// using (var transaction = this.DataItemRepository.BeginTransaction())
// {
loadedData = await this.LoadDataInternal(transaction);
// transaction.Commit();
// }
// NOTE: by changing the caller of this method so that it's
// initiated on the UI thread, now the continuations also run
// on the UI thread, and you don't need to explicitly invoke them there.
this.SetLoadedData(loadedData);
}
finally
{
this.WorkFinished();
}
}
}