如何加载我的viewmodel并仍然获取事件?

时间:2013-12-01 09:52:44

标签: c# windows-phone-8 async-await

我正在尝试实现MVVM,在ViewModel中我正在做一些异步提取数据。为此,我试图在构造函数中加载数据:

MyModel Model { get; set; }

public MyViewModel()
{
  Model = new MyModel();
  Model.Foo = await LoadDataFromIsolatedStorage();

但这不是有效的,因为你不能将异步附加到构造函数。所以我尝试了一个公共静态加载函数:

MyModel Model { get; set; }

public MyViewModel()
{
  Model = new MyModel();
}

async public static void Load()
{
  Model.Foo = await LoadDataFromIsolatedStorage();

但是WP8抱怨它Cannot await void。因为您将设置ViewModel并将其绑定到视图后面的代码中的View。无聊。 最后一个修复是使Load函数返回一个ViewModel,这样你在视图后面的代码可以做类似的事情:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
  MyViewModel viewModel = await MyViewModel.Load();

使用以下代码加载:

MyModel Model { get; set; }

public MyViewModel()
{
  Model = new MyModel();
}

async public static Task<MyViewModel> Load()
{
  MyViewModel viewModel = new MyViewModel();
  viewModel.Model.Foo = await LoadDataFromIsolatedStorage();
  return viewModel;

现在,问题在于如果加载的数据应该强制应用程序导航到另一个页面,我无法控制。让我们说MyViewModel从隔离存储中加载一个变量,然后应该让应用程序导航到另一个页面?

我已经将eventlistener设置为MyViewModel以使应用程序导航,但是当我启动它时我无法做到这一点。

不适用于事件:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
  MyViewModel viewModel = await MyViewModel.Load();
  viewModel.NavigationAction += viewmodel_NavigationAction;
}

void viewmodel_NavigationAction(sender, args)
{
  NavigationService.Navigate(...)
}

会工作,但我“不能等待无效”:

async protected override void OnNavigatedTo(NavigationEventArgs e)
{
  MyViewModel viewModel = new MyViewModel();
  viewModel.NavigationAction += viewmodel_NavigationAction;
  await viewModel.Load(); // given the Load only is a async void and not Task<T>
}

void viewmodel_NavigationAction(sender, args)
{
  NavigationService.Navigate(...)
}

2 个答案:

答案 0 :(得分:1)

我认为此代码块“不起作用”,因为在加载数据后事件设置得太晚了:

MyViewModel viewModel = await MyViewModel.Load();
viewModel.NavigationAction += viewmodel_NavigationAction;

在这种情况下,修复上一个代码块非常简单:让Load返回Task并且它会起作用。 Taskasync相当于void;除非您正在编写事件处理程序,否则不应使用async void。有关详细信息,请参阅我的best practices文章。

但是,即使你开始工作,你也会得到一个空视图,直到VM加载为止;如果您的负载可能需要很长时间(或者错误输出,例如,如果没有网络连接),这可能是糟糕的用户体验。

您可能需要考虑使用NotifyTaskCompletion from my AsyncEx library,我describe on my blog。该模式允许您(立即)创建处于“加载”状态的VM,该状态将(通过INotifyPropertyChanged)转换为“加载”或“加载错误”状态。这种模式提供了更好的用户体验,IMO。

答案 1 :(得分:-1)

roliu编写的内容非常有意义,因为async / await模式的整个概念基于Tasks。在编写异步方法时,您总是需要返回一个任务以确保以下await可以正常工作。通常编译器会在后台进行一些特殊的替换,所以你不必关心它。

由于异步操作,你永远不会得到一个bool - 而是一个通用的bool类型的任务!确保您理解MSDN所述的模式。

然后你可以创建这样的东西。它不是Win8应用程序。为简单起见,我选择它作为控制台应用程序。

class Program
    {
        static void Main(string[] args)
        {
            DoWork();
        }

        static async void DoWork()
        {
            await YourVoidMethod();
        }

        static Task YourVoidMethod()
        {
            Task task = Task.Run(() =>
                {
                    // Your payload code
                }
            );

            return task; 
        }
    }

正如提示:使用GUI时,您还需要与调度程序一起工作。更改UI线程的数据时,否则可能会生成跨线程异常。