Merge branch 'master' into winui
This commit is contained in:
Коммит
2d8d53a8fe
|
@ -10,7 +10,7 @@
|
|||
<PackageReleaseNotes>https://github.com/windows-toolkit/WindowsCommunityToolkit/releases</PackageReleaseNotes>
|
||||
<Copyright>(c) .NET Foundation and Contributors. All rights reserved.</Copyright>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<IsDesignProject>$(MSBuildProjectName.Contains('.Design'))</IsDesignProject>
|
||||
<IsDesignProject>$(MSBuildProjectName.Contains('.DesignTools'))</IsDesignProject>
|
||||
<IsTestProject>$(MSBuildProjectName.Contains('Test'))</IsTestProject>
|
||||
<IsUwpProject Condition="'$(IsDesignProject)' != 'true'">$(MSBuildProjectName.Contains('Uwp'))</IsUwpProject>
|
||||
<IsSampleProject>$(MSBuildProjectName.Contains('Sample'))</IsSampleProject>
|
||||
|
|
|
@ -3,13 +3,15 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.System;
|
||||
using Microsoft.Toolkit.Uwp.UI.Extensions;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Shapes;
|
||||
using Windows.Foundation;
|
||||
using Windows.System;
|
||||
using VirtualKey = Windows.System.VirtualKey;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
|
@ -35,6 +37,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
private const double DefaultMinimum = 0.0;
|
||||
private const double DefaultMaximum = 1.0;
|
||||
private const double DefaultStepFrequency = 1;
|
||||
private static readonly TimeSpan TimeToHideToolTipOnKeyUp = TimeSpan.FromSeconds(1);
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the Minimum dependency property.
|
||||
|
@ -61,6 +64,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
/// </summary>
|
||||
public static readonly DependencyProperty StepFrequencyProperty = DependencyProperty.Register(nameof(StepFrequency), typeof(double), typeof(RangeSelector), new PropertyMetadata(DefaultStepFrequency));
|
||||
|
||||
private readonly DispatcherQueueTimer keyDebounceTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
||||
|
||||
private Border _outOfRangeContentContainer;
|
||||
private Rectangle _activeRectangle;
|
||||
private Thumb _minThumb;
|
||||
|
@ -247,7 +252,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
case VirtualKey.Right:
|
||||
if (_toolTip != null)
|
||||
{
|
||||
_toolTip.Visibility = Visibility.Collapsed;
|
||||
keyDebounceTimer.Debounce(
|
||||
() => _toolTip.Visibility = Visibility.Collapsed,
|
||||
TimeToHideToolTipOnKeyUp);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:animations="using:Microsoft.Toolkit.Uwp.UI.Animations"
|
||||
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls">
|
||||
|
||||
<Style x:Key="SliderThumbStyle"
|
||||
|
@ -58,20 +57,6 @@
|
|||
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
|
||||
BorderThickness="1"
|
||||
Visibility="Collapsed">
|
||||
<animations:Implicit.ShowAnimations>
|
||||
<animations:OpacityAnimation From="0"
|
||||
To="1.0"
|
||||
Duration="0:0:0.3" />
|
||||
</animations:Implicit.ShowAnimations>
|
||||
|
||||
<animations:Implicit.HideAnimations>
|
||||
<animations:ScalarAnimation Target="Opacity"
|
||||
To="0"
|
||||
Duration="0:0:1">
|
||||
<animations:ScalarKeyFrame Key="0.7"
|
||||
Value="1.0" />
|
||||
</animations:ScalarAnimation>
|
||||
</animations:Implicit.HideAnimations>
|
||||
<TextBlock x:Name="ToolTipText"
|
||||
Margin="8"
|
||||
Foreground="{ThemeResource SystemControlForegroundBaseHighBrush}" />
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Image alignment
|
||||
/// </summary>
|
||||
public enum ImageAlignment
|
||||
{
|
||||
/// <summary>
|
||||
/// No alignment needed
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Align to Left when the property ScrollOrientation is Horizontal
|
||||
/// </summary>
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// Align to Right when the property ScrollOrientation is Horizontal
|
||||
/// </summary>
|
||||
Right,
|
||||
|
||||
/// <summary>
|
||||
/// Align to Top when the property ScrollOrientation is Vertical
|
||||
/// </summary>
|
||||
Top,
|
||||
|
||||
/// <summary>
|
||||
/// Align to Bottom when the property ScrollOrientation is Vertical
|
||||
/// </summary>
|
||||
Bottom
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Orientation of the scroll
|
||||
/// </summary>
|
||||
public enum ScrollOrientation
|
||||
{
|
||||
/// <summary>
|
||||
/// Scroll only Horizontally (and optimize the number of image used)
|
||||
/// </summary>
|
||||
Horizontal,
|
||||
|
||||
/// <summary>
|
||||
/// Scroll only Vertically (and optimize the number of image used)
|
||||
/// </summary>
|
||||
Vertical,
|
||||
|
||||
/// <summary>
|
||||
/// Scroll both Horizontally and vertically
|
||||
/// </summary>
|
||||
Both
|
||||
}
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
// 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.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A ContentControl that show an image repeated many times.
|
||||
/// The control can be synchronized with a ScrollViewer and animated easily.
|
||||
/// </summary>
|
||||
public partial class TileControl : ContentControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ScrollViewerContainer"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ScrollViewerContainerProperty =
|
||||
DependencyProperty.Register(nameof(ScrollViewerContainer), typeof(FrameworkElement), typeof(TileControl), new PropertyMetadata(null, OnScrollViewerContainerChange));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ImageAlignment"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ImageAlignmentProperty =
|
||||
DependencyProperty.Register(nameof(ImageAlignment), typeof(ImageAlignment), typeof(TileControl), new PropertyMetadata(ImageAlignment.None, OnAlignmentChange));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ImageSource"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ImageSourceProperty =
|
||||
DependencyProperty.Register(nameof(ImageSource), typeof(Uri), typeof(TileControl), new PropertyMetadata(null, OnImageSourceChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ScrollOrientation"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ScrollOrientationProperty =
|
||||
DependencyProperty.Register(nameof(ScrollOrientation), typeof(ScrollOrientation), typeof(TileControl), new PropertyMetadata(ScrollOrientation.Both, OnOrientationChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="OffsetX"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty OffsetXProperty =
|
||||
DependencyProperty.Register(nameof(OffsetX), typeof(double), typeof(TileControl), new PropertyMetadata(0.0, OnOffsetChange));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="OffsetY"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty OffsetYProperty =
|
||||
DependencyProperty.Register(nameof(OffsetY), typeof(double), typeof(TileControl), new PropertyMetadata(0.0, OnOffsetChange));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ParallaxSpeedRatio"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ParallaxSpeedRatioProperty =
|
||||
DependencyProperty.Register(nameof(ParallaxSpeedRatio), typeof(double), typeof(TileControl), new PropertyMetadata(1.0, OnScrollSpeedRatioChange));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="IsAnimated"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsAnimatedProperty =
|
||||
DependencyProperty.Register(nameof(IsAnimated), typeof(bool), typeof(TileControl), new PropertyMetadata(false, OnIsAnimatedChange));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="AnimationStepX"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty AnimationStepXProperty =
|
||||
DependencyProperty.Register(nameof(AnimationStepX), typeof(double), typeof(TileControl), new PropertyMetadata(1.0));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="AnimationStepY"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty AnimationStepYProperty =
|
||||
DependencyProperty.Register(nameof(AnimationStepY), typeof(double), typeof(TileControl), new PropertyMetadata(1.0));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="AnimationDuration"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty AnimationDurationProperty =
|
||||
DependencyProperty.Register(nameof(AnimationDuration), typeof(double), typeof(TileControl), new PropertyMetadata(30.0, OnAnimationDuration));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a ScrollViewer or a frameworkElement containing a ScrollViewer.
|
||||
/// The tile control is synchronized with the offset of the scrollViewer
|
||||
/// </summary>
|
||||
public FrameworkElement ScrollViewerContainer
|
||||
{
|
||||
get { return (FrameworkElement)GetValue(ScrollViewerContainerProperty); }
|
||||
set { SetValue(ScrollViewerContainerProperty, value); }
|
||||
}
|
||||
|
||||
private static async void OnScrollViewerContainerChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var control = d as TileControl;
|
||||
await control.InitializeScrollViewerContainer(e.OldValue as FrameworkElement, e.NewValue as FrameworkElement);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alignment of the tile when the <see cref="ScrollOrientation"/> is set to Vertical or Horizontal.
|
||||
/// Valid values are Left or Right for <see cref="ScrollOrientation"/> set to Horizontal and Top or Bottom for <see cref="ScrollOrientation"/> set to Vertical.
|
||||
/// </summary>
|
||||
public ImageAlignment ImageAlignment
|
||||
{
|
||||
get { return (ImageAlignment)GetValue(ImageAlignmentProperty); }
|
||||
set { SetValue(ImageAlignmentProperty, value); }
|
||||
}
|
||||
|
||||
private static async void OnAlignmentChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var control = d as TileControl;
|
||||
await control.RefreshContainerTileLocked();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the uri of the image to load
|
||||
/// </summary>
|
||||
public Uri ImageSource
|
||||
{
|
||||
get { return (Uri)GetValue(ImageSourceProperty); }
|
||||
set { SetValue(ImageSourceProperty, value); }
|
||||
}
|
||||
|
||||
private static async void OnImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var control = d as TileControl;
|
||||
await control.LoadImageBrushAsync(e.NewValue as Uri);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scroll orientation of the tile.
|
||||
/// Less images are drawn when you choose the Horizontal or Vertical value.
|
||||
/// </summary>
|
||||
public ScrollOrientation ScrollOrientation
|
||||
{
|
||||
get { return (ScrollOrientation)GetValue(ScrollOrientationProperty); }
|
||||
set { SetValue(ScrollOrientationProperty, value); }
|
||||
}
|
||||
|
||||
private static async void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var control = d as TileControl;
|
||||
await control.RefreshContainerTileLocked();
|
||||
await control.CreateModuloExpression(control._scrollViewer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an X offset of the image
|
||||
/// </summary>
|
||||
public double OffsetX
|
||||
{
|
||||
get { return (double)GetValue(OffsetXProperty); }
|
||||
set { SetValue(OffsetXProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnOffsetChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var c = d as TileControl;
|
||||
|
||||
c.RefreshMove();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an Y offset of the image
|
||||
/// </summary>
|
||||
public double OffsetY
|
||||
{
|
||||
get { return (double)GetValue(OffsetYProperty); }
|
||||
set { SetValue(OffsetYProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the speed ratio of the parallax effect with the <see cref="ScrollViewerContainer"/>
|
||||
/// </summary>
|
||||
public double ParallaxSpeedRatio
|
||||
{
|
||||
get { return (double)GetValue(ParallaxSpeedRatioProperty); }
|
||||
set { SetValue(ParallaxSpeedRatioProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnScrollSpeedRatioChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var c = d as TileControl;
|
||||
c.RefreshScrollSpeedRatio((double)e.NewValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the tile is animated or not
|
||||
/// </summary>
|
||||
public bool IsAnimated
|
||||
{
|
||||
get { return (bool)GetValue(IsAnimatedProperty); }
|
||||
set { SetValue(IsAnimatedProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnIsAnimatedChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var c = d as TileControl;
|
||||
|
||||
if ((bool)e.NewValue)
|
||||
{
|
||||
c._timerAnimation.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
c._timerAnimation.Stop();
|
||||
c._animationX = 0;
|
||||
c._animationY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the animation step of the OffsetX
|
||||
/// </summary>
|
||||
public double AnimationStepX
|
||||
{
|
||||
get { return (double)GetValue(AnimationStepXProperty); }
|
||||
set { SetValue(AnimationStepXProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the animation step of the OffsetY
|
||||
/// </summary>
|
||||
public double AnimationStepY
|
||||
{
|
||||
get { return (double)GetValue(AnimationStepYProperty); }
|
||||
set { SetValue(AnimationStepYProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a duration for the animation of the tile
|
||||
/// </summary>
|
||||
public double AnimationDuration
|
||||
{
|
||||
get { return (double)GetValue(AnimationDurationProperty); }
|
||||
set { SetValue(AnimationDurationProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnAnimationDuration(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var c = d as TileControl;
|
||||
|
||||
c._timerAnimation.Interval = TimeSpan.FromMilliseconds(c.AnimationDuration);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,130 +19,12 @@ using Windows.Foundation;
|
|||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Orientation of the scroll
|
||||
/// </summary>
|
||||
public enum ScrollOrientation
|
||||
{
|
||||
/// <summary>
|
||||
/// Scroll only Horizontally (and optimize the number of image used)
|
||||
/// </summary>
|
||||
Horizontal,
|
||||
|
||||
/// <summary>
|
||||
/// Scroll only Vertically (and optimize the number of image used)
|
||||
/// </summary>
|
||||
Vertical,
|
||||
|
||||
/// <summary>
|
||||
/// Scroll both Horizontally and vertically
|
||||
/// </summary>
|
||||
Both
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Image alignment
|
||||
/// </summary>
|
||||
public enum ImageAlignment
|
||||
{
|
||||
/// <summary>
|
||||
/// No alignment needed
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Align to Left when the property ScrollOrientation is Horizontal
|
||||
/// </summary>
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// Align to Right when the property ScrollOrientation is Horizontal
|
||||
/// </summary>
|
||||
Right,
|
||||
|
||||
/// <summary>
|
||||
/// Align to Top when the property ScrollOrientation is Vertical
|
||||
/// </summary>
|
||||
Top,
|
||||
|
||||
/// <summary>
|
||||
/// Align to Bottom when the property ScrollOrientation is Vertical
|
||||
/// </summary>
|
||||
Bottom
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A ContentControl that show an image repeated many times.
|
||||
/// The control can be synchronized with a ScrollViewer and animated easily.
|
||||
/// </summary>
|
||||
public class TileControl : ContentControl
|
||||
public partial class TileControl : ContentControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ScrollViewerContainer"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ScrollViewerContainerProperty =
|
||||
DependencyProperty.Register(nameof(ScrollViewerContainer), typeof(FrameworkElement), typeof(TileControl), new PropertyMetadata(null, OnScrollViewerContainerChange));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ImageAlignment"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ImageAlignmentProperty =
|
||||
DependencyProperty.Register(nameof(ImageAlignment), typeof(ImageAlignment), typeof(TileControl), new PropertyMetadata(ImageAlignment.None, OnAlignmentChange));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ImageSource"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ImageSourceProperty =
|
||||
DependencyProperty.Register(nameof(ImageSource), typeof(Uri), typeof(TileControl), new PropertyMetadata(null, OnImageSourceChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ScrollOrientation"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ScrollOrientationProperty =
|
||||
DependencyProperty.Register(nameof(ScrollOrientation), typeof(ScrollOrientation), typeof(TileControl), new PropertyMetadata(ScrollOrientation.Both, OnOrientationChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="OffsetX"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty OffsetXProperty =
|
||||
DependencyProperty.Register(nameof(OffsetX), typeof(double), typeof(TileControl), new PropertyMetadata(0.0, OnOffsetChange));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="OffsetY"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty OffsetYProperty =
|
||||
DependencyProperty.Register(nameof(OffsetY), typeof(double), typeof(TileControl), new PropertyMetadata(0.0, OnOffsetChange));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ParallaxSpeedRatio"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ParallaxSpeedRatioProperty =
|
||||
DependencyProperty.Register(nameof(ParallaxSpeedRatio), typeof(double), typeof(TileControl), new PropertyMetadata(1.0, OnScrollSpeedRatioChange));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="IsAnimated"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsAnimatedProperty =
|
||||
DependencyProperty.Register(nameof(IsAnimated), typeof(bool), typeof(TileControl), new PropertyMetadata(false, OnIsAnimatedChange));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="AnimationStepX"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty AnimationStepXProperty =
|
||||
DependencyProperty.Register(nameof(AnimationStepX), typeof(double), typeof(TileControl), new PropertyMetadata(1.0));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="AnimationStepY"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty AnimationStepYProperty =
|
||||
DependencyProperty.Register(nameof(AnimationStepY), typeof(double), typeof(TileControl), new PropertyMetadata(1.0));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="AnimationDuration"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty AnimationDurationProperty =
|
||||
DependencyProperty.Register(nameof(AnimationDuration), typeof(double), typeof(TileControl), new PropertyMetadata(30.0, OnAnimationDuration));
|
||||
|
||||
/// <summary>
|
||||
/// a flag to lock shared method
|
||||
/// </summary>
|
||||
|
@ -189,22 +71,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
InitializeAnimation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a ScrollViewer or a frameworkElement containing a ScrollViewer.
|
||||
/// The tile control is synchronized with the offset of the scrollViewer
|
||||
/// </summary>
|
||||
public FrameworkElement ScrollViewerContainer
|
||||
{
|
||||
get { return (FrameworkElement)GetValue(ScrollViewerContainerProperty); }
|
||||
set { SetValue(ScrollViewerContainerProperty, value); }
|
||||
}
|
||||
|
||||
private static async void OnScrollViewerContainerChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var control = d as TileControl;
|
||||
await control.InitializeScrollViewerContainer(e.OldValue as FrameworkElement, e.NewValue as FrameworkElement);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the new ScrollViewer
|
||||
/// </summary>
|
||||
|
@ -239,22 +105,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
await AttachScrollViewer(sender as FrameworkElement);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alignment of the tile when the <see cref="ScrollOrientation"/> is set to Vertical or Horizontal.
|
||||
/// Valid values are Left or Right for <see cref="ScrollOrientation"/> set to Horizontal and Top or Bottom for <see cref="ScrollOrientation"/> set to Vertical.
|
||||
/// </summary>
|
||||
public ImageAlignment ImageAlignment
|
||||
{
|
||||
get { return (ImageAlignment)GetValue(ImageAlignmentProperty); }
|
||||
set { SetValue(ImageAlignmentProperty, value); }
|
||||
}
|
||||
|
||||
private static async void OnAlignmentChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var control = d as TileControl;
|
||||
await control.RefreshContainerTileLocked();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attach a ScrollViewer to the TileControl (parallax effect)
|
||||
/// </summary>
|
||||
|
@ -278,21 +128,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the uri of the image to load
|
||||
/// </summary>
|
||||
public Uri ImageSource
|
||||
{
|
||||
get { return (Uri)GetValue(ImageSourceProperty); }
|
||||
set { SetValue(ImageSourceProperty, value); }
|
||||
}
|
||||
|
||||
private static async void OnImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var control = d as TileControl;
|
||||
await control.LoadImageBrushAsync(e.NewValue as Uri);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the image and transform it to a composition brush or a XAML brush (depends of the UIStrategy)
|
||||
/// </summary>
|
||||
|
@ -376,23 +211,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scroll orientation of the tile.
|
||||
/// Less images are drawn when you choose the Horizontal or Vertical value.
|
||||
/// </summary>
|
||||
public ScrollOrientation ScrollOrientation
|
||||
{
|
||||
get { return (ScrollOrientation)GetValue(ScrollOrientationProperty); }
|
||||
set { SetValue(ScrollOrientationProperty, value); }
|
||||
}
|
||||
|
||||
private static async void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var control = d as TileControl;
|
||||
await control.RefreshContainerTileLocked();
|
||||
await control.CreateModuloExpression(control._scrollViewer);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async void OnApplyTemplate()
|
||||
{
|
||||
|
@ -585,46 +403,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an X offset of the image
|
||||
/// </summary>
|
||||
public double OffsetX
|
||||
{
|
||||
get { return (double)GetValue(OffsetXProperty); }
|
||||
set { SetValue(OffsetXProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnOffsetChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var c = d as TileControl;
|
||||
|
||||
c.RefreshMove();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an Y offset of the image
|
||||
/// </summary>
|
||||
public double OffsetY
|
||||
{
|
||||
get { return (double)GetValue(OffsetYProperty); }
|
||||
set { SetValue(OffsetYProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the speed ratio of the parallax effect with the <see cref="ScrollViewerContainer"/>
|
||||
/// </summary>
|
||||
public double ParallaxSpeedRatio
|
||||
{
|
||||
get { return (double)GetValue(ParallaxSpeedRatioProperty); }
|
||||
set { SetValue(ParallaxSpeedRatioProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnScrollSpeedRatioChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var c = d as TileControl;
|
||||
c.RefreshScrollSpeedRatio((double)e.NewValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the modulo expression and apply it to the ContainerVisual element
|
||||
/// </summary>
|
||||
|
@ -833,31 +611,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
_propertySetModulo?.InsertScalar("speed", (float)speedRatio);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the tile is animated or not
|
||||
/// </summary>
|
||||
public bool IsAnimated
|
||||
{
|
||||
get { return (bool)GetValue(IsAnimatedProperty); }
|
||||
set { SetValue(IsAnimatedProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnIsAnimatedChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var c = d as TileControl;
|
||||
|
||||
if ((bool)e.NewValue)
|
||||
{
|
||||
c._timerAnimation.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
c._timerAnimation.Stop();
|
||||
c._animationX = 0;
|
||||
c._animationY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAnimation()
|
||||
{
|
||||
if (_timerAnimation == null)
|
||||
|
@ -900,39 +653,5 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
RefreshMove();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the animation step of the OffsetX
|
||||
/// </summary>
|
||||
public double AnimationStepX
|
||||
{
|
||||
get { return (double)GetValue(AnimationStepXProperty); }
|
||||
set { SetValue(AnimationStepXProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the animation step of the OffsetY
|
||||
/// </summary>
|
||||
public double AnimationStepY
|
||||
{
|
||||
get { return (double)GetValue(AnimationStepYProperty); }
|
||||
set { SetValue(AnimationStepYProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a duration for the animation of the tile
|
||||
/// </summary>
|
||||
public double AnimationDuration
|
||||
{
|
||||
get { return (double)GetValue(AnimationDurationProperty); }
|
||||
set { SetValue(AnimationDurationProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnAnimationDuration(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var c = d as TileControl;
|
||||
|
||||
c._timerAnimation.Interval = TimeSpan.FromMilliseconds(c.AnimationDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,16 +4,16 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.System;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Set of extention methods for using <see cref="DispatcherTimer"/>.
|
||||
/// Set of extention methods for using <see cref="DispatcherQueueTimer"/>.
|
||||
/// </summary>
|
||||
public static class DispatcherTimerExtensions
|
||||
public static class DispatcherQueueTimerExtensions
|
||||
{
|
||||
private static ConcurrentDictionary<DispatcherTimer, Action> _debounceInstances = new ConcurrentDictionary<DispatcherTimer, Action>();
|
||||
private static ConcurrentDictionary<DispatcherQueueTimer, Action> _debounceInstances = new ConcurrentDictionary<DispatcherQueueTimer, Action>();
|
||||
|
||||
/// <summary>
|
||||
/// <para>Used to debounce (rate-limit) an event. The action will be postponed and executed after the interval has elapsed. At the end of the interval, the function will be called with the arguments that were passed most recently to the debounced function.</para>
|
||||
|
@ -27,7 +27,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions
|
|||
/// <param name="immediate">Determines if the action execute on the leading edge instead of trailing edge.</param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// private DispatcherTimer _typeTimer = new DispatcherTimer();
|
||||
/// private DispatcherQueueTimer _typeTimer = new DispatcherQueueTimer();
|
||||
///
|
||||
/// _typeTimer.Debounce(async () =>
|
||||
/// {
|
||||
|
@ -35,10 +35,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions
|
|||
/// }, TimeSpan.FromSeconds(0.3));
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static void Debounce(this DispatcherTimer timer, Action action, TimeSpan interval, bool immediate = false)
|
||||
public static void Debounce(this DispatcherQueueTimer timer, Action action, TimeSpan interval, bool immediate = false)
|
||||
{
|
||||
// Check and stop any existing timer
|
||||
var timeout = timer.IsEnabled;
|
||||
var timeout = timer.IsRunning;
|
||||
if (timeout)
|
||||
{
|
||||
timer.Stop();
|
||||
|
@ -72,7 +72,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions
|
|||
private static void Timer_Tick(object sender, object e)
|
||||
{
|
||||
// This event is only registered/run if we weren't in immediate mode above
|
||||
if (sender is DispatcherTimer timer)
|
||||
if (sender is DispatcherQueueTimer timer)
|
||||
{
|
||||
timer.Tick -= Timer_Tick;
|
||||
timer.Stop();
|
|
@ -0,0 +1,103 @@
|
|||
// 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 System.Diagnostics;
|
||||
using Microsoft.Toolkit.Mvvm.Messaging;
|
||||
using UITests.App.Pages;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.ApplicationModel.AppService;
|
||||
using Windows.ApplicationModel.Background;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace UITests.App
|
||||
{
|
||||
/// <summary>
|
||||
/// This file contains part of the app related to the AppService for communication between this test host and the test harness processes.
|
||||
/// </summary>
|
||||
public sealed partial class App
|
||||
{
|
||||
private AppServiceConnection _appServiceConnection;
|
||||
private BackgroundTaskDeferral _appServiceDeferral;
|
||||
|
||||
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
|
||||
{
|
||||
base.OnBackgroundActivated(args);
|
||||
IBackgroundTaskInstance taskInstance = args.TaskInstance;
|
||||
AppServiceTriggerDetails appService = taskInstance.TriggerDetails as AppServiceTriggerDetails;
|
||||
_appServiceDeferral = taskInstance.GetDeferral();
|
||||
taskInstance.Canceled += OnAppServicesCanceled;
|
||||
_appServiceConnection = appService.AppServiceConnection;
|
||||
_appServiceConnection.RequestReceived += OnAppServiceRequestReceived;
|
||||
_appServiceConnection.ServiceClosed += AppServiceConnection_ServiceClosed;
|
||||
}
|
||||
|
||||
private async void OnAppServiceRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
|
||||
{
|
||||
AppServiceDeferral messageDeferral = args.GetDeferral();
|
||||
ValueSet message = args.Request.Message;
|
||||
string cmd = message["Command"] as string;
|
||||
|
||||
try
|
||||
{
|
||||
// Return the data to the caller.
|
||||
if (cmd == "Start")
|
||||
{
|
||||
var pageName = message["Page"] as string;
|
||||
|
||||
Log.Comment("Received request for Page: {0}", pageName);
|
||||
|
||||
ValueSet returnMessage = new ValueSet();
|
||||
|
||||
// We await the OpenPage method to ensure the navigation has finished.
|
||||
if (await WeakReferenceMessenger.Default.Send(new RequestPageMessage(pageName)))
|
||||
{
|
||||
returnMessage.Add("Status", "OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
returnMessage.Add("Status", "BAD");
|
||||
}
|
||||
|
||||
await args.Request.SendResponseAsync(returnMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Your exception handling code here.
|
||||
Log.Error("Exception processing request: {0}", e.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Complete the deferral so that the platform knows that we're done responding to the app service call.
|
||||
// Note: for error handling: this must be called even if SendResponseAsync() throws an exception.
|
||||
messageDeferral.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAppServicesCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
|
||||
{
|
||||
_appServiceDeferral.Complete();
|
||||
}
|
||||
|
||||
private void AppServiceConnection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
|
||||
{
|
||||
_appServiceDeferral.Complete();
|
||||
}
|
||||
|
||||
public async void SendLogMessage(string level, string msg)
|
||||
{
|
||||
var message = new ValueSet();
|
||||
message.Add("Command", "Log");
|
||||
message.Add("Level", level);
|
||||
message.Add("Message", msg);
|
||||
|
||||
await _appServiceConnection.SendMessageAsync(message);
|
||||
|
||||
// TODO: do we care if we have a problem here?
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,4 @@
|
|||
<Application
|
||||
x:Class="UITests.App.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:UITests.App">
|
||||
|
||||
</Application>
|
||||
<Application x:Class="UITests.App.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:UITests.App" />
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UITests.App.Pages;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.UI.Xaml;
|
||||
|
@ -11,12 +15,22 @@ using Windows.UI.Xaml.Navigation;
|
|||
|
||||
namespace UITests.App
|
||||
{
|
||||
/// <summary>
|
||||
/// Application class for hosting UI pages to test.
|
||||
/// </summary>
|
||||
public sealed partial class App
|
||||
{
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.Suspending += OnSuspending;
|
||||
this.UnhandledException += this.App_UnhandledException;
|
||||
}
|
||||
|
||||
private void App_UnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||
{
|
||||
// TODO: Also Log to a file?
|
||||
Log.Error("Unhandled Exception: " + e.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -53,7 +67,7 @@ namespace UITests.App
|
|||
// When the navigation stack isn't restored navigate to the first page,
|
||||
// configuring the new page by passing required information as a navigation
|
||||
// parameter
|
||||
rootFrame.Navigate(typeof(MainPage), e.Arguments);
|
||||
rootFrame.Navigate(typeof(MainTestHost), e.Arguments);
|
||||
}
|
||||
|
||||
// Ensure the current window is active
|
||||
|
@ -68,6 +82,7 @@ namespace UITests.App
|
|||
/// <param name="e">Details about the navigation failure</param>
|
||||
private void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
|
||||
{
|
||||
Log.Error("Failed to load root page: " + e.SourcePageType.FullName);
|
||||
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
<Page
|
||||
x:Class="UITests.App.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:testhelpers="using:AppTestAutomationHelpers"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid>
|
||||
<testhelpers:TestAutomationHelpersPanel />
|
||||
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Button Content="Click Me" Click="Button_Click"/>
|
||||
<TextBlock x:Name="textBlock"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,19 +0,0 @@
|
|||
// 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.
|
||||
|
||||
namespace UITests.App
|
||||
{
|
||||
public sealed partial class MainPage
|
||||
{
|
||||
public MainPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
textBlock.Text = "Clicked";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<Page x:Class="UITests.App.MainTestHost"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:testhelpers="using:AppTestAutomationHelpers"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<testhelpers:TestAutomationHelpersPanel />
|
||||
|
||||
<Frame x:Name="navigationFrame"
|
||||
Navigated="NavigationFrame_Navigated"
|
||||
NavigationFailed="NavigationFrame_NavigationFailed" />
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,118 @@
|
|||
// 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 System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Mvvm.Messaging;
|
||||
using Microsoft.Toolkit.Uwp.Extensions;
|
||||
using UITests.App.Pages;
|
||||
using Windows.System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
|
||||
namespace UITests.App
|
||||
{
|
||||
/// <summary>
|
||||
/// MainPage hosting all other test pages.
|
||||
/// </summary>
|
||||
public sealed partial class MainTestHost : IRecipient<RequestPageMessage>
|
||||
{
|
||||
private DispatcherQueue _queue;
|
||||
|
||||
private Assembly _executingAssembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
private TaskCompletionSource<bool> _loadingStateTask;
|
||||
|
||||
public MainTestHost()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
WeakReferenceMessenger.Default.Register<RequestPageMessage>(this);
|
||||
|
||||
_queue = DispatcherQueue.GetForCurrentThread();
|
||||
}
|
||||
|
||||
public void Receive(RequestPageMessage message)
|
||||
{
|
||||
// Reply with task back to so it can be properly awaited link:App.AppService.xaml.cs#L56
|
||||
message.Reply(OpenPage(message.PageName));
|
||||
}
|
||||
|
||||
private async Task<bool> OpenPage(string pageName)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Comment("Trying to Load Page: " + pageName);
|
||||
|
||||
_loadingStateTask = new TaskCompletionSource<bool>();
|
||||
|
||||
// Ensure we're on the UI thread as we'll be called from the AppService now.
|
||||
_ = _queue.EnqueueAsync(() =>
|
||||
{
|
||||
// Navigate without extra animations
|
||||
navigationFrame.Navigate(FindPageType(pageName), new SuppressNavigationTransitionInfo());
|
||||
});
|
||||
|
||||
// Wait for load to complete
|
||||
await _loadingStateTask.Task;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("Exception Loading Page {0}: {1} ", pageName, e.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Type FindPageType(string pageName)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _executingAssembly.GetType("UITests.App.Pages." + pageName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("Exception Finding Page {0}: {1} ", pageName, e.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void NavigationFrame_Navigated(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
|
||||
{
|
||||
Log.Comment("Navigated to Page {0}", e.SourcePageType.FullName);
|
||||
if (e.Content is Page page)
|
||||
{
|
||||
if (page.IsLoaded)
|
||||
{
|
||||
Log.Comment("Loaded Page {0}", e.SourcePageType.FullName);
|
||||
_loadingStateTask.SetResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.Loaded += this.Page_Loaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var page = sender as Page;
|
||||
|
||||
page.Loaded -= Page_Loaded;
|
||||
|
||||
Log.Comment("Loaded Page (E) {0}", page.GetType().FullName);
|
||||
_loadingStateTask.SetResult(true);
|
||||
}
|
||||
|
||||
private void NavigationFrame_NavigationFailed(object sender, Windows.UI.Xaml.Navigation.NavigationFailedEventArgs e)
|
||||
{
|
||||
Log.Error("Failed to navigate to page {0}", e.SourcePageType.FullName);
|
||||
_loadingStateTask.SetResult(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,6 +40,11 @@
|
|||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<uap:Extension Category="windows.appService">
|
||||
<uap:AppService Name="TestHarnessCommunicationService"/>
|
||||
</uap:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
// 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.Toolkit.Mvvm.Messaging.Messages;
|
||||
|
||||
namespace UITests.App
|
||||
{
|
||||
public sealed class RequestPageMessage : AsyncRequestMessage<bool>
|
||||
{
|
||||
public RequestPageMessage(string name)
|
||||
{
|
||||
PageName = name;
|
||||
}
|
||||
|
||||
public string PageName { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// 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 Windows.UI.Xaml;
|
||||
|
||||
namespace UITests.App.Pages
|
||||
{
|
||||
// TAEF has a different terms for the same concepts as compared with MSTest.
|
||||
// In order to allow both to use the same test files, we'll define these helper classes
|
||||
// to translate TAEF into MSTest.
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "Shim helpers")]
|
||||
public static class Log
|
||||
{
|
||||
public static void Comment(string format, params object[] args)
|
||||
{
|
||||
LogMessage("Comment", format, args);
|
||||
}
|
||||
|
||||
public static void Warning(string format, params object[] args)
|
||||
{
|
||||
LogMessage("Warning", "[Warning] " + format, args);
|
||||
}
|
||||
|
||||
public static void Error(string format, params object[] args)
|
||||
{
|
||||
LogMessage("Error", "[Error] " + format, args);
|
||||
}
|
||||
|
||||
private static void LogMessage(string level, string format, object[] args)
|
||||
{
|
||||
// string.Format() complains if we pass it something with braces, even if we have no arguments.
|
||||
// To account for that, we'll escape braces if we have no arguments.
|
||||
if (args.Length == 0)
|
||||
{
|
||||
format = format.Replace("{", "{{").Replace("}", "}}");
|
||||
}
|
||||
|
||||
// Send back to Test Harness via AppService
|
||||
// TODO: Make this a cleaner connection/pattern
|
||||
((App)Application.Current).SendLogMessage(level, string.Format(format, args));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
|
||||
<GenerateAppxPackageOnBuild>true</GenerateAppxPackageOnBuild>
|
||||
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
|
||||
<IsTestHost>true</IsTestHost>
|
||||
</PropertyGroup>
|
||||
<Target Name="Pack">
|
||||
</Target>
|
||||
|
@ -130,9 +131,14 @@
|
|||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
<Compile Include="App.AppService.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MainTestHost.xaml.cs">
|
||||
<DependentUpon>MainTestHost.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="RequestPageMessage.cs" />
|
||||
<Compile Include="TestInterop.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -155,7 +161,7 @@
|
|||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Page Include="MainPage.xaml">
|
||||
<Page Include="MainTestHost.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
|
@ -252,4 +258,5 @@
|
|||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<Import Project="..\UITests.Tests.Shared\UITests.Tests.Shared.projitems" Label="Shared" />
|
||||
</Project>
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace UITests.Tests
|
|||
// TAEF has a different terms for the same concepts as compared with MSTest.
|
||||
// In order to allow both to use the same test files, we'll define these helper classes
|
||||
// to translate TAEF into MSTest.
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "Shim helpers")]
|
||||
public static class Log
|
||||
{
|
||||
public static void Comment(string format, params object[] args)
|
||||
|
@ -20,12 +21,12 @@ namespace UITests.Tests
|
|||
|
||||
public static void Warning(string format, params object[] args)
|
||||
{
|
||||
LogMessage(format, args);
|
||||
LogMessage("[Warning] " + format, args);
|
||||
}
|
||||
|
||||
public static void Error(string format, params object[] args)
|
||||
{
|
||||
LogMessage(format, args);
|
||||
LogMessage("[Error] " + format, args);
|
||||
}
|
||||
|
||||
private static void LogMessage(string format, object[] args)
|
||||
|
@ -41,6 +42,7 @@ namespace UITests.Tests
|
|||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Shim helpers.")]
|
||||
public static class LogController
|
||||
{
|
||||
public static void InitializeLogging()
|
||||
|
@ -49,6 +51,7 @@ namespace UITests.Tests
|
|||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Shim helpers.")]
|
||||
public static class Verify
|
||||
{
|
||||
// TODO: implement
|
||||
|
|
|
@ -13,9 +13,15 @@
|
|||
<PlatformTarget>x86</PlatformTarget>
|
||||
<OutputType>Exe</OutputType>
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
<IsTestHarness>true</IsTestHarness>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'netcoreapp3.1'"
|
||||
Include="Microsoft.Windows.SDK.Contracts"
|
||||
Version="10.0.19041.0" />
|
||||
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
||||
<PackageReference Include="MUXTestInfra.MSTest" Version="0.0.4" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||
|
@ -27,5 +33,5 @@
|
|||
<PackageReference Include="Microsoft.Windows.Apps.Test" Version="1.0.181205002" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\UITests.Tests.Shared\UITests.Tests.Shared.projitems" />
|
||||
<Import Project="..\UITests.Tests.Shared\UITests.Tests.Shared.projitems" Label="Shared" />
|
||||
</Project>
|
|
@ -0,0 +1,56 @@
|
|||
// 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.Windows.Apps.Test.Foundation.Controls;
|
||||
using Windows.UI.Xaml.Tests.MUXControls.InteractionTests.Common;
|
||||
using Windows.UI.Xaml.Tests.MUXControls.InteractionTests.Infra;
|
||||
|
||||
#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 TextBoxMaskTest : UITestBase
|
||||
{
|
||||
[ClassInitialize]
|
||||
[TestProperty("RunAs", "User")]
|
||||
[TestProperty("Classification", "ScenarioTestSuite")]
|
||||
[TestProperty("Platform", "Any")]
|
||||
public static void ClassInitialize(TestContext testContext)
|
||||
{
|
||||
TestEnvironment.Initialize(testContext, WinUICsUWPSampleApp);
|
||||
}
|
||||
|
||||
[ClassCleanup]
|
||||
public static void ClassCleanup()
|
||||
{
|
||||
TestEnvironment.AssemblyCleanupWorker(WinUICsUWPSampleApp);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[TestPage("TextBoxMaskTestPage")]
|
||||
public void TestTextBoxMaskBinding_Property()
|
||||
{
|
||||
var initialValue = FindElement.ById<TextBlock>("InitialValueTextBlock").GetText();
|
||||
var textBox = FindElement.ById<Edit>("TextBox");
|
||||
|
||||
Verify.AreEqual(initialValue, textBox.GetText());
|
||||
|
||||
var changeButton = FindElement.ById<Button>("ChangeButton");
|
||||
|
||||
changeButton.Click();
|
||||
Wait.ForIdle();
|
||||
|
||||
var newValue = FindElement.ById<TextBlock>("NewValueTextBlock").GetText();
|
||||
|
||||
Verify.AreEqual(newValue, textBox.GetText());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<Page x:Class="UITests.App.Pages.TextBoxMaskTestPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:extensions="using:Microsoft.Toolkit.Uwp.UI.Extensions"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
mc:Ignorable="d">
|
||||
<Grid HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="10" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button x:Name="ChangeButton"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Click="ChangeButton_Click"
|
||||
Content="Change Target Value" />
|
||||
<TextBox x:Name="TextBox"
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Width="120"
|
||||
Padding="0,4,0,0"
|
||||
VerticalAlignment="Center"
|
||||
extensions:TextBoxMask.CustomMask="5:[0-5]"
|
||||
extensions:TextBoxMask.Mask="99:59:59"
|
||||
Text="{x:Bind Value, Mode=OneWay}"
|
||||
TextAlignment="Center" />
|
||||
<TextBlock x:Name="InitialValueTextBlock"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{x:Bind InitialValue}" />
|
||||
<TextBlock x:Name="NewValueTextBlock"
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{x:Bind NewValue}" />
|
||||
</Grid>
|
||||
</Page>
|
|
@ -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 Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace UITests.App.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class TextBoxMaskTestPage : Page
|
||||
{
|
||||
private const string INITIAL_VALUE = "12:50:59";
|
||||
private const string NEW_VALUE = "00:00:00";
|
||||
|
||||
public string InitialValue => INITIAL_VALUE;
|
||||
|
||||
public string NewValue => NEW_VALUE;
|
||||
|
||||
public string Value
|
||||
{
|
||||
get { return (string)GetValue(ValueProperty); }
|
||||
set { SetValue(ValueProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ValueProperty =
|
||||
DependencyProperty.Register(nameof(Value), typeof(string), typeof(TextBoxMaskTestPage), new PropertyMetadata(INITIAL_VALUE));
|
||||
|
||||
public TextBoxMaskTestPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private void ChangeButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Value = NEW_VALUE;
|
||||
Log.Comment("Value Changed to {0}", Value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// 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.Windows.Apps.Test.Foundation.Controls;
|
||||
using Windows.UI.Xaml.Tests.MUXControls.InteractionTests.Common;
|
||||
using Windows.UI.Xaml.Tests.MUXControls.InteractionTests.Infra;
|
||||
|
||||
#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 SimpleTest : UITestBase
|
||||
{
|
||||
[ClassInitialize]
|
||||
[TestProperty("RunAs", "User")]
|
||||
[TestProperty("Classification", "ScenarioTestSuite")]
|
||||
[TestProperty("Platform", "Any")]
|
||||
public static void ClassInitialize(TestContext testContext)
|
||||
{
|
||||
TestEnvironment.Initialize(testContext, WinUICsUWPSampleApp);
|
||||
}
|
||||
|
||||
[ClassCleanup]
|
||||
public static void ClassCleanup()
|
||||
{
|
||||
TestEnvironment.AssemblyCleanupWorker(WinUICsUWPSampleApp);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[TestPage("SimpleTestPage")]
|
||||
public void SimpleTestMethod()
|
||||
{
|
||||
var button = new Button(FindElement.ByName("Click Me"));
|
||||
var textBlock = new TextBlock(FindElement.ById("textBlock"));
|
||||
|
||||
Verify.IsNotNull(button);
|
||||
|
||||
Verify.AreEqual(string.Empty, textBlock.GetText());
|
||||
|
||||
button.Click();
|
||||
|
||||
Wait.ForIdle();
|
||||
|
||||
Verify.AreEqual("Clicked", textBlock.GetText());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<Page x:Class="UITests.App.Pages.SimpleTestPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
mc:Ignorable="d">
|
||||
<StackPanel HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<Button Click="Button_Click"
|
||||
Content="Click Me" />
|
||||
<TextBlock x:Name="textBlock" />
|
||||
</StackPanel>
|
||||
</Page>
|
|
@ -0,0 +1,26 @@
|
|||
// 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 Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace UITests.App.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class SimpleTestPage : Page
|
||||
{
|
||||
public SimpleTestPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private void Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
textBlock.Text = "Clicked";
|
||||
Log.Comment("Button Clicked!");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,12 +2,19 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Windows.UI.Xaml.Tests.MUXControls.InteractionTests.Infra;
|
||||
|
||||
#if USING_TAEF
|
||||
using WEX.Logging.Interop;
|
||||
using WEX.TestExecution;
|
||||
using WEX.TestExecution.Markup;
|
||||
#else
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
#endif
|
||||
using Windows.UI.Xaml.Tests.MUXControls.InteractionTests.Infra;
|
||||
|
||||
namespace UITests.Tests
|
||||
{
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// 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;
|
||||
|
||||
namespace UITests.Tests
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class TestPageAttribute : Attribute
|
||||
{
|
||||
public TestPageAttribute(string xamlFile)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(xamlFile))
|
||||
{
|
||||
throw new ArgumentException($"'{nameof(xamlFile)}' cannot be null or whitespace", nameof(xamlFile));
|
||||
}
|
||||
|
||||
XamlFile = xamlFile;
|
||||
}
|
||||
|
||||
public string XamlFile { get; private set; }
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
// 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 System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.Windows.Apps.Test.Foundation.Controls;
|
||||
using Windows.UI.Xaml.Tests.MUXControls.InteractionTests.Common;
|
||||
using Windows.UI.Xaml.Tests.MUXControls.InteractionTests.Infra;
|
||||
|
||||
#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 Tests
|
||||
{
|
||||
public static TestApplicationInfo WinUICsUWPSampleApp
|
||||
{
|
||||
get
|
||||
{
|
||||
string assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
string baseDirectory = Path.Combine(Directory.GetParent(assemblyDir).Parent.Parent.Parent.Parent.FullName, "UITests.App");
|
||||
|
||||
Log.Comment($"Base Package Search Directory = \"{baseDirectory}\"");
|
||||
|
||||
var exclude = new[] { "Microsoft.NET.CoreRuntime", "Microsoft.VCLibs", "Microsoft.UI.Xaml", "Microsoft.NET.CoreFramework.Debug" };
|
||||
var files = Directory.GetFiles(baseDirectory, "*.msix", SearchOption.AllDirectories).Where(f => !exclude.Any(Path.GetFileNameWithoutExtension(f).Contains));
|
||||
|
||||
if (files.Count() == 0)
|
||||
{
|
||||
throw new Exception(string.Format("Failed to find '*.msix' in {0}'!", baseDirectory));
|
||||
}
|
||||
|
||||
string mostRecentlyBuiltPackage = string.Empty;
|
||||
DateTime timeMostRecentlyBuilt = DateTime.MinValue;
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
DateTime fileWriteTime = File.GetLastWriteTime(file);
|
||||
|
||||
if (fileWriteTime > timeMostRecentlyBuilt)
|
||||
{
|
||||
timeMostRecentlyBuilt = fileWriteTime;
|
||||
mostRecentlyBuiltPackage = file;
|
||||
}
|
||||
}
|
||||
|
||||
return new TestApplicationInfo(
|
||||
"UITests.App",
|
||||
"3568ebdf-5b6b-4ddd-bb17-462d614ba50f_gspb8g6x97k2t!App",
|
||||
"3568ebdf-5b6b-4ddd-bb17-462d614ba50f_gspb8g6x97k2t",
|
||||
"UITests.App",
|
||||
"UITests.App.exe",
|
||||
mostRecentlyBuiltPackage.Replace(".msix", string.Empty),
|
||||
"24d62f3b13b8b9514ead9c4de48cc30f7cc6151d",
|
||||
baseDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
public static TestSetupHelper.TestSetupHelperOptions TestSetupHelperOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TestSetupHelper.TestSetupHelperOptions
|
||||
{
|
||||
AutomationIdOfSafeItemToClick = string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[ClassInitialize]
|
||||
[TestProperty("RunAs", "User")]
|
||||
[TestProperty("Classification", "ScenarioTestSuite")]
|
||||
[TestProperty("Platform", "Any")]
|
||||
public static void ClassInitialize(TestContext testContext)
|
||||
{
|
||||
TestEnvironment.Initialize(testContext, WinUICsUWPSampleApp);
|
||||
}
|
||||
|
||||
[ClassCleanup]
|
||||
public static void ClassCleanup()
|
||||
{
|
||||
TestEnvironment.AssemblyCleanupWorker(WinUICsUWPSampleApp);
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void TestCleanup()
|
||||
{
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SimpleLaunchTest()
|
||||
{
|
||||
var button = new Button(FindElement.ByName("Click Me"));
|
||||
var textBlock = new TextBlock(FindElement.ById("textBlock"));
|
||||
|
||||
Verify.IsNotNull(button);
|
||||
|
||||
Verify.AreEqual(string.Empty, textBlock.GetText());
|
||||
|
||||
button.Click();
|
||||
|
||||
Wait.ForIdle();
|
||||
|
||||
Verify.AreEqual("Clicked", textBlock.GetText());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
// 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 System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Windows.Apps.Test.Foundation.Controls;
|
||||
using Windows.ApplicationModel.AppService;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml.Tests.MUXControls.InteractionTests.Common;
|
||||
using Windows.UI.Xaml.Tests.MUXControls.InteractionTests.Infra;
|
||||
|
||||
#if USING_TAEF
|
||||
using WEX.Logging.Interop;
|
||||
using WEX.TestExecution;
|
||||
using WEX.TestExecution.Markup;
|
||||
#else
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
#endif
|
||||
|
||||
namespace UITests.Tests
|
||||
{
|
||||
public abstract class UITestBase
|
||||
{
|
||||
public static TestApplicationInfo WinUICsUWPSampleApp
|
||||
{
|
||||
get
|
||||
{
|
||||
string assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
string baseDirectory = Path.Combine(Directory.GetParent(assemblyDir).Parent.Parent.Parent.Parent.FullName, "UITests.App");
|
||||
|
||||
Log.Comment($"Base Package Search Directory = \"{baseDirectory}\"");
|
||||
|
||||
var exclude = new[] { "Microsoft.NET.CoreRuntime", "Microsoft.VCLibs", "Microsoft.UI.Xaml", "Microsoft.NET.CoreFramework.Debug" };
|
||||
var files = Directory.GetFiles(baseDirectory, "*.msix", SearchOption.AllDirectories).Where(f => !exclude.Any(Path.GetFileNameWithoutExtension(f).Contains));
|
||||
|
||||
if (files.Count() == 0)
|
||||
{
|
||||
throw new Exception(string.Format("Failed to find '*.msix' in {0}'!", baseDirectory));
|
||||
}
|
||||
|
||||
string mostRecentlyBuiltPackage = string.Empty;
|
||||
DateTime timeMostRecentlyBuilt = DateTime.MinValue;
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
DateTime fileWriteTime = File.GetLastWriteTime(file);
|
||||
|
||||
if (fileWriteTime > timeMostRecentlyBuilt)
|
||||
{
|
||||
timeMostRecentlyBuilt = fileWriteTime;
|
||||
mostRecentlyBuiltPackage = file;
|
||||
}
|
||||
}
|
||||
|
||||
return new TestApplicationInfo(
|
||||
testAppPackageName: "UITests.App",
|
||||
testAppName: "3568ebdf-5b6b-4ddd-bb17-462d614ba50f_gspb8g6x97k2t!App",
|
||||
testAppPackageFamilyName: "3568ebdf-5b6b-4ddd-bb17-462d614ba50f_gspb8g6x97k2t",
|
||||
testAppMainWindowTitle: "UITests.App",
|
||||
processName: "UITests.App.exe",
|
||||
installerName: mostRecentlyBuiltPackage.Replace(".msix", string.Empty),
|
||||
certSerialNumber: "24d62f3b13b8b9514ead9c4de48cc30f7cc6151d",
|
||||
baseAppxDir: baseDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
public static TestSetupHelper.TestSetupHelperOptions TestSetupHelperOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TestSetupHelper.TestSetupHelperOptions
|
||||
{
|
||||
AutomationIdOfSafeItemToClick = string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public TestContext TestContext { get; set; }
|
||||
|
||||
private AppServiceConnection CommunicationService { get; set; }
|
||||
|
||||
[TestInitialize]
|
||||
public async Task TestInitialize()
|
||||
{
|
||||
PreTestSetup();
|
||||
|
||||
#if USING_TAEF
|
||||
var fullTestName = TestContext.TestName;
|
||||
var lastDotIndex = fullTestName.LastIndexOf('.');
|
||||
var testName = fullTestName.Substring(lastDotIndex + 1);
|
||||
var theClassName = fullTestName.Substring(0, lastDotIndex);
|
||||
#else
|
||||
var testName = TestContext.TestName;
|
||||
var theClassName = TestContext.FullyQualifiedTestClassName;
|
||||
#endif
|
||||
var currentlyRunningClassType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(f => f.FullName == theClassName);
|
||||
if (!(Type.GetType(theClassName) is Type type))
|
||||
{
|
||||
Verify.Fail("Type is null. TestClassName : " + theClassName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(type.GetMethod(testName) is MethodInfo method))
|
||||
{
|
||||
Verify.Fail("Mothod is null. TestClassName : " + theClassName + " Testname: " + testName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(method.GetCustomAttribute(typeof(TestPageAttribute), true) is TestPageAttribute attribute))
|
||||
{
|
||||
Verify.Fail("Attribute is null. TestClassName : " + theClassName);
|
||||
return;
|
||||
}
|
||||
|
||||
var pageName = attribute.XamlFile;
|
||||
|
||||
Log.Comment("[Harness] Sending Host Page Request: {0}", pageName);
|
||||
|
||||
// Make the connection if we haven't already.
|
||||
if (CommunicationService == null)
|
||||
{
|
||||
CommunicationService = new AppServiceConnection();
|
||||
|
||||
CommunicationService.RequestReceived += this.CommunicationService_RequestReceived;
|
||||
|
||||
// Here, we use the app service name defined in the app service
|
||||
// provider's Package.appxmanifest file in the <Extension> section.
|
||||
CommunicationService.AppServiceName = "TestHarnessCommunicationService";
|
||||
|
||||
// Use Windows.ApplicationModel.Package.Current.Id.FamilyName
|
||||
// within the app service provider to get this value.
|
||||
CommunicationService.PackageFamilyName = "3568ebdf-5b6b-4ddd-bb17-462d614ba50f_gspb8g6x97k2t";
|
||||
|
||||
var status = await CommunicationService.OpenAsync();
|
||||
|
||||
if (status != AppServiceConnectionStatus.Success)
|
||||
{
|
||||
Log.Error("Failed to connect to App Service host.");
|
||||
CommunicationService = null;
|
||||
throw new Exception("Failed to connect to App Service host.");
|
||||
}
|
||||
}
|
||||
|
||||
// Call the service.
|
||||
var message = new ValueSet();
|
||||
message.Add("Command", "Start");
|
||||
message.Add("Page", pageName);
|
||||
|
||||
AppServiceResponse response = await CommunicationService.SendMessageAsync(message);
|
||||
string result = string.Empty;
|
||||
|
||||
if (response.Status == AppServiceResponseStatus.Success)
|
||||
{
|
||||
// Get the data that the service sent to us.
|
||||
if (response.Message["Status"] as string == "OK")
|
||||
{
|
||||
Log.Comment("[Harness] Received Host Ready with Page: {0}", pageName);
|
||||
Wait.ForIdle();
|
||||
Log.Comment("[Harness] Starting Test for {0}...", pageName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Error case, we didn't get confirmation of test starting.
|
||||
throw new InvalidOperationException("Test host didn't confirm test ready to execute page: " + pageName);
|
||||
}
|
||||
|
||||
private void CommunicationService_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
|
||||
{
|
||||
AppServiceDeferral messageDeferral = args.GetDeferral();
|
||||
ValueSet message = args.Request.Message;
|
||||
string cmd = message["Command"] as string;
|
||||
|
||||
try
|
||||
{
|
||||
// Return the data to the caller.
|
||||
if (cmd == "Log")
|
||||
{
|
||||
string level = message["Level"] as string;
|
||||
string msg = message["Message"] as string;
|
||||
|
||||
switch (level)
|
||||
{
|
||||
case "Comment":
|
||||
Log.Comment("[Host] {0}", msg);
|
||||
break;
|
||||
case "Warning":
|
||||
Log.Warning("[Host] {0}", msg);
|
||||
break;
|
||||
case "Error":
|
||||
Log.Error("[Host] {0}", msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("Exception receiving message: {0}", e.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Complete the deferral so that the platform knows that we're done responding to the app service call.
|
||||
// Note: for error handling: this must be called even if SendResponseAsync() throws an exception.
|
||||
messageDeferral.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
// This will reset the test for each run (as from original WinUI https://github.com/microsoft/microsoft-ui-xaml/blob/master/test/testinfra/MUXTestInfra/Infra/TestHelpers.cs)
|
||||
// We construct it so it doesn't try to run any tests since we use the AppService Bridge to complete
|
||||
// our loading.
|
||||
private void PreTestSetup()
|
||||
{
|
||||
_ = new TestSetupHelper(new string[] { }, new TestSetupHelper.TestSetupHelperOptions()
|
||||
{
|
||||
AutomationIdOfSafeItemToClick = null
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,8 +8,38 @@
|
|||
<PropertyGroup Label="Configuration">
|
||||
<Import_RootNamespace>UITests.Tests.Shared</Import_RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<Choose>
|
||||
<!-- When we're in the test harness include all '*Test.cs' files -->
|
||||
<When Condition="'$(IsTestHarness)' == 'true'">
|
||||
<ItemGroup>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)**\*Test.cs" Exclude="**\bin\**\*Test.cs;**\obj\**\*Test.cs"/>
|
||||
<!-- Base test helpers -->
|
||||
<Compile Include="$(MSBuildThisFileDirectory)UITestBase.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)TestAssembly.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)TestPageAttribute.cs" />
|
||||
</ItemGroup>
|
||||
</When>
|
||||
</Choose>
|
||||
|
||||
<Choose>
|
||||
<!-- When we're in the UI app used by the test harness, include all the pages -->
|
||||
<When Condition="'$(IsTestHost)' == 'true'">
|
||||
<ItemGroup>
|
||||
<Page Include="$(MSBuildThisFileDirectory)**\*Page.xaml" Exclude="**\bin\**\*Page.xaml;**\obj\**\*Page.xaml" Link="%(RecursiveDir)%(FileName)%(Extension)" SubType="Designer" Generator="MSBuild:Compile" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)**\*Page.xaml.cs" DependentUpon="%(Filename)" />
|
||||
</ItemGroup>
|
||||
</When>
|
||||
</Choose>
|
||||
|
||||
<!-- Have a None Include as well to make all items visible in VS in the Shared Project -->
|
||||
<ItemGroup>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)TestAssembly.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Tests.cs" />
|
||||
<None Include="$(MSBuildThisFileDirectory)**\*Test.cs" Exclude="**\bin\**\*Test.cs;**\obj\**\*Test.cs"/>
|
||||
<None Include="$(MSBuildThisFileDirectory)**\*Page.xaml" Exclude="**\bin\**\*Page.xaml;**\obj\**\*Page.xaml" SubType="Designer" Generator="MSBuild:Compile" />
|
||||
<None Include="$(MSBuildThisFileDirectory)**\*Page.xaml.cs" DependentUpon="%(Filename)" />
|
||||
<!-- Base test helpers -->
|
||||
<None Include="$(MSBuildThisFileDirectory)UITestBase.cs" />
|
||||
<None Include="$(MSBuildThisFileDirectory)TestAssembly.cs" />
|
||||
<None Include="$(MSBuildThisFileDirectory)TestPageAttribute.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -15,6 +15,8 @@
|
|||
<RuntimeIdentifier>win10-x86</RuntimeIdentifier>
|
||||
<IsPackable>false</IsPackable>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
|
||||
<IsTestHarness>true</IsTestHarness>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -37,6 +39,10 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'netcoreapp3.1'"
|
||||
Include="Microsoft.Windows.SDK.Contracts"
|
||||
Version="10.0.19041.0" />
|
||||
|
||||
<PackageReference Include="MUXTestInfra.TAEF" Version="0.0.4" />
|
||||
<PackageReference Include="TAEF.Redist.Wlk" Version="10.31.180822002" GeneratePathProperty="true" />
|
||||
<PackageReference Include="MUXCustomBuildTasks" Version="1.0.67" GeneratePathProperty="true" />
|
||||
|
@ -64,5 +70,5 @@
|
|||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\UITests.Tests.Shared\UITests.Tests.Shared.projitems" />
|
||||
<Import Project="..\UITests.Tests.Shared\UITests.Tests.Shared.projitems" Label="Shared" />
|
||||
</Project>
|
|
@ -3,10 +3,10 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Uwp.Extensions;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Microsoft.Toolkit.Uwp.Extensions;
|
||||
|
||||
namespace UnitTests.XamlIslands.UWPApp
|
||||
{
|
||||
|
|
|
@ -55,6 +55,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||
global.json = global.json
|
||||
nuget.config = nuget.config
|
||||
settings.xamlstyler = settings.xamlstyler
|
||||
build\Windows.Toolkit.Uwp.Build.targets = build\Windows.Toolkit.Uwp.Build.targets
|
||||
build\Windows.Toolkit.VisualStudio.Design.props = build\Windows.Toolkit.VisualStudio.Design.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Toolkit.Uwp.UI.Controls.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Design\Microsoft.Toolkit.Uwp.UI.Controls.DesignTools.csproj", "{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}"
|
||||
|
@ -141,14 +143,17 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "UITests.Tests.Shared", "UIT
|
|||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
UITests\UITests.Tests.Shared\UITests.Tests.Shared.projitems*{05c83067-fa46-45e2-bec4-edee84ad18d0}*SharedItemsImports = 4
|
||||
UITests\UITests.Tests.Shared\UITests.Tests.Shared.projitems*{1d8b0260-5c17-41da-9c38-1e37441b3925}*SharedItemsImports = 13
|
||||
UnitTests\UnitTests.Shared\UnitTests.Shared.projitems*{4e9466d1-d5aa-46ac-801b-c8fdab79f0d4}*SharedItemsImports = 13
|
||||
UnitTests\UnitTests.HighPerformance.Shared\UnitTests.HighPerformance.Shared.projitems*{5524523e-db0f-41f7-a0d4-43128422a342}*SharedItemsImports = 4
|
||||
UITests\UITests.Tests.Shared\UITests.Tests.Shared.projitems*{5f720475-e263-4a5a-8c88-2b805b45b5bc}*SharedItemsImports = 5
|
||||
UnitTests\UnitTests.Shared\UnitTests.Shared.projitems*{7878cd00-85e8-4d02-9757-8a43db4c6510}*SharedItemsImports = 5
|
||||
UnitTests\UnitTests.Notifications.Shared\UnitTests.Notifications.Shared.projitems*{982cc826-aacd-4855-9075-430bb6ce40a9}*SharedItemsImports = 13
|
||||
UnitTests\UnitTests.HighPerformance.Shared\UnitTests.HighPerformance.Shared.projitems*{9b3a94a6-0d29-4523-880b-6938e2efeef7}*SharedItemsImports = 13
|
||||
UnitTests\UnitTests.Shared\UnitTests.Shared.projitems*{a139968e-ad78-4e8c-93b8-9a5523bcac89}*SharedItemsImports = 5
|
||||
UnitTests\UnitTests.Notifications.Shared\UnitTests.Notifications.Shared.projitems*{bab1caf4-c400-4a7f-a987-c576de63cffd}*SharedItemsImports = 4
|
||||
UITests\UITests.Tests.Shared\UITests.Tests.Shared.projitems*{c8182ef0-77fb-4b43-a588-c71748a309c7}*SharedItemsImports = 5
|
||||
UnitTests\UnitTests.HighPerformance.Shared\UnitTests.HighPerformance.Shared.projitems*{d9bdbc68-3d0a-47fc-9c88-0bf769101644}*SharedItemsImports = 5
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -872,6 +877,8 @@ Global
|
|||
{54349AB0-9E41-4AA6-849C-EC9CE80CDD2A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{54349AB0-9E41-4AA6-849C-EC9CE80CDD2A}.Release|x86.Deploy.0 = Release|Any CPU
|
||||
{05C83067-FA46-45E2-BEC4-EDEE84AD18D0}.Debug|Any CPU.ActiveCfg = Debug|x86
|
||||
{05C83067-FA46-45E2-BEC4-EDEE84AD18D0}.Debug|Any CPU.Build.0 = Debug|x86
|
||||
{05C83067-FA46-45E2-BEC4-EDEE84AD18D0}.Debug|Any CPU.Deploy.0 = Debug|x86
|
||||
{05C83067-FA46-45E2-BEC4-EDEE84AD18D0}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{05C83067-FA46-45E2-BEC4-EDEE84AD18D0}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{05C83067-FA46-45E2-BEC4-EDEE84AD18D0}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
|
|
Загрузка…
Ссылка в новой задаче