Skip to content

Commit

Permalink
DispatcherHelper class improved, minor fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio0694 committed Jul 13, 2017
1 parent 11cc001 commit 685385e
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ public static async Task<AttachedAnimatableCompositionEffect<T>> AttachCompositi
where T : FrameworkElement
{
// Get the compositor
Visual visual = await DispatcherHelper.GetFromUIThreadAsync(element.GetVisual);
Visual visual = await element.Dispatcher.GetFromUIThreadAsync(element.GetVisual);
Compositor compositor = visual.Compositor;

// Create the saturation effect and the effect factory
Expand All @@ -450,7 +450,7 @@ public static async Task<AttachedAnimatableCompositionEffect<T>> AttachCompositi
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
await AddToTreeAndBindSizeAsync(visual, element, sprite);
if (initiallyVisible) await DispatcherHelper.RunOnUIThreadAsync(() => element.Opacity = 1);
if (initiallyVisible) await element.Dispatcher.RunOnUIThreadAsync(() => element.Opacity = 1);
return new AttachedAnimatableCompositionEffect<T>(element, sprite, new CompositionAnimationParameters(animationPropertyName, on, off), disposeOnUnload);
}

Expand All @@ -468,7 +468,7 @@ public static async Task<AttachedAnimatableCompositionEffect<T>> AttachCompositi
[NotNull] this T element, float on, float off, bool initiallyVisible, bool disposeOnUnload = false) where T : FrameworkElement
{
// Get the compositor
Visual visual = await DispatcherHelper.GetFromUIThreadAsync(element.GetVisual);
Visual visual = await element.Dispatcher.GetFromUIThreadAsync(element.GetVisual);
Compositor compositor = visual.Compositor;

// Create the blur effect and the effect factory
Expand All @@ -491,7 +491,7 @@ public static async Task<AttachedAnimatableCompositionEffect<T>> AttachCompositi
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
await AddToTreeAndBindSizeAsync(visual, element, sprite);
if (initiallyVisible) await DispatcherHelper.RunOnUIThreadAsync(() => element.Opacity = 1);
if (initiallyVisible) await element.Dispatcher.RunOnUIThreadAsync(() => element.Opacity = 1);
return new AttachedAnimatableCompositionEffect<T>(element, sprite, new CompositionAnimationParameters(animationPropertyName, on, off), disposeOnUnload);
}

Expand Down Expand Up @@ -522,7 +522,7 @@ public static async Task<AttachedAnimatableCompositionEffect<T>> AttachCompositi
where T : FrameworkElement
{
// Get the compositor
Visual visual = await DispatcherHelper.GetFromUIThreadAsync(element.GetVisual);
Visual visual = await element.Dispatcher.GetFromUIThreadAsync(element.GetVisual);
Compositor compositor = visual.Compositor;

// Create the blur effect and the effect factory
Expand Down Expand Up @@ -561,7 +561,7 @@ public static async Task<AttachedAnimatableCompositionEffect<T>> AttachCompositi
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
await AddToTreeAndBindSizeAsync(target.GetVisual(), target, sprite);
if (initiallyVisible) await DispatcherHelper.RunOnUIThreadAsync(() => element.Opacity = 1);
if (initiallyVisible) await element.Dispatcher.RunOnUIThreadAsync(() => element.Opacity = 1);
return new AttachedAnimatableCompositionEffect<T>(target, sprite, new CompositionAnimationParameters(animationPropertyName, on, off), disposeOnUnload);
}

