Skip to content

Commit

Permalink
Code improvements from background threads
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio0694 committed Jun 26, 2017
1 parent 8c4707b commit 9a168e4
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ public static class AttachedCompositionEffectsFactory
/// <param name="blur">The amount of blur to apply to the element</param>
/// <param name="ms">The duration of the initial blur animation, in milliseconds</param>
/// <param name="disposeOnUnload">Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded</param>
[NotNull]
public static AttachedStaticCompositionEffect<T> AttachCompositionBlurEffect<T>(
/// <remarks>This method returns a <see cref="ValueTask{TResult}"/> instance and runs synchronously if called on the UI thread</remarks>
[ItemNotNull]
public static async ValueTask<AttachedStaticCompositionEffect<T>> AttachCompositionBlurEffect<T>(
[NotNull] this T element, float blur, int ms, bool disposeOnUnload = false) where T : FrameworkElement
{
// Get the visual and the compositor
Expand All @@ -61,10 +62,10 @@ public static AttachedStaticCompositionEffect<T> AttachCompositionBlurEffect<T>(
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
sprite.Size = new Vector2((float)element.ActualWidth, (float)element.ActualHeight);
AddToTreeAndBindSize(visual, element, sprite);
await AddToTreeAndBindSizeAsync(visual, element, sprite);

// Animate the blur amount
effectBrush.StartAnimationAsync("Blur.BlurAmount", blur, TimeSpan.FromMilliseconds(ms));
effectBrush.StartAnimationAsync("Blur.BlurAmount", blur, TimeSpan.FromMilliseconds(ms)).Forget();

// Prepare and return the manager
return new AttachedStaticCompositionEffect<T>(element, sprite, disposeOnUnload);
Expand Down Expand Up @@ -161,7 +162,7 @@ const String
sprite.StopAnimation("Opacity");
sprite.Opacity = 0;
}
AddToTreeAndBindSize(target.GetVisual(), target, sprite);
await AddToTreeAndBindSizeAsync(target.GetVisual(), target, sprite);
if (fadeIn)
{
// Fade the effect in
Expand All @@ -181,8 +182,9 @@ const String
/// <typeparam name="T">The type of element to use to host the effect</typeparam>
/// <param name="element">The target element</param>
/// <param name="disposeOnUnload">Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded</param>
[NotNull]
public static AttachedStaticCompositionEffect<T> AttachCompositionHostBackdropBlurEffect<T>(
/// <remarks>This method returns a <see cref="ValueTask{TResult}"/> instance and runs synchronously if called on the UI thread</remarks>
[ItemNotNull]
public static async ValueTask<AttachedStaticCompositionEffect<T>> AttachCompositionHostBackdropBlurEffect<T>(
[NotNull] this T element, bool disposeOnUnload = false) where T : FrameworkElement
{
// Setup the host backdrop effect
Expand All @@ -191,7 +193,7 @@ public static AttachedStaticCompositionEffect<T> AttachCompositionHostBackdropBl
CompositionBackdropBrush brush = compositor.CreateHostBackdropBrush();
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = brush;
AddToTreeAndBindSize(visual, element, sprite);
await AddToTreeAndBindSizeAsync(visual, element, sprite);
return new AttachedStaticCompositionEffect<T>(element, sprite, disposeOnUnload);
}

Expand Down Expand Up @@ -263,7 +265,7 @@ public static async Task<AttachedStaticCompositionEffect<T>> AttachCompositionCu
// Create the sprite to display and add it to the visual tree
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
AddToTreeAndBindSize(visual, element, sprite);
await AddToTreeAndBindSizeAsync(visual, element, sprite);
return new AttachedStaticCompositionEffect<T>(element, sprite, disposeOnUnload);
}

Expand Down Expand Up @@ -306,7 +308,7 @@ public static async Task<AttachedAnimatableCompositionEffect<T>> AttachCompositi
// Assign the effect to a brush and display it
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
AddToTreeAndBindSize(visual, element, sprite);
await AddToTreeAndBindSizeAsync(visual, element, sprite);
if (initiallyVisible) await DispatcherHelper.RunOnUIThreadAsync(() => element.Opacity = 1);
return new AttachedAnimatableCompositionEffect<T>(element, sprite, new CompositionAnimationParameters(animationPropertyName, on, off), disposeOnUnload);
}
Expand Down Expand Up @@ -347,7 +349,7 @@ public static async Task<AttachedAnimatableCompositionEffect<T>> AttachCompositi
// Assign the effect to a brush and display it
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
AddToTreeAndBindSize(visual, element, sprite);
await AddToTreeAndBindSizeAsync(visual, element, sprite);
if (initiallyVisible) await DispatcherHelper.RunOnUIThreadAsync(() => element.Opacity = 1);
return new AttachedAnimatableCompositionEffect<T>(element, sprite, new CompositionAnimationParameters(animationPropertyName, on, off), disposeOnUnload);
}
Expand Down Expand Up @@ -417,7 +419,7 @@ public static async Task<AttachedAnimatableCompositionEffect<T>> AttachCompositi
// Assign the effect to a brush and display it
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
AddToTreeAndBindSize(target.GetVisual(), target, sprite);
await AddToTreeAndBindSizeAsync(target.GetVisual(), target, sprite);
if (initiallyVisible) await DispatcherHelper.RunOnUIThreadAsync(() => element.Opacity = 1);
return new AttachedAnimatableCompositionEffect<T>(target, sprite, new CompositionAnimationParameters(animationPropertyName, on, off), disposeOnUnload);
}
Expand Down Expand Up @@ -504,7 +506,7 @@ public static async Task<AttachedCompositeAnimatableCompositionEffect<T>> Attach
// Assign the effect to a brush and display it
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
AddToTreeAndBindSize(target.GetVisual(), target, sprite);
await AddToTreeAndBindSizeAsync(target.GetVisual(), target, sprite);
if (initiallyVisible) await DispatcherHelper.RunOnUIThreadAsync(() => element.Opacity = 1);
return new AttachedCompositeAnimatableCompositionEffect<T>(target, sprite,
new Dictionary<String, CompositionAnimationValueParameters>
Expand Down Expand Up @@ -564,7 +566,7 @@ public static async Task<AttachedCompositeAnimatableCompositionEffect<T>> Attach
// Assign the effect to a brush and display it
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
AddToTreeAndBindSize(visual, element, sprite);
await AddToTreeAndBindSizeAsync(visual, element, sprite);
if (initiallyVisible) await DispatcherHelper.RunOnUIThreadAsync(() => element.Opacity = 1);

// Prepare and return the wrapped effect
Expand All @@ -586,10 +588,10 @@ public static async Task<AttachedCompositeAnimatableCompositionEffect<T>> Attach
/// <param name="host">The <see cref="Visual"/> object that will host the effect</param>
/// <param name="element">The target <see cref="UIElement"/> (bound to the given visual) that will host the effect</param>
/// <param name="visual">The source <see cref="Visual"/> object to display</param>
private static void AddToTreeAndBindSize([NotNull] Visual host, [NotNull] UIElement element, [NotNull] Visual visual)
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
ElementCompositionPreview.SetElementChildVisual(element, visual);
await DispatcherHelper.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
57 changes: 0 additions & 57 deletions UICompositionAnimations/Helpers/DispatcherHelper.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
using Windows.UI.Core;

namespace UICompositionAnimations.Helpers
Expand Down Expand Up @@ -56,24 +55,6 @@ 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="asyncCallback">The action to execute on the UI thread</param>
public static Task RunOnUIThreadAsync(Func<Task> asyncCallback)
{
// Check the current thread
if (HasUIThreadAccess) return asyncCallback();

// Schedule on the UI thread if necessary
TaskCompletionSource<Task> tcs = new TaskCompletionSource<Task>();
_CoreDispatcher.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>
Expand All @@ -90,43 +71,5 @@ public static async ValueTask<T> GetFromUIThreadAsync<T>(Func<T> function)
}).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="function">The async function to execute on the UI thread</param>
public static Task<T> GetFromUIThreadAsync<T>(Func<Task<T>> function)
{
// Check the current thread
if (HasUIThreadAccess) return function();

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

/// <summary>
/// Executes a given async function that returns an async operation 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>
public static Task<T> GetFromUIThreadAsync<T>(Func<IAsyncOperation<T>> function)
{
// Check the current thread
if (HasUIThreadAccess) return function().AsTask();

// Schedule on the UI thread if necessary
TaskCompletionSource<Task<T>> tcs = new TaskCompletionSource<Task<T>>();
_CoreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
tcs.SetResult(function().AsTask());
}).Forget();
return tcs.Task.Unwrap();
}
}
}
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.5.3.0")]
[assembly: AssemblyVersion("2.5.4.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.5.3.0</version>
<version>2.5.4.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>Minor improvements</releaseNotes>
<releaseNotes>The attached effects are now working properly even when created on a background thread</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 9a168e4

Please sign in to comment.