IValueConverter的异步实现

IValueConverter的异步实现,第1张

IValueConverter的异步实现

您可能不想致电

Task.Result
,原因有两个。

首先,正如我在博客上详细解释的那样,除非您的代码已被广泛使用,否则您可能会陷入僵局。其次,您可能不想(同步)阻止您的UI。最好在从磁盘读取时暂时显示“正在加载…”或空白图像,并在读取完成后进行更新。

async``ConfigureAwait

因此,就我个人而言,我将把它作为ViewModel的一部分,而不是值转换器。我有一篇博客文章,描述了一些进行异步初始化的数据绑定友好方法。那将是我的第一选择。 值转换器启动 异步后台 *** 作只是不合适。

但是,如果您已经考虑了设计,并且确实认为您需要异步值转换器,那么您必须有所创新。值转换器的问题在于它们 必须
是同步的:数据绑定从数据上下文开始,评估路径,然后调用值转换。仅数据上下文和路径支持更改通知。

因此,您必须在数据上下文中使用一个(同步)值转换器将原始值转换为一个对数据绑定友好的

Task
类对象,然后您的属性绑定仅使用
Task
该类对象上的属性之一来获得结果。

这是我的意思的示例:

<TextBox Text="" Name="Input"/><TextBlock DataContext="{Binding ElementName=Input, Path=Text, Converter={local:MyAsyncValueConverter}}"Text="{Binding Path=Result}"/>

TextBox
只是一个输入框。第
TextBlock
一个将自己
DataContext
设置为
TextBox
的输入文本,通过“异步”转换器运行它。
TextBlock.Text
设置
Result
为该转换器的。

转换器非常简单:

public class MyAsyncValueConverter : MarkupExtension, IValueConverter{    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)    {        var val = (string)value;        var task = Task.Run(async () =>        { await Task.Delay(5000); return val + " done!";        });        return new TaskCompletionNotifier<string>(task);    }    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)    {        return null;    }    public override object Providevalue(IServiceProvider serviceProvider)    {        return this;    }}

转换器首先启动异步 *** 作以等待5秒钟,然后添加“完成!”。到输入字符串的末尾。转换器的结果不能只是简单的,

Task
因为
Task
它没有实现
IPropertyNotifyChanged
,所以我使用的类型将在我的AsyncEx库的下一个版本中使用。看起来像这样(在此示例中进行了简化;提供了完整的源代码):

// Watches a task and raises property-changed notifications when the task completes.public sealed class TaskCompletionNotifier<TResult> : INotifyPropertyChanged{    public TaskCompletionNotifier(Task<TResult> task)    {        Task = task;        if (!task.IsCompleted)        { var scheduler = (SynchronizationContext.Current == null) ? TaskScheduler.Current : TaskScheduler.FromCurrentSynchronizationContext(); task.ContinueWith(t => {     var propertyChanged = PropertyChanged;     if (propertyChanged != null)     {         propertyChanged(this, new PropertyChangedEventArgs("IsCompleted"));         if (t.IsCanceled)         {  propertyChanged(this, new PropertyChangedEventArgs("IsCanceled"));         }         else if (t.IsFaulted)         {  propertyChanged(this, new PropertyChangedEventArgs("IsFaulted"));  propertyChanged(this, new PropertyChangedEventArgs("ErrorMessage"));         }         else         {  propertyChanged(this, new PropertyChangedEventArgs("IsSuccessfullyCompleted"));  propertyChanged(this, new PropertyChangedEventArgs("Result"));         }     } }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, scheduler);        }    }    // Gets the task being watched. This property never changes and is never <c>null</c>.    public Task<TResult> Task { get; private set; }    Task ITaskCompletionNotifier.Task    {        get { return Task; }    }    // Gets the result of the task. Returns the default value of TResult if the task has not completed successfully.    public TResult Result { get { return (Task.Status == TaskStatus.RanToCompletion) ? Task.Result : default(TResult); } }    // Gets whether the task has completed.    public bool IsCompleted { get { return Task.IsCompleted; } }    // Gets whether the task has completed successfully.    public bool IsSuccessfullyCompleted { get { return Task.Status == TaskStatus.RanToCompletion; } }    // Gets whether the task has been canceled.    public bool IsCanceled { get { return Task.IsCanceled; } }    // Gets whether the task has faulted.    public bool IsFaulted { get { return Task.IsFaulted; } }    // Gets the error message for the original faulting exception for the task. Returns <c>null</c> if the task is not faulted.    public string ErrorMessage { get { return (InnerException == null) ? null : InnerException.Message; } }    public event PropertyChangedEventHandler PropertyChanged;}

通过将这些部分放在一起,我们创建了一个异步数据上下文,该上下文是值转换器的结果。便于数据绑定的

Task
包装器将仅使用默认结果(通常为
null
0
),直到
Task
完成为止。因此,包装程序
Result
Task.Result
:完全不同:它不会同步阻塞,也没有死锁的危险。

但是重申一下:我选择将异步逻辑放入ViewModel中,而不是值转换器中。



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5499057.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-12
下一篇 2022-12-13

发表评论

登录后才能评论

评论列表(0条)

保存