Expand Down Expand Up @@ -596,7 +596,7 @@ public static async Task<AttachedCompositeAnimatableCompositionEffect<T>> Attach
where T : FrameworkElement
{
// Get the compositor
Visual visual = await DispatcherHelper.GetFromUIThreadAsync(element.GetVisual);
Visual visual = await element.Dispatcher.GetFromUIThreadAsync(element.GetVisual);
Compositor compositor = visual.Compositor;

// Create the blur effect and the effect factory
Expand Down Expand Up @@ -648,7 +648,7 @@ public static async Task<AttachedCompositeAnimatableCompositionEffect<T>> Attach
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
await AddToTreeAndBindSizeAsync(target.GetVisual(), target, sprite);
if (initiallyVisible) await DispatcherHelper.RunOnUIThreadAsync(() => element.Opacity = 1);
if (initiallyVisible) await element.Dispatcher.RunOnUIThreadAsync(() => element.Opacity = 1);
return new AttachedCompositeAnimatableCompositionEffect<T>(target, sprite,
new Dictionary<String, CompositionAnimationValueParameters>
{
Expand All @@ -675,7 +675,7 @@ public static async Task<AttachedCompositeAnimatableCompositionEffect<T>> Attach
where T : FrameworkElement
{
// Get the compositor
Visual visual = await DispatcherHelper.GetFromUIThreadAsync(element.GetVisual);
Visual visual = await element.Dispatcher.GetFromUIThreadAsync(element.GetVisual);
Compositor compositor = visual.Compositor;

// Create the blur effect, the saturation effect and the effect factory
Expand Down Expand Up @@ -708,7 +708,7 @@ public static async Task<AttachedCompositeAnimatableCompositionEffect<T>> Attach
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
await AddToTreeAndBindSizeAsync(visual, element, sprite);
if (initiallyVisible) await DispatcherHelper.RunOnUIThreadAsync(() => element.Opacity = 1);
if (initiallyVisible) await element.Dispatcher.RunOnUIThreadAsync(() => element.Opacity = 1);

// Prepare and return the wrapped effect
return new AttachedCompositeAnimatableCompositionEffect<T>(element, sprite,
Expand All @@ -732,7 +732,7 @@ public static async Task<AttachedCompositeAnimatableCompositionEffect<T>> Attach
private static async Task AddToTreeAndBindSizeAsync([NotNull] Visual host, [NotNull] UIElement element, [NotNull] Visual visual)
{
// Add the shadow as a child of the host in the visual tree
await DispatcherHelper.RunOnUIThreadAsync(() => ElementCompositionPreview.SetElementChildVisual(element, visual));
await element.Dispatcher.RunOnUIThreadAsync(() => ElementCompositionPreview.SetElementChildVisual(element, visual));

// Make sure size of shadow host and shadow visual always stay in sync
ExpressionAnimation bindSizeAnimation = host.Compositor.CreateExpressionAnimation($"{nameof(host)}.Size");
Expand Down
4 changes: 2 additions & 2 deletions UICompositionAnimations/Brushes/CustomAcrylicBrush.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public double BlurAmount
/// Gets the <see cref="DependencyProperty"/> for the <see cref="BlurAmount"/> property
/// </summary>
public static readonly DependencyProperty BlurAmountProperty =
DependencyProperty.Register(nameof(BlurAmount), typeof(double), typeof(LightingBrush), new PropertyMetadata(8, OnBlurAmountPropertyChanged));
DependencyProperty.Register(nameof(BlurAmount), typeof(double), typeof(LightingBrush), new PropertyMetadata(8d, OnBlurAmountPropertyChanged));

private static async void OnBlurAmountPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Expand Down Expand Up @@ -131,7 +131,7 @@ public double TintMix
/// Gets the <see cref="DependencyProperty"/> for the <see cref="TintMix"/> property
/// </summary>
public static readonly DependencyProperty TintMixProperty =
DependencyProperty.Register(nameof(Tint), typeof(double), typeof(LightingBrush), new PropertyMetadata(0, OnTintMixPropertyChanged));
DependencyProperty.Register(nameof(Tint), typeof(double), typeof(LightingBrush), new PropertyMetadata(0d, OnTintMixPropertyChanged));

private static async void OnTintMixPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Expand Down
154 changes: 136 additions & 18 deletions UICompositionAnimations/Helpers/DispatcherHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.UI.Core;
using JetBrains.Annotations;

namespace UICompositionAnimations.Helpers
{
Expand All @@ -10,43 +11,57 @@ namespace UICompositionAnimations.Helpers
/// </summary>
public static class DispatcherHelper
{
/// <summary>
/// Gets the current CoreDispatcher instance
/// </summary>
private static CoreDispatcher _CoreDispatcher;

/// <summary>
/// Checks whether or not the current thread has access to the UI
/// </summary>
public static bool HasUIThreadAccess => (_CoreDispatcher ?? (_CoreDispatcher = CoreApplication.MainView.CoreWindow.Dispatcher)).HasThreadAccess;
#region Target dispatcher helpers

/// <summary>
/// Executes a given action on the UI thread without awaiting the operation
/// </summary>
/// <param name="dispatcher">The target dispatcher to use to schedule the callback execution</param>
/// <param name="callback">The action to execute on the UI thread</param>
public static void RunOnUIThread(Action callback)
[PublicAPI]
public static void RunOnUIThread([NotNull] this CoreDispatcher dispatcher, [NotNull] Action callback)
{
if (HasUIThreadAccess) callback();
if (dispatcher.HasThreadAccess) callback();
else
{
_CoreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
callback();
}).Forget();
}
}

/// <summary>
/// Executes a given async action on the UI thread without awaiting the operation
/// </summary>
/// <param name="dispatcher">The target dispatcher to use to schedule the callback execution</param>
/// <param name="asyncCallback">The action to execute on the UI thread</param>
[PublicAPI]
public static void RunOnUIThread([NotNull] this CoreDispatcher dispatcher, [NotNull] Func<Task> asyncCallback)
{
// Check the current thread
if (dispatcher.HasThreadAccess) asyncCallback();

// Schedule on the UI thread if necessary
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
asyncCallback();
}).Forget();
}

/// <summary>
/// Executes a given action on the UI thread and waits for it to be completed
/// </summary>
/// <param name="dispatcher">The target dispatcher to use to schedule the callback execution</param>
/// <param name="callback">The action to execute on the UI thread</param>
public static async Task RunOnUIThreadAsync(Action callback)
[PublicAPI]
public static async Task RunOnUIThreadAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Action callback)
{
if (HasUIThreadAccess) callback();
if (dispatcher.HasThreadAccess) callback();
else
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
_CoreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
callback();
tcs.SetResult(null);
Expand All @@ -55,21 +70,124 @@ public static async Task RunOnUIThreadAsync(Action callback)
}
}

/// <summary>
/// Executes a given action on the UI thread and waits for it to be completed
/// </summary>
/// <param name="dispatcher">The target dispatcher to use to schedule the callback execution</param>
/// <param name="asyncCallback">The action to execute on the UI thread</param>
[PublicAPI]
public static Task RunOnUIThreadAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func<Task> asyncCallback)
{
// Check the current thread
if (dispatcher.HasThreadAccess) return asyncCallback();

// Schedule on the UI thread if necessary
TaskCompletionSource<Task> tcs = new TaskCompletionSource<Task>();
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
tcs.SetResult(asyncCallback());
}).Forget();
return tcs.Task.Unwrap();
}

