Skip to content

Commit 8cf414a

Browse files
authored
Merge branch 'master' into aleader/win32-scheduled-notif-fix
2 parents 153f0a6 + 5424989 commit 8cf414a

File tree

7 files changed

+162
-11
lines changed

7 files changed

+162
-11
lines changed

Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageEx/ImageExLazyLoadingControl.xaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@
2828
<TextBlock HorizontalAlignment="Left"
2929
VerticalAlignment="Top"
3030
Foreground="OrangeRed"
31-
IsHitTestVisible="False"
32-
Text="Please scroll down to see the effect." />
31+
IsHitTestVisible="False"><Run Text="Please scroll down to see the effect." /><LineBreak /><Run Text="The default threshold for triggering lazy loading is 300 px." /></TextBlock>
3332
<Button Width="48"
3433
Height="48"
3534
HorizontalAlignment="Right"

Microsoft.Toolkit.Uwp.UI.Controls/ImageEx/ImageEx.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<Setter Property="Background" Value="Transparent" />
77
<Setter Property="Foreground" Value="{ThemeResource ApplicationForegroundThemeBrush}" />
88
<Setter Property="IsTabStop" Value="False" />
9+
<Setter Property="LazyLoadingThreshold" Value="300" />
910
<Setter Property="Template">
1011
<Setter.Value>
1112
<ControlTemplate TargetType="controls:ImageEx">

Microsoft.Toolkit.Uwp.UI.Controls/ImageEx/ImageExBase.Members.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ public partial class ImageExBase
4848
/// <summary>
4949
/// Identifies the <see cref="EnableLazyLoading"/> dependency property.
5050
/// </summary>
51-
public static readonly DependencyProperty EnableLazyLoadingProperty = DependencyProperty.Register(nameof(EnableLazyLoading), typeof(bool), typeof(ImageExBase), new PropertyMetadata(false));
51+
public static readonly DependencyProperty EnableLazyLoadingProperty = DependencyProperty.Register(nameof(EnableLazyLoading), typeof(bool), typeof(ImageExBase), new PropertyMetadata(false, EnableLazyLoadingChanged));
52+
53+
/// <summary>
54+
/// Identifies the <see cref="LazyLoadingThreshold"/> dependency property.
55+
/// </summary>
56+
public static readonly DependencyProperty LazyLoadingThresholdProperty = DependencyProperty.Register(nameof(LazyLoadingThreshold), typeof(double), typeof(ImageExBase), new PropertyMetadata(default(double), LazyLoadingThresholdChanged));
5257

5358
/// <summary>
5459
/// Returns a mask that represents the alpha channel of an image as a <see cref="CompositionBrush"/>
@@ -139,5 +144,40 @@ public bool EnableLazyLoading
139144
get { return (bool)GetValue(EnableLazyLoadingProperty); }
140145
set { SetValue(EnableLazyLoadingProperty, value); }
141146
}
147+
148+
/// <summary>
149+
/// Gets or sets a value indicating the threshold for triggering lazy loading.
150+
/// </summary>
151+
public double LazyLoadingThreshold
152+
{
153+
get { return (double)GetValue(LazyLoadingThresholdProperty); }
154+
set { SetValue(LazyLoadingThresholdProperty, value); }
155+
}
156+
157+
private static void EnableLazyLoadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
158+
{
159+
if (d is ImageExBase control)
160+
{
161+
var value = (bool)e.NewValue;
162+
if (value)
163+
{
164+
control.LayoutUpdated += control.ImageExBase_LayoutUpdated;
165+
166+
control.InvalidateLazyLoading();
167+
}
168+
else
169+
{
170+
control.LayoutUpdated -= control.ImageExBase_LayoutUpdated;
171+
}
172+
}
173+
}
174+
175+
private static void LazyLoadingThresholdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
176+
{
177+
if (d is ImageExBase control && control.EnableLazyLoading)
178+
{
179+
control.InvalidateLazyLoading();
180+
}
181+
}
142182
}
143183
}

Microsoft.Toolkit.Uwp.UI.Controls/ImageEx/ImageExBase.cs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Linq;
7+
using Microsoft.Toolkit.Uwp.Extensions;
8+
using Microsoft.Toolkit.Uwp.UI.Extensions;
69
using Windows.Foundation;
710
using Windows.UI.Xaml;
811
using Windows.UI.Xaml.Controls;
@@ -79,8 +82,6 @@ public abstract partial class ImageExBase : Control
7982
public ImageExBase()
8083
{
8184
LockObj = new object();
82-
83-
EffectiveViewportChanged += ImageExBase_EffectiveViewportChanged;
8485
}
8586

8687
/// <summary>
@@ -215,15 +216,47 @@ private void OnImageFailed(object sender, ExceptionRoutedEventArgs e)
215216
VisualStateManager.GoToState(this, FailedState, true);
216217
}
217218

