Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# IValueConverter的异步实现_C#_Windows Runtime_Async Await_C# 5.0_Winrt Async - Fatal编程技术网

C# IValueConverter的异步实现

C# IValueConverter的异步实现,c#,windows-runtime,async-await,c#-5.0,winrt-async,C#,Windows Runtime,Async Await,C# 5.0,Winrt Async,我想在IValueConverter中触发一个异步方法 有没有比通过调用Result属性强制同步更好的方法 public async Task<object> Convert(object value, Type targetType, object parameter, string language) { StorageFile file = value as StorageFile; if (file != null) { var im

我想在
IValueConverter
中触发一个异步方法

有没有比通过调用
Result
属性强制同步更好的方法

public async Task<object> Convert(object value, Type targetType, object parameter, string language)
{
    StorageFile file = value as StorageFile;

    if (file != null)
    {
        var image = ImageEx.ImageFromFile(file).Result;
        return image;
    }
    else
    {
        throw new InvalidOperationException("invalid parameter");
    }
}
公共异步任务转换(对象值、类型targetType、对象参数、字符串语言)
{
StorageFile=值为StorageFile;
如果(文件!=null)
{
var image=ImageEx.ImageFromFile(file.Result);
返回图像;
}
其他的
{
抛出新的InvalidOperationException(“无效参数”);
}
}

出于几个原因,您可能不想调用
Task.Result

首先,正如我在我的博客上详细解释的,除非您的
async
代码是使用
configurewait
编写的。其次,您可能不想(同步地)阻塞您的UI;从磁盘读取时最好临时显示“正在加载…”或空白图像,并在读取完成时更新

所以,就我个人而言,我会把这部分作为我的ViewModel的一部分,而不是一个值转换器。我有一篇博客文章描述了一些。那将是我的第一选择。让值转换器启动异步后台操作是不对的

然而,如果你已经考虑过你的设计,并且真的认为异步值转换器是你所需要的,那么你必须有点创造性。值转换器的问题是它们必须是同步的:数据绑定从数据上下文开始,计算路径,然后调用值转换。只有数据上下文和路径支持更改通知

因此,您必须在数据上下文中使用(同步)值转换器将原始值转换为数据绑定友好的
任务
-类对象,然后您的属性绑定只使用
任务
-类对象上的一个属性来获得结果

这里有一个例子来说明我的意思:

<TextBox Text="" Name="Input"/>
<TextBlock DataContext="{Binding ElementName=Input, Path=Text, Converter={local:MyAsyncValueConverter}}"
           Text="{Binding Path=Result}"/>
转换器首先启动异步操作以等待5秒,然后在输入字符串的末尾添加“done!”。转换器的结果不能仅仅是一个普通的
任务
,因为
任务
没有实现
IPropertyNotifyChanged
,所以我使用的类型将出现在我的下一个版本中。它看起来像这样(本例简化了;):

//监视任务并在任务完成时引发属性更改通知。
公共密封类TaskCompletionNotifier:INotifyPropertyChanged
{
公共任务完成通知程序(任务)
{
任务=任务;
如果(!task.IsCompleted)
{
var scheduler=(SynchronizationContext.Current==null)?TaskScheduler.Current:TaskScheduler.FromCurrentSynchronizationContext();
task.ContinueWith(t=>
{
var propertyChanged=propertyChanged;
if(propertyChanged!=null)
{
房地产变更(这是指新的房地产变更开发项目(“已完成”);
如果(t.IsCanceled)
{
房地产变更(这是指新的房地产变更数据(“已取消”);
}
否则,如果(t.IsFaulted)
{
propertyChanged(这是新的PropertyChangedEventArgs(“IsFaulted”);
propertyChanged(这是新的PropertyChangedEventArgs(“ErrorMessage”);
}
其他的
{
房地产变更(此,新的房地产变更已完成(“IsSuccessfullyCompleted”);
propertyChanged(此,新PropertyChangedEventArgs(“结果”);
}
}
},
取消令牌。无,
TaskContinuationOptions.Executes同步执行,
调度程序);
}
}
//获取正在监视的任务。此属性从不更改,也从不为null。
公共任务任务{get;private set;}
任务ITaskCompletionNotifier.Task
{
获取{return Task;}
}
//获取任务的结果。如果任务未成功完成,则返回TResult的默认值。
公共TResult结果{get{return(Task.Status==TaskStatus.RanToCompletion)?Task.Result:default(TResult);}
//获取任务是否已完成。
public bool IsCompleted{get{return Task.IsCompleted;}
//获取任务是否已成功完成。
public bool IsSuccessfullyCompleted{get{return Task.Status==TaskStatus.RanToCompletion;}
//获取任务是否已取消。
public bool IsCanceled{get{return Task.IsCanceled;}
//获取任务是否出现错误。
public bool出现故障{get{return Task.IsFaulted;}}
//获取任务的原始故障异常的错误消息。如果任务没有故障,则返回null。
公共字符串ErrorMessage{get{return(InnerException==null)?null:InnerException.Message;}
公共事件属性更改事件处理程序属性更改;
}
通过将这些部分放在一起,我们创建了一个异步数据上下文,它是值转换器的结果。数据绑定友好的
任务
包装器将只使用默认结果(通常为
null
0
),直到
任务
完成。因此,包装器的
结果
任务.Result
大不相同:它不会同步阻塞,也不会出现死锁的危险


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

另一种方法是创建支持异步源或数据的控件

下面是带有图像的示例

    public class AsyncSourceCachedImage : CachedImage
{
    public static BindableProperty AsyncSourceProperty = BindableProperty.Create(nameof(AsyncSource), typeof(Task<Xamarin.Forms.ImageSource>), typeof(AsyncSourceSvgCachedImage), null, propertyChanged: SourceAsyncPropertyChanged);

    public Task<Xamarin.Forms.ImageSource> AsyncSource
    {
        get { return (Task<Xamarin.Forms.ImageSource>)GetValue(AsyncSourceProperty); }
        set { SetValue(AsyncSourceProperty, value); }
    }

    private static async void SourceAsyncPropertyChanged(BindableObject bindable, object oldColor, object newColor)
    {
        var view = bindable as AsyncSourceCachedImage;
        var taskForImageSource = newColor as Task<Xamarin.Forms.ImageSource>;

        if (taskForImageSource != null)
        {
            var awaitedImageSource = await taskForImageSource;

            view.Source = awaitedImageSource;
        }
    }
}
公共类AsyncSourceCachedImage:CachedImage
{
公共静态BindableProperty异步源
// 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;
}
    public class AsyncSourceCachedImage : CachedImage
{
    public static BindableProperty AsyncSourceProperty = BindableProperty.Create(nameof(AsyncSource), typeof(Task<Xamarin.Forms.ImageSource>), typeof(AsyncSourceSvgCachedImage), null, propertyChanged: SourceAsyncPropertyChanged);

    public Task<Xamarin.Forms.ImageSource> AsyncSource
    {
        get { return (Task<Xamarin.Forms.ImageSource>)GetValue(AsyncSourceProperty); }
        set { SetValue(AsyncSourceProperty, value); }
    }

    private static async void SourceAsyncPropertyChanged(BindableObject bindable, object oldColor, object newColor)
    {
        var view = bindable as AsyncSourceCachedImage;
        var taskForImageSource = newColor as Task<Xamarin.Forms.ImageSource>;

        if (taskForImageSource != null)
        {
            var awaitedImageSource = await taskForImageSource;

            view.Source = awaitedImageSource;
        }
    }
}