/// <summary>
/// Executes a given function on the UI thread and returns its result
/// </summary>
/// <typeparam name="T">The return type</typeparam>
/// <param name="dispatcher">The target dispatcher to use to schedule the callback execution</param>
/// <param name="function">The function to execute on the UI thread</param>
public static async ValueTask<T> GetFromUIThreadAsync<T>(Func<T> function)
[PublicAPI]
public static async ValueTask<T> GetFromUIThreadAsync<T>([NotNull] this CoreDispatcher dispatcher, [NotNull] Func<T> function)
{
if (HasUIThreadAccess) return function();
if (dispatcher.HasThreadAccess) return function();
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
_CoreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
T result = function();
tcs.SetResult(result);
}).Forget();
return await tcs.Task;
}

/// <summary>
/// Executes a given async function on the UI thread and returns its result
/// </summary>
/// <typeparam name="T">The return type</typeparam>
/// <param name="dispatcher">The target dispatcher to use to schedule the callback execution</param>
/// <param name="function">The async function to execute on the UI thread</param>
[PublicAPI]
public static Task<T> GetFromUIThreadAsync<T>([NotNull] this CoreDispatcher dispatcher, [NotNull] Func<Task<T>> function)
{
// Check the current thread
if (dispatcher.HasThreadAccess) return function();

// Schedule on the UI thread if necessary
TaskCompletionSource<Task<T>> tcs = new TaskCompletionSource<Task<T>>();
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
tcs.SetResult(function());
}).Forget();
return tcs.Task.Unwrap();
}