218-
private void ImageExBase_EffectiveViewportChanged(FrameworkElement sender, EffectiveViewportChangedEventArgs args)
219+
private void ImageExBase_LayoutUpdated(object sender, object e)
220+
{
221+
InvalidateLazyLoading();
222+
}
223+
224+
private void InvalidateLazyLoading()
219225
{
220-
var bringIntoViewDistanceX = args.BringIntoViewDistanceX;
221-
var bringIntoViewDistanceY = args.BringIntoViewDistanceY;
226+
if (!IsLoaded)
227+
{
228+
_isInViewport = false;
229+
return;
230+
}
231+
232+
// Find the first ascendant ScrollViewer, if not found, use the root element.
233+
FrameworkElement hostElement = null;
234+
var ascendants = this.FindAscendants().OfType<FrameworkElement>();
235+
foreach (var ascendant in ascendants)
236+
{
237+
hostElement = ascendant;
238+
if (hostElement is ScrollViewer)
239+
{
240+
break;
241+
}
242+
}
243+
244+
if (hostElement == null)
245+
{
246+
_isInViewport = false;
247+
return;
248+
}
222249

223-
var width = ActualWidth;
224-
var height = ActualHeight;
250+
var controlRect = TransformToVisual(hostElement)
251+
.TransformBounds(new Rect(0, 0, ActualWidth, ActualHeight));
252+
var lazyLoadingThreshold = LazyLoadingThreshold;
253+
var hostRect = new Rect(
254+
0 - lazyLoadingThreshold,
255+
0 - lazyLoadingThreshold,
256+
hostElement.ActualWidth + (2 * lazyLoadingThreshold),
257+
hostElement.ActualHeight + (2 * lazyLoadingThreshold));
225258

226-
if (bringIntoViewDistanceX <= width && bringIntoViewDistanceY <= height)
259+
if (controlRect.IntersectsWith(hostRect))
227260
{
228261
_isInViewport = true;
229262

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Diagnostics.Contracts;
6+
using System.Runtime.CompilerServices;
7+
using Rect = Windows.Foundation.Rect;
8+
9+
namespace Microsoft.Toolkit.Uwp.Extensions
10+
{
11+
/// <summary>
12+
/// Extensions for the <see cref="Rect"/> type.
13+
/// </summary>
14+
public static class RectExtensions
15+
{
16+
/// <summary>
17+
/// Determines if a rectangle intersects with another rectangle.
18+
/// </summary>
19+
/// <param name="rect1">The first rectangle to test.</param>
20+
/// <param name="rect2">The second rectangle to test.</param>
21+
/// <returns>This method returns <see langword="true"/> if there is any intersection, otherwise <see langword="false"/>.</returns>
22+
[Pure]
23+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24+
public static bool IntersectsWith(this Rect rect1, Rect rect2)
25+
{
26+
if (rect1.IsEmpty || rect2.IsEmpty)
27+
{
28+
return false;
29+
}
30+
31+
return (rect1.Left <= rect2.Right) &&
32+
(rect1.Right >= rect2.Left) &&
33+
(rect1.Top <= rect2.Bottom) &&
34+
(rect1.Bottom >= rect2.Top);
35+
}
36+
}
37+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Diagnostics.CodeAnalysis;
6+
using Microsoft.Toolkit.Uwp.Extensions;
7+
using Microsoft.VisualStudio.TestTools.UnitTesting;
8+
using Windows.Foundation;
9+
10+
namespace UnitTests.Extensions
11+
{
12+
[TestClass]
13+
public class Test_RectExtensions
14+
{
15+
[TestCategory("RectExtensions")]
16+
[TestMethod]
17+
[DataRow(0, 0, 2, 2, 0, 0, 2, 2, true)]// Full intersection.
18+
[DataRow(0, 0, 2, 2, 1, 1, 2, 2, true)]// Partial intersection.
19+
[DataRow(0, 0, 2, 2, -2, 0, 2, 2, true)]// Left edge intersection.
20+
[DataRow(0, 0, 2, 2, 0, -2, 2, 2, true)]// Top edge intersection.
21+
[DataRow(0, 0, 2, 2, 2, 0, 2, 2, true)]// Right edge intersection.
22+
[DataRow(0, 0, 2, 2, 0, 2, 2, 2, true)]// Bottom edge intersection.
23+
[DataRow(0, 0, 2, 2, -2, -2, 2, 2, true)]// Left top corner(0, 0) intersection.
24+
[DataRow(0, 0, 2, 2, 2, -2, 2, 2, true)]// Right top corner(2, 0) intersection.
25+
[DataRow(0, 0, 2, 2, 2, 2, 2, 2, true)]// Right bottom corner(2, 2) intersection.
26+
[DataRow(0, 0, 2, 2, -2, 2, 2, 2, true)]// Left bottom corner(0, 2) intersection.
27+
[DataRow(0, 0, 2, 2, 3, 0, 2, 2, false)]// No intersection.
28+
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1117:Parameters should be on same line or separate lines", Justification = "Put the parameters of the same rectangle on the same line is clearer.")]
29+
public static void Test_RectExtensions_IntersectsWith(
30+
double rect1X, double rect1Y, double rect1Width, double rect1Height,
31+
double rect2X, double rect2Y, double rect2Width, double rect2Height,
32+
bool shouldIntersectsWith)
33+
{
34+
var rect1 = new Rect(rect1X, rect1Y, rect1Width, rect1Height);
35+
var rect2 = new Rect(rect2X, rect2Y, rect2Width, rect2Height);
36+
var isIntersectsWith = rect1.IntersectsWith(rect2);
37+
Assert.IsTrue(isIntersectsWith == shouldIntersectsWith);
38+
}
39+
}
40+
}

UnitTests/UnitTests.UWP/UnitTests.UWP.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
<Compile Include="Converters\Test_TypeToObjectConverter.cs" />
134134
<Compile Include="Extensions\Helpers\ObjectWithNullableBoolProperty.cs" />
135135
<Compile Include="Extensions\Test_PointExtensions.cs" />
136+
<Compile Include="Extensions\Test_RectExtensions.cs" />
136137
<Compile Include="Extensions\Test_SizeExtensions.cs" />
137138
<Compile Include="Extensions\Test_BitmapIconExtensionMarkupExtension.cs" />
138139
<Compile Include="Extensions\Test_FontIconSourceExtensionMarkupExtension.cs" />

0 commit comments

Comments
 (0)