Skip to content

Commit

Permalink
Merge branch 'main' into winui
Browse files Browse the repository at this point in the history
# Conflicts:
#	CommunityToolkit.WinUI.UI.Behaviors/Viewport/ViewportBehavior.Properties.cs
#	UITests/UITests.App/UITests.App.csproj
  • Loading branch information
azchohfi committed Oct 19, 2021
2 parents 1a7fb53 + 44ad0cc commit 85bfe0e
Show file tree
Hide file tree
Showing 13 changed files with 334 additions and 86 deletions.
6 changes: 4 additions & 2 deletions CommunityToolkit.Diagnostics/Guard.String.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public static void IsNullOrEmpty(string? text, string name)
/// </summary>
/// <param name="text">The input <see cref="string"/> instance to test.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="text"/> is <see langword="null"/> or empty.</exception>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="text"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown if <paramref name="text"/> is empty.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotNullOrEmpty([NotNull] string? text, string name)
{
Expand Down Expand Up @@ -89,7 +90,8 @@ public static void IsNullOrWhitespace(string? text, string name)
/// </summary>
/// <param name="text">The input <see cref="string"/> instance to test.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="text"/> is <see langword="null"/> or whitespace.</exception>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="text"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown if <paramref name="text"/> is whitespace.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotNullOrWhiteSpace([NotNull] string? text, string name)
{
Expand Down
29 changes: 26 additions & 3 deletions CommunityToolkit.Diagnostics/Internals/Guard.String.ThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace CommunityToolkit.Diagnostics
{
Expand All @@ -27,12 +28,23 @@ public static void ThrowArgumentExceptionForIsNullOrEmpty(string? text, string n
}

/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="Guard.IsNotNullOrEmpty"/> fails.
/// Throws an <see cref="ArgumentNullException"/> or <see cref="ArgumentException"/> when <see cref="Guard.IsNotNullOrEmpty"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotNullOrEmpty(string? text, string name)
{
throw new ArgumentException($"Parameter {AssertString(name)} (string) must not be null or empty, was {(text is null ? "null" : "empty")}", name);
[MethodImpl(MethodImplOptions.NoInlining)]
static Exception GetException(string? text, string name)
{
if (text is null)
{
return new ArgumentNullException(name, $"Parameter {AssertString(name)} (string) must not be null or empty, was null");
}

return new ArgumentException($"Parameter {AssertString(name)} (string) must not be null or empty, was empty", name);
}

throw GetException(text, name);
}

/// <summary>
Expand All @@ -50,7 +62,18 @@ public static void ThrowArgumentExceptionForIsNullOrWhiteSpace(string? text, str
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotNullOrWhiteSpace(string? text, string name)
{
throw new ArgumentException($"Parameter {AssertString(name)} (string) must not be null or whitespace, was {(text is null ? "null" : "whitespace")}", name);
[MethodImpl(MethodImplOptions.NoInlining)]
static Exception GetException(string? text, string name)
{
if (text is null)
{
return new ArgumentNullException(name, $"Parameter {AssertString(name)} (string) must not be null or whitespace, was null");
}

return new ArgumentException($"Parameter {AssertString(name)} (string) must not be null or whitespace, was whitespace", name);
}

throw GetException(text, name);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
Height="200"
Background="Gray">
<interactivity:Interaction.Behaviors>
<behaviors:ViewportBehavior x:Name="ViewportBehavior" />
<behaviors:ViewportBehavior x:Name="ViewportBehavior" IsAlwaysOn="True" />
</interactivity:Interaction.Behaviors>
<Image x:Name="EffectElement"
Width="100"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<ui:SurfaceDialOptions />
</ui:TextBoxExtensions.SurfaceDialOptions>
</TextBox>
<TextBlock x:Key="OnDeviceExtension" Text="{ui:OnDevice Desktop=Desktop, Xbox=Xbox, Default=Default}" />
<controls:SwitchPresenter x:Key="SwitchPresenterControl">
<controls:CaseCollection>
<controls:Case IsDefault="True" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using Microsoft.UI.Xaml;
using Microsoft.Xaml.Interactivity;

namespace CommunityToolkit.WinUI.UI.Behaviors
{
/// <summary>
/// A class for listening to an element enter or exit the ScrollViewer viewport
/// </summary>
public partial class ViewportBehavior
{
/// <summary>
/// The IsFullyInViewport value of the associated element
/// </summary>
public static readonly DependencyProperty IsFullyInViewportProperty =
DependencyProperty.Register(nameof(IsFullyInViewport), typeof(bool), typeof(ViewportBehavior), new PropertyMetadata(default(bool), OnIsFullyInViewportChanged));

/// <summary>
/// The IsInViewport value of the associated element
/// </summary>
public static readonly DependencyProperty IsInViewportProperty =
DependencyProperty.Register(nameof(IsInViewport), typeof(bool), typeof(ViewportBehavior), new PropertyMetadata(default(bool), OnIsInViewportChanged));

/// <summary>
/// The IsAlwaysOn value of the associated element
/// </summary>
public static readonly DependencyProperty IsAlwaysOnProperty =
DependencyProperty.Register(nameof(IsAlwaysOn), typeof(bool), typeof(ViewportBehavior), new PropertyMetadata(default(bool)));

/// <summary>
/// Gets or sets a value indicating whether this behavior will remain attached after the associated element enters the viewport. When false, the behavior will remove itself after entering.
/// </summary>
public bool IsAlwaysOn
{
get { return (bool)GetValue(IsAlwaysOnProperty); }
set { SetValue(IsAlwaysOnProperty, value); }
}

/// <summary>
/// Gets a value indicating whether associated element is fully in the ScrollViewer viewport
/// </summary>
public bool IsFullyInViewport
{
get { return (bool)GetValue(IsFullyInViewportProperty); }
private set { SetValue(IsFullyInViewportProperty, value); }
}

/// <summary>
/// Gets a value indicating whether associated element is in the ScrollViewer viewport
/// </summary>
public bool IsInViewport
{
get { return (bool)GetValue(IsInViewportProperty); }
private set { SetValue(IsInViewportProperty, value); }
}

/// <summary>
/// Event tracking when the object is fully within the viewport or not
/// </summary>
/// <param name="d">DependencyObject</param>
/// <param name="e">EventArgs</param>
private static void OnIsFullyInViewportChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = (ViewportBehavior)d;
var value = (bool)e.NewValue;

if (value)
{
obj.EnteredViewport?.Invoke(obj.AssociatedObject, EventArgs.Empty);

if (!obj.IsAlwaysOn)
{
Interaction.GetBehaviors(obj.AssociatedObject).Remove(obj);
}
}
else
{
obj.ExitingViewport?.Invoke(obj.AssociatedObject, EventArgs.Empty);
}
}

/// <summary>
/// Event tracking the state of the object as it moves into and out of the viewport
/// </summary>
/// <param name="d">DependencyObject</param>
/// <param name="e">EventArgs</param>
private static void OnIsInViewportChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = (ViewportBehavior)d;
var value = (bool)e.NewValue;

if (value)
{
obj.EnteringViewport?.Invoke(obj.AssociatedObject, EventArgs.Empty);
}
else
{
obj.ExitedViewport?.Invoke(obj.AssociatedObject, EventArgs.Empty);
}
}
}
}
82 changes: 2 additions & 80 deletions CommunityToolkit.WinUI.UI.Behaviors/Viewport/ViewportBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,15 @@
namespace CommunityToolkit.WinUI.UI.Behaviors
{
/// <summary>
/// A class for listening element enter or exit the ScrollViewer viewport
/// A class for listening to an element enter or exit the ScrollViewer viewport
/// </summary>
public class ViewportBehavior : BehaviorBase<FrameworkElement>
public partial class ViewportBehavior : BehaviorBase<FrameworkElement>
{
/// <summary>
/// The ScrollViewer hosting this element.
/// </summary>
private ScrollViewer _hostScrollViewer;

/// <summary>
/// The IsFullyInViewport value of the associated element
/// </summary>
public static readonly DependencyProperty IsFullyInViewportProperty =
DependencyProperty.Register(nameof(IsFullyInViewport), typeof(bool), typeof(ViewportBehavior), new PropertyMetadata(default(bool), OnIsFullyInViewportChanged));

/// <summary>
/// The IsInViewport value of the associated element
/// </summary>
public static readonly DependencyProperty IsInViewportProperty =
DependencyProperty.Register(nameof(IsInViewport), typeof(bool), typeof(ViewportBehavior), new PropertyMetadata(default(bool), OnIsInViewportChanged));

/// <summary>
/// The IsAlwaysOn value of the associated element
/// </summary>
public static readonly DependencyProperty IsAlwaysOnProperty =
DependencyProperty.Register(nameof(IsAlwaysOn), typeof(bool), typeof(ViewportBehavior), new PropertyMetadata(default(bool)));

/// <summary>
/// Associated element fully enter the ScrollViewer viewport event
/// </summary>
Expand All @@ -59,33 +41,6 @@ public class ViewportBehavior : BehaviorBase<FrameworkElement>
/// </summary>
public event EventHandler ExitingViewport;

/// <summary>
/// Gets or sets a value indicating whether this behavior will remain attached after the associated element enters the viewport. When false, the behavior will remove itself after entering.
/// </summary>
public bool IsAlwaysOn
{
get { return (bool)GetValue(IsAlwaysOnProperty); }
set { SetValue(IsAlwaysOnProperty, value); }
}

/// <summary>
/// Gets a value indicating whether associated element is fully in the ScrollViewer viewport
/// </summary>
public bool IsFullyInViewport
{
get { return (bool)GetValue(IsFullyInViewportProperty); }
private set { SetValue(IsFullyInViewportProperty, value); }
}

/// <summary>
/// Gets a value indicating whether associated element is in the ScrollViewer viewport
/// </summary>
public bool IsInViewport
{
get { return (bool)GetValue(IsInViewportProperty); }
private set { SetValue(IsInViewportProperty, value); }
}

/// <summary>
/// Called after the behavior is attached to the <see cref="P:Microsoft.Xaml.Interactivity.Behavior.AssociatedObject" />.
/// </summary>
Expand Down Expand Up @@ -120,39 +75,6 @@ protected override void OnDetaching()
_hostScrollViewer = null;
}

private static void OnIsFullyInViewportChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = (ViewportBehavior)d;
var value = (bool)e.NewValue;
if (value)
{
obj.EnteredViewport?.Invoke(obj.AssociatedObject, EventArgs.Empty);

if (!obj.IsAlwaysOn)
{
Interaction.GetBehaviors(obj.AssociatedObject).Remove(obj);
}
}
else
{
obj.ExitingViewport?.Invoke(obj.AssociatedObject, EventArgs.Empty);
}
}

private static void OnIsInViewportChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = (ViewportBehavior)d;
var value = (bool)e.NewValue;
if (value)
{
obj.EnteringViewport?.Invoke(obj.AssociatedObject, EventArgs.Empty);
}
else
{
obj.ExitedViewport?.Invoke(obj.AssociatedObject, EventArgs.Empty);
}
}

private void ParentScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
var associatedElementRect = AssociatedObject.TransformToVisual(_hostScrollViewer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Globalization;

namespace CommunityToolkit.WinUI.UI.Controls
Expand Down Expand Up @@ -63,6 +64,13 @@ public AspectRatio(double ratio)
/// <param name="ratio"><see cref="double"/> value representing the <see cref="AspectRatio"/>.</param>
public static implicit operator AspectRatio(double ratio) => new AspectRatio(ratio);

/// <summary>
/// Implicit conversion operator to convert a <see cref="int"/> to an <see cref="AspectRatio"/> value.
/// Creates a simple aspect ratio of N:1, where N is int
/// </summary>
/// <param name="width"><see cref="int"/> value representing the <see cref="AspectRatio"/>.</param>
public static implicit operator AspectRatio(int width) => new AspectRatio(width, 1.0);

/// <summary>
/// Converter to take a string aspect ration like "16:9" and convert it to an <see cref="AspectRatio"/> struct.
/// Used automatically by XAML.
Expand Down
4 changes: 4 additions & 0 deletions UITests/UITests.App/UITests.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@
<Project>{e9faabfb-d726-42c1-83c1-cb46a29fea81}</Project>
<Name>CommunityToolkit.WinUI.UI.Controls.Core</Name>
</ProjectReference>
<ProjectReference Include="..\..\CommunityToolkit.WinUI.UI.Controls.Primitives\CommunityToolkit.WinUI.UI.Controls.Primitives.csproj">
<Project>{84ab7dc5-95c9-4cf8-a370-d077e9e9ef1a}</Project>
<Name>CommunityToolkit.WinUI.UI.Controls.Primitives</Name>
</ProjectReference>
<ProjectReference Include="..\..\CommunityToolkit.WinUI.UI.Media\CommunityToolkit.WinUI.UI.Media.csproj">
<Project>{75f9ee44-3efa-47bc-aedd-351b9834a0af}</Project>
<Name>CommunityToolkit.WinUI.UI.Media</Name>
Expand Down
42 changes: 42 additions & 0 deletions UITests/UITests.Tests.Shared/Controls/ConstrainedBoxTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.UI.Xaml.Tests.MUXControls.InteractionTests.Common;
using Microsoft.UI.Xaml.Tests.MUXControls.InteractionTests.Infra;
using Microsoft.Windows.Apps.Test.Automation;
using Microsoft.Windows.Apps.Test.Foundation;
using Microsoft.Windows.Apps.Test.Foundation.Controls;

#if USING_TAEF
using WEX.Logging.Interop;
using WEX.TestExecution;
using WEX.TestExecution.Markup;
#else
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endif

namespace UITests.Tests
{
[TestClass]
public class ConstrainedBoxTest : UITestBase
{
[ClassInitialize]
[TestProperty("RunAs", "User")]
[TestProperty("Classification", "ScenarioTestSuite")]
[TestProperty("Platform", "Any")]
public static void ClassInitialize(TestContext testContext)
{
TestEnvironment.Initialize(testContext, UITestsAppSampleApp);
}

[TestMethod]
[TestPage("ConstrainedBoxTestPage")]
public void Test_AspectRatioBoundToInteger_Placeholder()
{
// The test is if the AspectRatio can be bound to integer
// This test method acts as a placeholder, to spawn the XAML test page
// and test the binding to an integer
}
}
}
Loading

0 comments on commit 85bfe0e

Please sign in to comment.