如何在viewmodel中的异步任务后更新UI

时间:2016-08-01 10:57:54

标签: c# async-await xamarin.forms mvvm-light

在我的Xamarin.Forms pcl项目中,我有一个xaml页面label。我希望在label之后更新async task。在我的ViewModel constructor中,我为label设置了默认文字。并创建一个名为async Task的{​​{1}}函数。

问题1:我在哪里可以调用SomeTask()函数。无法在SomeTask()中调用async Task功能。

问题2:如何在constructor功能之后更新Label文字。

我的代码:

async Task SomeTask()

5 个答案:

答案 0 :(得分:1)

我建议您使用my NotifyTask type;它在我的MSDN article on asynchronous MVVM data binding中描述,我认为这是最简单的方法:

public class MyPageViewModel : ViewModelBase
{
  private NotifyTask<string> _selectedText;
  public NotifyTask<string> SelectedText => _selectedText;

  public MyPageViewModel()
  {
    _selectedText = NotifyTask.Create(SomeTask(), "Welcome");
  }

  private async Task<string> SomeTask()
  {            
    await Task.Delay(3000);
    return "Thanks";
  }
}

然后,您的数据绑定将更改为绑定到SelectedText.Result以显示“欢迎”,然后显示“谢谢”。数据绑定还有其他NotifyTask<T>属性,例如IsNotCompletedIsCompletedErrorMessage,它们允许您通过数据绑定处理故障情况。

如果您不想使用此类型,您可以自己做类似的事情:

public class MyPageViewModel : ViewModelBase
{
  private string _selectedText;
  public string SelectedText
  {
    get { return _selectedText; }
    set
    {
      if (_selectedText != value)
      {
        _selectedText = value;
        RaisePropertyNotifyChanged(); // However you're doing this.
      }
    }
  }

  public MyPageViewModel()
  {
    _selectedText = "Welcome";
    var _ = RunSomeTask();
  }

  private async Task RunSomeTask()
  {
    try
    {
      SelectedText = await SomeTask();
    }
    catch (Exception ex)
    {
      // TODO: Handle the exception.
      // It *must* be handled here, or else it will be silently ignored!
    }
  }

  private async Task<string> SomeTask()
  {            
    await Task.Delay(3000);
    return "Thanks";
  }
}

构造函数启动RunSomeTask操作,然后显式忽略其结果(请注意,这意味着将忽略所有异常)。 RunSomeTask负责运行SomeTask并处理其结果(和例外)。结果仅用于更新SelectedText,并且会根据您认为适合您的应用的情况处理异常。

答案 1 :(得分:0)

怎么样

public MyPageViewModel()
{
    _selectedText = "Welcome";   //Default text
    SomeTask().ContinueWith(previousTask => SelectedText = previousTask.Result);
}

答案 2 :(得分:0)

什么是someTask?即,应该从哪里调用?出于什么原因?这里需要上下文来了解调用函数可以接受的内容。该功能应该在页面的负载上运行吗?它可以晚点运行吗?如何通过用户输入调用?

我建议你可以调用一个静态方法,例如:

public static async Task<T> someTask() {
    Console.WriteLine("Asynchronous method called.");
}

然后,您可以从多个地方拨打此电话。这严格取决于需要调用方法的位置/时间。它可能位于Load事件处理程序中,以及其他位置。

答案 3 :(得分:0)

您可以创建异步工厂方法并使构造函数成为私有。然后,您调用该方法来创建MyPageViewModel的实例。在该方法中,您可以调用string str = await SomeTask

public class MyPageViewModel : ViewModelBase
{  
     public async MyPageViewModel CreateAsync()
     {
         var model = new MyPageViewModel();

         SelectedText = await SomeTask();

         return model;
     }        

     private MyPageViewModel ()
     {
         _selectedText = "Welcome";   //Default text
     }

     private Task<string> SomeTask()
     {            
         return Task.Run(async () =>
         {
             await Task.Delay(3000); //Dummy task. It will return the status of Task.
             return "Thanks";         //Update Text       
         });         
      }
}

所以不要像这样创建你的模型:

var model = new MyPageViewModel();

您可以像这样创建:

var model = await MyPageViewModel.CreateAsync();

答案 4 :(得分:0)

问题1:

使用委托和事件。

  1. 创建代表&amp;相关事件:

    • private delegate void MyDelegate();
    • private event MyDelegate myEvent;
  2. 在构造函数中订阅该事件:

    • myEvent += async () => await SomeTask();
  3. 在任何需要的地方执行活动:

    • myEvent(); //Note: Check the event for null, before executing
  4. 问题2:

    如果在非UI线程上,则:

    • 使用一些框架类来执行UI操作:例如 - Xamarin提供Device.BeginInvokeOnMainThread

    • 我们可能总是将DataBinding与Label一起使用,并且只使用事件订阅更新ViewModel中的Binding Path值。

    string _message;
    
    public string Message
    
            {
                get => _message;
                set
                {
                    _message = value;
                }
            }
    myEvent += () => Message = "New Value";
    
    <Label Text = "{Binding Message}"/>