#endregion

#region Standalone helpers

/// <summary>
/// Gets the current CoreDispatcher instance
/// </summary>
private static CoreDispatcher _CoreDispatcher;

/// <summary>
/// Checks whether or not the current thread has access to the UI
/// </summary>
public static bool HasUIThreadAccess => (_CoreDispatcher ?? (_CoreDispatcher = CoreApplication.MainView.CoreWindow.Dispatcher)).HasThreadAccess;

/// <summary>
/// Executes a given action on the UI thread without awaiting the operation
/// </summary>
/// <param name="callback">The action to execute on the UI thread</param>
[PublicAPI]
public static void RunOnUIThread([NotNull] Action callback) => _CoreDispatcher.RunOnUIThread(callback);

/// <summary>
/// Executes a given async action on the UI thread without awaiting the operation
/// </summary>
/// <param name="asyncCallback">The action to execute on the UI thread</param>
[PublicAPI]
public static void RunOnUIThread([NotNull] Func<Task> asyncCallback) => _CoreDispatcher.RunOnUIThread(asyncCallback);

/// <summary>
/// Executes a given action on the UI thread and waits for it to be completed
/// </summary>
/// <param name="callback">The action to execute on the UI thread</param>
[PublicAPI]
public static Task RunOnUIThreadAsync([NotNull] Action callback) => _CoreDispatcher.RunOnUIThreadAsync(callback);

/// <summary>
/// Executes a given action on the UI thread and waits for it to be completed
/// </summary>
/// <param name="asyncCallback">The action to execute on the UI thread</param>
[PublicAPI]
public static Task RunOnUIThreadAsync([NotNull] Func<Task> asyncCallback) => _CoreDispatcher.RunOnUIThreadAsync(asyncCallback);

/// <summary>
/// Executes a given function on the UI thread and returns its result
/// </summary>
/// <typeparam name="T">The return type</typeparam>
/// <param name="function">The function to execute on the UI thread</param>
[PublicAPI]
public static ValueTask<T> GetFromUIThreadAsync<T>([NotNull] Func<T> function) => _CoreDispatcher.GetFromUIThreadAsync(function);

/// <summary>
/// Executes a given async function on the UI thread and returns its result
/// </summary>
/// <typeparam name="T">The return type</typeparam>
/// <param name="function">The async function to execute on the UI thread</param>
[PublicAPI]
public static Task<T> GetFromUIThreadAsync<T>([NotNull] Func<Task<T>> function) => _CoreDispatcher.GetFromUIThreadAsync(function);

#endregion
}
}
2 changes: 1 addition & 1 deletion UICompositionAnimations/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.8.0.0")]
[assembly: AssemblyVersion("2.8.1.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]
4 changes: 2 additions & 2 deletions UICompositionAnimations/UICompositionAnimations.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
<package >
<metadata>
<id>Sergio0694.UWP.UICompositionAnimations</id>
<version>2.8.0.0</version>
<version>2.8.1.0</version>
<title>UICompositionAnimations</title>
<description>A wrapper UWP PCL to work with Windows.UI.Composition and XAML animations, and Win2D effects</description>
<authors>Sergio Pedri</authors>
<owners>Sergio Pedri</owners>
<projectUrl>https://github.com/Sergio0694/UICompositionAnimations</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<releaseNotes>PointerHelper class added, minor bug fixes and improvements</releaseNotes>
<releaseNotes>DispatcherHelper class improved, minor bug fixes</releaseNotes>
<copyright>Copyright 2017</copyright>
<tags>uwp composition animations xaml csharp windows winrt universal app ui win2d graphics</tags>
</metadata>
Expand Down

0 comments on commit 685385e

Please sign in to comment.