如何在视图模型中异步任务后更新UI

本文关键字:任务 更新 UI 异步 视图 模型 | 更新日期: 2023-09-27 17:59:31

在我的Xamarin.Forms pcl项目中,我有一个包含labelxaml页面。我想在一些async task之后更新label。在我的ViewModel constructor中,我为label设置了默认文本。并创建一个名为SomeTask()async Task函数。

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

问题2:如何在async Task SomeTask()函数后更新Label文本。

我的代码:

  public class MyPageViewModel : ViewModelBase
    {  
        private String _selectedText;
        public String SelectedText
        {
            get { return _selectedText; }
            set {
                if (_selectedText != value)
                {
                    _selectedText = value;          
                }       
            }
        }
        public MyPageViewModel ()
        {
            _selectedText = "Welcome";   //Default text
        }
        private async Task<string> SomeTask()
        {            
            return await Task.Run(async () =>
            {
                await Task.Delay(3000); //Dummy task. It will return the status of Task.
                return "Thanks";         //Update Text       
            });         
        }
    }

如何在视图模型中异步任务后更新UI

我建议您使用我的NotifyTask类型;在我的MSDN文章中介绍了异步MVVM数据绑定,我认为这是最简单的方法:

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,并且将根据您认为适合您的应用程序的方式处理异常。

怎么样

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

您可以创建一个异步工厂方法并使构造函数私有。然后调用该方法来创建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();

问题1:

使用委托和事件。

  1. 创建委派&关联事件:

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

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

    • myEvent(); //Note: Check the event for null, before executing

问题2:

如果在非UI线程上,则:

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

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


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