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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在viewmodel中的异步任务后更新UI相关的知识,希望对你有一定的参考价值。

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

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

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

我的代码:

  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       
            });         
        }
    }
答案

我建议你使用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,并且您将认为适用于您的应用的异常将被处理。

另一答案

怎么样

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

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

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

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

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

另一答案

您可以创建异步工厂方法并使构造函数为私有。然后,您调用该方法来创建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.BeginInvokeOnMainThread
  • 我们可能总是将DataBinding与Label一起使用,并且仅使用事件订阅更新ViewModel中的Binding Path的值。

string _message;

public string Message

        {
            get => _message;
            set
            {
                _message = value;
            }
        }
myEvent += () => Message = "New Value";

<Label Text = "{Binding Message}"/>

以上是关于如何在viewmodel中的异步任务后更新UI的主要内容,如果未能解决你的问题,请参考以下文章

如何在运行任务后或在任务运行期间更新 Asp .Net 中的 UI?

Android 异步更新UI-线程池-Future-Handler实例分析

Kendo UI:在 TextBox 中的 KeyPress 上强制更新 ViewModel

为啥将 ViewModel 添加到我的 SwiftUI 应用程序后我的 UI 没有更新?

尝试更新 ViewModel 中的 Ui 属性时 DispatcherQueue null

更新按钮的异步任务