Skip to content

Instantly share code, notes, and snippets.

@Platonenkov
Created December 4, 2022 14:28
Show Gist options
  • Save Platonenkov/4c2cb02dacc0fa0df1dcce6920a6c814 to your computer and use it in GitHub Desktop.
Save Platonenkov/4c2cb02dacc0fa0df1dcce6920a6c814 to your computer and use it in GitHub Desktop.
Async Converter for wpf
public class AsyncConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var task = Task.Run(async () =>
{
await Task.Delay(3000);
return "Async operation";
});
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;
}
}
<Image DataContext="{Binding Converter={converters:NFTImageLoadConverter}}"
Source="{Binding Path=Result, UpdateSourceTrigger=PropertyChanged}" />
using System;
using System.ComponentModel;
using System.Threading.Tasks;
namespace TaskConverters;
public interface ITaskCompletionNotifier<TResult> : ITaskCompletionNotifier
{
new Task<TResult> Task { get; }
TResult Result { get; }
}
/// <summary>
/// Watches a task and raises property-changed notifications when the task completes.
/// </summary>
public interface ITaskCompletionNotifier : INotifyPropertyChanged
{
Task Task { get; }
TaskStatus Status { get; }
bool IsCompleted { get; }
bool IsSuccessfullyCompleted { get; }
bool IsCanceled { get; }
bool IsFaulted { get; }
AggregateException Exception { get; }
Exception InnerException { get; }
string ErrorMessage { get; }
}
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
namespace TaskConverters;
/// <summary>
/// Watches a task and raises property-changed notifications when the task completes.
/// </summary>
/// <typeparam name="TResult"></typeparam>
public sealed class TaskCompletionNotifier<TResult> : ITaskCompletionNotifier<TResult>
{
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 => Task;
public TResult Result => (Task.Status == TaskStatus.RanToCompletion) ? Task.Result : default(TResult);
public TaskStatus Status => Task.Status;
public bool IsCompleted => Task.IsCompleted;
public bool IsSuccessfullyCompleted => Task.Status == TaskStatus.RanToCompletion;
public bool IsCanceled => Task.IsCanceled;
public bool IsFaulted => Task.IsFaulted;
public AggregateException Exception => Task.Exception;
public Exception InnerException => (Exception == null) ? null : Exception.InnerException;
public string ErrorMessage => (InnerException == null) ? null : InnerException.Message;
public event PropertyChangedEventHandler PropertyChanged;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment