diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 2018763e733..15a32095f9b 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -273,6 +273,7 @@ + PreserveNewest @@ -607,6 +608,7 @@ + @@ -1565,4 +1567,4 @@ - \ No newline at end of file + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Pages/SampleController.xaml b/Microsoft.Toolkit.Uwp.SampleApp/Pages/SampleController.xaml index 57f54f29aa5..887f76df9cd 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Pages/SampleController.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/Pages/SampleController.xaml @@ -220,8 +220,8 @@ Width="11" Background="Transparent" HorizontalAlignment="Left" - GripperForeground="{ThemeResource Brush-Alt}" - ParentLevel="1" /> + ParentLevel="1"> + + + + + + + + + + + + + + Side Content + + + + + + + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ContentSizer/ContentSizer.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ContentSizer/ContentSizer.png new file mode 100644 index 00000000000..004cf329744 Binary files /dev/null and b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ContentSizer/ContentSizer.png differ diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/FrameworkElementExtensions/FrameworkElementExtensionsCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/FrameworkElementExtensions/FrameworkElementExtensionsCode.bind index 18e22db846f..7f6e9a52f17 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/FrameworkElementExtensions/FrameworkElementExtensionsCode.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/FrameworkElementExtensions/FrameworkElementExtensionsCode.bind @@ -31,13 +31,13 @@ + Foreground="{ThemeResource Brush-Alt}"> @@ -45,16 +45,12 @@ Grid.Row="1" Background="{ThemeResource Brush-Grey-04}" Height="11" HorizontalAlignment="Stretch"> - + Foreground="{ThemeResource Brush-Alt}" /> - diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GridSplitter/GridSplitter.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GridSplitter/GridSplitter.bind index 4cec3829383..3a31184b549 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GridSplitter/GridSplitter.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GridSplitter/GridSplitter.bind @@ -63,7 +63,6 @@ - - - diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml index a4187d65e0a..694fb69707f 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml @@ -50,6 +50,7 @@ + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 8cdfcc3a619..73610fa2068 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -175,6 +175,15 @@ "Icon": "/SamplePages/ScrollHeader/ScrollHeader.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/behaviors/HeaderBehaviors.md" }, + { + "Name": "ContentSizer", + "Subcategory": "Layout", + "About": "ContentSizer is a general sizing control which can manipulate the size of its parent or other elements. Used as a building block for more complex UI systems.", + "CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer", + "XamlCodeFile": "/SamplePages/ContentSizer/ContentSizer.bind", + "Icon": "/SamplePages/ContentSizer/ContentSizer.png", + "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/ContentSizer.md" + }, { "Name": "GridSplitter", "Type": "GridSplitterPage", diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/GridSplitter.Metadata.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/GridSplitter.Metadata.cs index cfc9fc1fa0e..d04fef137e0 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/GridSplitter.Metadata.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/GridSplitter.Metadata.cs @@ -20,7 +20,6 @@ public GridSplitterMetadata() b.AddCustomAttributes(nameof(GridSplitter.Element), new CategoryAttribute(Resources.CategoryCommon)); b.AddCustomAttributes(nameof(GridSplitter.ResizeDirection), new CategoryAttribute(Resources.CategoryCommon)); b.AddCustomAttributes(nameof(GridSplitter.ResizeBehavior), new CategoryAttribute(Resources.CategoryCommon)); - b.AddCustomAttributes(nameof(GridSplitter.GripperForeground), new CategoryAttribute(Resources.CategoryBrush)); b.AddCustomAttributes(nameof(GridSplitter.ParentLevel), new CategoryAttribute(Resources.CategoryCommon)); b.AddCustomAttributes(nameof(GridSplitter.GripperCursor), new CategoryAttribute(Resources.CategoryAppearance)); b.AddCustomAttributes(nameof(GridSplitter.GripperCustomCursorResource), diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/GridSplitter.Typedata.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/GridSplitter.Typedata.cs index 13c64c02a0e..ac420c22cc5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/GridSplitter.Typedata.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/GridSplitter.Typedata.cs @@ -17,7 +17,6 @@ internal static class GridSplitter internal const string Element = nameof(Element); internal const string GripperCursor = nameof(GripperCursor); internal const string GripperCustomCursorResource = nameof(GripperCustomCursorResource); - internal const string GripperForeground = nameof(GripperForeground); internal const string ParentLevel = nameof(ParentLevel); internal const string ResizeBehavior = nameof(ResizeBehavior); internal const string ResizeDirection = nameof(ResizeDirection); diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentResizeDirection.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentResizeDirection.cs new file mode 100644 index 00000000000..810769aaed3 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentResizeDirection.cs @@ -0,0 +1,28 @@ +// 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 +{ + /// + /// Enum to indicate whether resizes Vertically or Horizontally. + /// + public enum ContentResizeDirection + { + /// + /// Determines whether to resize rows or columns based on its Alignment and + /// width compared to height + /// + Auto, // TODO: Detect? + + /// + /// Resize columns when dragging Splitter. + /// + Vertical, + + /// + /// Resize rows when dragging Splitter. + /// + Horizontal + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentSizer.Events.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentSizer.Events.cs new file mode 100644 index 00000000000..eed7bccbbf6 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentSizer.Events.cs @@ -0,0 +1,58 @@ +// 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.Uwp.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + /// + /// Events for . + /// + public partial class ContentSizer + { + // If no values specified, setup our default behaviors. + private void ContentSizer_Loaded(object sender, RoutedEventArgs e) + { + // Adding Grip to Grid Splitter + if (Content == null) + { + // TODO: Make Converter to put in XAML? + Content = + ResizeDirection == ContentResizeDirection.Vertical ? GripperBarVertical : GripperBarHorizontal; + } + + if (TargetControl == null) + { + TargetControl = this.FindAscendant(); + } + } + + /// + protected override void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e) + { + var horizontalChange = e.Delta.Translation.X; + var verticalChange = e.Delta.Translation.Y; + + if (ResizeDirection == ContentResizeDirection.Vertical) + { + if (HorizontalMove(horizontalChange)) + { + return; + } + } + else if (ResizeDirection == ContentResizeDirection.Horizontal) + { + if (VerticalMove(verticalChange)) + { + return; + } + } + + base.OnManipulationDelta(e); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentSizer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentSizer.cs new file mode 100644 index 00000000000..14b042fcd7c --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentSizer.cs @@ -0,0 +1,50 @@ +// 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.Uwp.UI.Automation.Peers; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Automation.Peers; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + /// + /// The is a control which can be used to resize any element, usually its parent. If you are using a , use instead. + /// + public partial class ContentSizer : SplitBase + { + /// + /// Initializes a new instance of the class. + /// + public ContentSizer() + { + this.DefaultStyleKey = typeof(ContentSizer); + + KeyUp += SplitBase_KeyUp; + } + + /// + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + // Note, we re-register for the proper timing to check for default property values. If we just set Loaded once in our constructor this doesn't work... Not sure why... 🤷‍ + + // Unhook registered events + Loaded -= ContentSizer_Loaded; + + // Register Events + Loaded += ContentSizer_Loaded; + } + + /// + /// Creates AutomationPeer () + /// + /// An automation peer for this . + protected override AutomationPeer OnCreateAutomationPeer() + { + return new ContentSizerAutomationPeer(this); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentSizer.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentSizer.xaml new file mode 100644 index 00000000000..5de944f1b53 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentSizer.xaml @@ -0,0 +1,40 @@ + + + diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentSizerAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentSizerAutomationPeer.cs new file mode 100644 index 00000000000..9a9e71f3e19 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ContentSizer/ContentSizerAutomationPeer.cs @@ -0,0 +1,77 @@ +// 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.Uwp.UI.Controls; +using Windows.UI.Xaml.Automation; +using Windows.UI.Xaml.Automation.Peers; + +namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers +{ + /// + /// Defines a framework element automation peer for the control. + /// + public class ContentSizerAutomationPeer : FrameworkElementAutomationPeer + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The that is associated with this . + /// + public ContentSizerAutomationPeer(ContentSizer owner) + : base(owner) + { + } + + private ContentSizer OwningContentSizer + { + get + { + return Owner as ContentSizer; + } + } + + /// + /// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType, + /// differentiates the control represented by this AutomationPeer. + /// + /// The string that contains the name. + protected override string GetClassNameCore() + { + return Owner.GetType().Name; + } + + /// + /// Called by GetName. + /// + /// + /// Returns the first of these that is not null or empty: + /// - Value returned by the base implementation + /// - Name of the owning ContentSizer + /// - ContentSizer class name + /// + protected override string GetNameCore() + { + string name = AutomationProperties.GetName(this.OwningContentSizer); + if (!string.IsNullOrEmpty(name)) + { + return name; + } + + name = this.OwningContentSizer.Name; + if (!string.IsNullOrEmpty(name)) + { + return name; + } + + name = base.GetNameCore(); + if (!string.IsNullOrEmpty(name)) + { + return name; + } + + return string.Empty; + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Data.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Data.cs index 6781e4ba1bf..ed50b075314 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Data.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Data.cs @@ -57,87 +57,6 @@ public enum GridResizeBehavior PreviousAndNext } - /// - /// Enum to indicate the supported gripper cursor types. - /// - public enum GripperCursorType - { - /// - /// Change the cursor based on the splitter direction - /// - Default = -1, - - /// - /// Standard Arrow cursor - /// - Arrow, - - /// - /// Standard Cross cursor - /// - Cross, - - /// - /// Standard Custom cursor - /// - Custom, - - /// - /// Standard Hand cursor - /// - Hand, - - /// - /// Standard Help cursor - /// - Help, - - /// - /// Standard IBeam cursor - /// - IBeam, - - /// - /// Standard SizeAll cursor - /// - SizeAll, - - /// - /// Standard SizeNortheastSouthwest cursor - /// - SizeNortheastSouthwest, - - /// - /// Standard SizeNorthSouth cursor - /// - SizeNorthSouth, - - /// - /// Standard SizeNorthwestSoutheast cursor - /// - SizeNorthwestSoutheast, - - /// - /// Standard SizeWestEast cursor - /// - SizeWestEast, - - /// - /// Standard UniversalNo cursor - /// - UniversalNo, - - /// - /// Standard UpArrow cursor - /// - UpArrow, - - /// - /// Standard Wait cursor - /// - Wait - } - /// /// Enum to indicate the behavior of window cursor on grid splitter hover /// diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Events.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Events.cs index a310d1b7d03..f6809f280cf 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Events.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Events.cs @@ -5,9 +5,7 @@ using Windows.System; using Windows.UI.Core; using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; namespace Microsoft.Toolkit.Uwp.UI.Controls { @@ -16,55 +14,19 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls /// public partial class GridSplitter { - // Symbols for GripperBar in Segoe MDL2 Assets - private const string GripperBarVertical = "\xE784"; - private const string GripperBarHorizontal = "\xE76F"; - private const string GripperDisplayFont = "Segoe MDL2 Assets"; - private void GridSplitter_Loaded(object sender, RoutedEventArgs e) { _resizeDirection = GetResizeDirection(); _resizeBehavior = GetResizeBehavior(); // Adding Grip to Grid Splitter - if (Element == default(UIElement)) + if (Content == null) { - CreateGripperDisplay(); - Element = _gripperDisplay; + Content = + _resizeDirection == GridResizeDirection.Columns ? GripperBarVertical : GripperBarHorizontal; } - if (_hoverWrapper == null) - { - var hoverWrapper = new GripperHoverWrapper( - CursorBehavior == SplitterCursorBehavior.ChangeOnSplitterHover - ? this - : Element, - _resizeDirection, - GripperCursor, - GripperCustomCursorResource); - ManipulationStarted += hoverWrapper.SplitterManipulationStarted; - ManipulationCompleted += hoverWrapper.SplitterManipulationCompleted; - - _hoverWrapper = hoverWrapper; - } - } - - private void CreateGripperDisplay() - { - if (_gripperDisplay == null) - { - _gripperDisplay = new TextBlock - { - FontFamily = new FontFamily(GripperDisplayFont), - HorizontalAlignment = HorizontalAlignment.Center, - VerticalAlignment = VerticalAlignment.Center, - Foreground = GripperForeground, - Text = _resizeDirection == GridResizeDirection.Columns ? GripperBarVertical : GripperBarHorizontal - }; - _gripperDisplay.SetValue( - Windows.UI.Xaml.Automation.AutomationProperties.AccessibilityViewProperty, - Windows.UI.Xaml.Automation.Peers.AccessibilityView.Raw); - } + GripperCursor = _resizeDirection == GridResizeDirection.Columns ? CoreCursorType.SizeWestEast : CoreCursorType.SizeNorthSouth; } /// @@ -121,30 +83,12 @@ protected override void OnKeyDown(KeyRoutedEventArgs e) protected override void OnManipulationStarted(ManipulationStartedRoutedEventArgs e) { // saving the previous state - PreviousCursor = Window.Current.CoreWindow.PointerCursor; _resizeDirection = GetResizeDirection(); _resizeBehavior = GetResizeBehavior(); - if (_resizeDirection == GridResizeDirection.Columns) - { - Window.Current.CoreWindow.PointerCursor = ColumnsSplitterCursor; - } - else if (_resizeDirection == GridResizeDirection.Rows) - { - Window.Current.CoreWindow.PointerCursor = RowSplitterCursor; - } - base.OnManipulationStarted(e); } - /// - protected override void OnManipulationCompleted(ManipulationCompletedRoutedEventArgs e) - { - Window.Current.CoreWindow.PointerCursor = PreviousCursor; - - base.OnManipulationCompleted(e); - } - /// protected override void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e) { @@ -174,7 +118,7 @@ protected override void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e) base.OnManipulationDelta(e); } - private bool VerticalMove(double verticalChange) + private new bool VerticalMove(double verticalChange) { if (CurrentRow == null || SiblingRow == null) { @@ -240,7 +184,7 @@ private bool VerticalMove(double verticalChange) return false; } - private bool HorizontalMove(double horizontalChange) + private new bool HorizontalMove(double horizontalChange) { if (CurrentColumn == null || SiblingColumn == null) { diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Options.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Options.cs index 5564c0336c7..fe32372c12f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Options.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Options.cs @@ -4,8 +4,6 @@ using Windows.UI.Core; using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media; namespace Microsoft.Toolkit.Uwp.UI.Controls { @@ -14,20 +12,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls /// public partial class GridSplitter { - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty ElementProperty - = DependencyProperty.Register( - nameof(Element), - typeof(UIElement), - typeof(GridSplitter), - new PropertyMetadata(default(UIElement), OnElementPropertyChanged)); - /// /// Identifies the dependency property. /// - public static readonly DependencyProperty ResizeDirectionProperty + public static new readonly DependencyProperty ResizeDirectionProperty = DependencyProperty.Register( nameof(ResizeDirection), typeof(GridResizeDirection), @@ -44,16 +32,6 @@ public static readonly DependencyProperty ResizeBehaviorProperty typeof(GridSplitter), new PropertyMetadata(GridResizeBehavior.BasedOnAlignment)); - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty GripperForegroundProperty - = DependencyProperty.Register( - nameof(GripperForeground), - typeof(Brush), - typeof(GridSplitter), - new PropertyMetadata(default(Brush), OnGripperForegroundPropertyChanged)); - /// /// Identifies the dependency property. /// @@ -64,26 +42,6 @@ public static readonly DependencyProperty ParentLevelProperty typeof(GridSplitter), new PropertyMetadata(default(int))); - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty GripperCursorProperty = - DependencyProperty.RegisterAttached( - nameof(GripperCursor), - typeof(CoreCursorType?), - typeof(GridSplitter), - new PropertyMetadata(GripperCursorType.Default, OnGripperCursorPropertyChanged)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty GripperCustomCursorResourceProperty = - DependencyProperty.RegisterAttached( - nameof(GripperCustomCursorResource), - typeof(uint), - typeof(GridSplitter), - new PropertyMetadata(GripperCustomCursorDefaultResource, GripperCustomCursorResourcePropertyChanged)); - /// /// Identifies the dependency property. /// @@ -92,24 +50,14 @@ public static readonly DependencyProperty ParentLevelProperty nameof(CursorBehavior), typeof(SplitterCursorBehavior), typeof(GridSplitter), - new PropertyMetadata(SplitterCursorBehavior.ChangeOnSplitterHover, CursorBehaviorPropertyChanged)); - - /// - /// Gets or sets the visual content of this Grid Splitter - /// - public UIElement Element - { - get { return (UIElement)GetValue(ElementProperty); } - set { SetValue(ElementProperty, value); } - } + new PropertyMetadata(SplitterCursorBehavior.ChangeOnSplitterHover)); /// /// Gets or sets whether the Splitter resizes the Columns, Rows, or Both. /// - public GridResizeDirection ResizeDirection + public new GridResizeDirection ResizeDirection { get { return (GridResizeDirection)GetValue(ResizeDirectionProperty); } - set { SetValue(ResizeDirectionProperty, value); } } @@ -119,48 +67,18 @@ public GridResizeDirection ResizeDirection public GridResizeBehavior ResizeBehavior { get { return (GridResizeBehavior)GetValue(ResizeBehaviorProperty); } - set { SetValue(ResizeBehaviorProperty, value); } } - /// - /// Gets or sets the foreground color of grid splitter grip - /// - public Brush GripperForeground - { - get { return (Brush)GetValue(GripperForegroundProperty); } - - set { SetValue(GripperForegroundProperty, value); } - } - /// /// Gets or sets the level of the parent grid to resize /// public int ParentLevel { get { return (int)GetValue(ParentLevelProperty); } - set { SetValue(ParentLevelProperty, value); } } - /// - /// Gets or sets the gripper Cursor type - /// - public GripperCursorType GripperCursor - { - get { return (GripperCursorType)GetValue(GripperCursorProperty); } - set { SetValue(GripperCursorProperty, value); } - } - - /// - /// Gets or sets the gripper Custom Cursor resource number - /// - public int GripperCustomCursorResource - { - get { return (int)GetValue(GripperCustomCursorResourceProperty); } - set { SetValue(GripperCustomCursorResourceProperty, value); } - } - /// /// Gets or sets splitter cursor on hover behavior /// @@ -169,61 +87,5 @@ public SplitterCursorBehavior CursorBehavior get { return (SplitterCursorBehavior)GetValue(CursorBehaviorProperty); } set { SetValue(CursorBehaviorProperty, value); } } - - private static void OnGripperForegroundPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var gridSplitter = (GridSplitter)d; - - if (gridSplitter._gripperDisplay == null) - { - return; - } - - gridSplitter._gripperDisplay.Foreground = gridSplitter.GripperForeground; - } - - private static void OnGripperCursorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var gridSplitter = (GridSplitter)d; - - if (gridSplitter._hoverWrapper == null) - { - return; - } - - gridSplitter._hoverWrapper.GripperCursor = gridSplitter.GripperCursor; - } - - private static void GripperCustomCursorResourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var gridSplitter = (GridSplitter)d; - - if (gridSplitter._hoverWrapper == null) - { - return; - } - - gridSplitter._hoverWrapper.GripperCustomCursorResource = gridSplitter.GripperCustomCursorResource; - } - - private static void CursorBehaviorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var gridSplitter = (GridSplitter)d; - - gridSplitter._hoverWrapper?.UpdateHoverElement(gridSplitter.CursorBehavior == - SplitterCursorBehavior.ChangeOnSplitterHover - ? gridSplitter - : gridSplitter.Element); - } - - private static void OnElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var gridSplitter = (GridSplitter)d; - - gridSplitter._hoverWrapper?.UpdateHoverElement(gridSplitter.CursorBehavior == - SplitterCursorBehavior.ChangeOnSplitterHover - ? gridSplitter - : gridSplitter.Element); - } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.cs index c6f807397aa..35a9db74767 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.cs @@ -2,29 +2,21 @@ // 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.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Automation; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Markup; namespace Microsoft.Toolkit.Uwp.UI.Controls { /// /// Represents the control that redistributes space between columns or rows of a Grid control. /// - public partial class GridSplitter : Control + public partial class GridSplitter : SplitBase { - internal const int GripperCustomCursorDefaultResource = -1; - internal static readonly CoreCursor ColumnsSplitterCursor = new CoreCursor(CoreCursorType.SizeWestEast, 1); - internal static readonly CoreCursor RowSplitterCursor = new CoreCursor(CoreCursorType.SizeNorthSouth, 1); - - internal CoreCursor PreviousCursor { get; set; } - private GridResizeDirection _resizeDirection; private GridResizeBehavior _resizeBehavior; - private GripperHoverWrapper _hoverWrapper; - private TextBlock _gripperDisplay; private bool _pressed = false; private bool _dragging = false; @@ -33,7 +25,7 @@ public partial class GridSplitter : Control /// /// Gets the target parent grid from level /// - private FrameworkElement TargetControl + private new FrameworkElement TargetControl { get { @@ -182,8 +174,6 @@ protected override void OnApplyTemplate() ManipulationStarted -= GridSplitter_ManipulationStarted; ManipulationCompleted -= GridSplitter_ManipulationCompleted; - _hoverWrapper?.UnhookEvents(); - // Register Events Loaded += GridSplitter_Loaded; PointerEntered += GridSplitter_PointerEntered; @@ -192,10 +182,6 @@ protected override void OnApplyTemplate() PointerReleased += GridSplitter_PointerReleased; ManipulationStarted += GridSplitter_ManipulationStarted; ManipulationCompleted += GridSplitter_ManipulationCompleted; - - _hoverWrapper?.UpdateHoverElement(Element); - - ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY; } private void GridSplitter_PointerReleased(object sender, PointerRoutedEventArgs e) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.xaml index 451e054d16f..619396a271e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.xaml @@ -1,6 +1,7 @@ + xmlns:local="using:Microsoft.Toolkit.Uwp.UI.Controls" + xmlns:ui="using:Microsoft.Toolkit.Uwp.UI"> @@ -26,15 +27,28 @@ - + + + + + + + + + + - + diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GripperHoverWrapper.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GripperHoverWrapper.cs deleted file mode 100644 index 6b22b204886..00000000000 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GripperHoverWrapper.cs +++ /dev/null @@ -1,169 +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 Windows.UI.Core; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Input; - -namespace Microsoft.Toolkit.Uwp.UI.Controls -{ - internal class GripperHoverWrapper - { - private readonly GridSplitter.GridResizeDirection _gridSplitterDirection; - - private CoreCursor _splitterPreviousPointer; - private CoreCursor _previousCursor; - private GridSplitter.GripperCursorType _gripperCursor; - private int _gripperCustomCursorResource; - private bool _isDragging; - private UIElement _element; - - internal GridSplitter.GripperCursorType GripperCursor - { - get - { - return _gripperCursor; - } - - set - { - _gripperCursor = value; - } - } - - internal int GripperCustomCursorResource - { - get - { - return _gripperCustomCursorResource; - } - - set - { - _gripperCustomCursorResource = value; - } - } - - /// - /// Initializes a new instance of the class that add cursor change on hover functionality for GridSplitter. - /// - /// UI element to apply cursor change on hover - /// GridSplitter resize direction - /// GridSplitter gripper on hover cursor type - /// GridSplitter gripper custom cursor resource number - internal GripperHoverWrapper(UIElement element, GridSplitter.GridResizeDirection gridSplitterDirection, GridSplitter.GripperCursorType gripperCursor, int gripperCustomCursorResource) - { - _gridSplitterDirection = gridSplitterDirection; - _gripperCursor = gripperCursor; - _gripperCustomCursorResource = gripperCustomCursorResource; - _element = element; - UnhookEvents(); - _element.PointerEntered += Element_PointerEntered; - _element.PointerExited += Element_PointerExited; - } - - internal void UpdateHoverElement(UIElement element) - { - UnhookEvents(); - _element = element; - _element.PointerEntered += Element_PointerEntered; - _element.PointerExited += Element_PointerExited; - } - - private void Element_PointerExited(object sender, PointerRoutedEventArgs e) - { - if (_isDragging) - { - // if dragging don't update the cursor just update the splitter cursor with the last window cursor, - // because the splitter is still using the arrow cursor and will revert to original case when drag completes - _splitterPreviousPointer = _previousCursor; - } - else - { - Window.Current.CoreWindow.PointerCursor = _previousCursor; - } - } - - private void Element_PointerEntered(object sender, PointerRoutedEventArgs e) - { - // if not dragging - if (!_isDragging) - { - _previousCursor = _splitterPreviousPointer = Window.Current.CoreWindow.PointerCursor; - UpdateDisplayCursor(); - } - - // if dragging - else - { - _previousCursor = _splitterPreviousPointer; - } - } - - private void UpdateDisplayCursor() - { - if (_gripperCursor == GridSplitter.GripperCursorType.Default) - { - if (_gridSplitterDirection == GridSplitter.GridResizeDirection.Columns) - { - Window.Current.CoreWindow.PointerCursor = GridSplitter.ColumnsSplitterCursor; - } - else if (_gridSplitterDirection == GridSplitter.GridResizeDirection.Rows) - { - Window.Current.CoreWindow.PointerCursor = GridSplitter.RowSplitterCursor; - } - } - else - { - var coreCursor = (CoreCursorType)((int)_gripperCursor); - if (_gripperCursor == GridSplitter.GripperCursorType.Custom) - { - if (_gripperCustomCursorResource > GridSplitter.GripperCustomCursorDefaultResource) - { - Window.Current.CoreWindow.PointerCursor = new CoreCursor(coreCursor, (uint)_gripperCustomCursorResource); - } - } - else - { - Window.Current.CoreWindow.PointerCursor = new CoreCursor(coreCursor, 1); - } - } - } - - internal void SplitterManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) - { - var splitter = sender as GridSplitter; - if (splitter == null) - { - return; - } - - _splitterPreviousPointer = splitter.PreviousCursor; - _isDragging = true; - } - - internal void SplitterManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) - { - var splitter = sender as GridSplitter; - if (splitter == null) - { - return; - } - - Window.Current.CoreWindow.PointerCursor = splitter.PreviousCursor = _splitterPreviousPointer; - _isDragging = false; - } - - internal void UnhookEvents() - { - if (_element == null) - { - return; - } - - _element.PointerEntered -= Element_PointerEntered; - _element.PointerExited -= Element_PointerExited; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/SplitBase.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/SplitBase.cs new file mode 100644 index 00000000000..b29b191778e --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/SplitBase.cs @@ -0,0 +1,288 @@ +// 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.Core; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Markup; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + /// + /// Base class for splitting/resizing controls + /// + [ContentProperty(Name = nameof(Content))] + public partial class SplitBase : Control + { + /// + /// Check for new requested vertical size is valid or not + /// + /// Target control being resized + /// The requested vertical change + /// The parent control's ActualHeight + /// Bool result if requested vertical change is valid or not + protected static bool IsValidHeight(FrameworkElement target, double verticalChange, double parentActualHeight) + { + var newHeight = target.ActualHeight + verticalChange; + + var minHeight = target.MinHeight; + if (newHeight < 0 || (!double.IsNaN(minHeight) && newHeight < minHeight)) + { + return false; + } + + var maxHeight = target.MaxHeight; + if (!double.IsNaN(maxHeight) && newHeight > maxHeight) + { + return false; + } + + if (newHeight <= parentActualHeight) + { + return false; + } + + return true; + } + + /// + /// Check for new requested horizontal size is valid or not + /// + /// Target control being resized + /// The requested horizontal change + /// The parent control's ActualWidth + /// Bool result if requested horizontal change is valid or not + protected static bool IsValidWidth(FrameworkElement target, double horizontalChange, double parentActualWidth) + { + var newWidth = target.ActualWidth + horizontalChange; + + var minWidth = target.MinWidth; + if (newWidth < 0 || (!double.IsNaN(minWidth) && newWidth < minWidth)) + { + return false; + } + + var maxWidth = target.MaxWidth; + if (!double.IsNaN(maxWidth) && newWidth > maxWidth) + { + return false; + } + + if (newWidth <= parentActualWidth) + { + return false; + } + + return true; + } + + /// + /// Vertical symbol for GripperBar in Segoe MDL2 Font asset. + /// + protected const string GripperBarVertical = "\xE784"; + + /// + /// Horizontal symbol for GripperBar in Segoe MDL2 Font asset. + /// + protected const string GripperBarHorizontal = "\xE76F"; + + /// + /// Distance (horizontal or vertical) to move, in response to keyboard activity. + /// + protected const double GripperKeyboardChange = 8.0d; + + /// + /// Gets or sets the content of the splitter control. + /// + public object Content + { + get { return (object)GetValue(ContentProperty); } + set { SetValue(ContentProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ContentProperty = + DependencyProperty.Register(nameof(Content), typeof(object), typeof(SplitBase), new PropertyMetadata(null)); + + /// + /// Gets or sets the content template for the . + /// + public DataTemplate ContentTemplate + { + get { return (DataTemplate)GetValue(ContentTemplateProperty); } + set { SetValue(ContentTemplateProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ContentTemplateProperty = + DependencyProperty.Register(nameof(ContentTemplate), typeof(DataTemplate), typeof(SplitBase), new PropertyMetadata(null)); + + /// + /// Gets or sets the cursor to use when hovering over the sizer. + /// + public CoreCursorType GripperCursor + { + get { return (CoreCursorType)GetValue(GripperCursorProperty); } + set { SetValue(GripperCursorProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty GripperCursorProperty = + DependencyProperty.Register(nameof(GripperCursor), typeof(CoreCursorType), typeof(SplitBase), new PropertyMetadata(CoreCursorType.SizeWestEast)); + + /// + /// Gets or sets the direction that the sizer will interact with. + /// + public ContentResizeDirection ResizeDirection + { + get { return (ContentResizeDirection)GetValue(ResizeDirectionProperty); } + set { SetValue(ResizeDirectionProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ResizeDirectionProperty = + DependencyProperty.Register(nameof(ResizeDirection), typeof(ContentResizeDirection), typeof(SplitBase), new PropertyMetadata(ContentResizeDirection.Vertical)); + + /// + /// Gets or sets the control that the is resizing. Be default, this will be the visual ancestor of the . + /// + public FrameworkElement TargetControl + { + get { return (FrameworkElement)GetValue(TargetControlProperty); } + set { SetValue(TargetControlProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty TargetControlProperty = + DependencyProperty.Register(nameof(TargetControl), typeof(FrameworkElement), typeof(SplitBase), new PropertyMetadata(null, OnTargetControlChanged)); + + private static void OnTargetControlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + // Check if our width can be manipulated + if (d is SplitBase splitterBase && e.NewValue is FrameworkElement element) + { + // TODO: For Auto we might want to do detection logic (TBD) here first? + if (splitterBase.ResizeDirection != ContentResizeDirection.Horizontal && double.IsNaN(element.Width)) + { + element.Width = element.DesiredSize.Width; + } + + if (splitterBase.ResizeDirection != ContentResizeDirection.Vertical && double.IsNaN(element.Height)) + { + element.Height = element.DesiredSize.Height; + } + } + } + + /// + /// Gets or sets a value indicating whether the control is resizing in the opposite direction. + /// + public bool IsDragInverted + { + get { return (bool)GetValue(IsDragInvertedProperty); } + set { SetValue(IsDragInvertedProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty IsDragInvertedProperty = + DependencyProperty.Register(nameof(IsDragInverted), typeof(bool), typeof(SplitBase), new PropertyMetadata(false)); + + /// + /// Process requested vertical resizing. + /// + /// The requested vertical change + /// result of processed vertical change + protected bool VerticalMove(double verticalChange) + { + if (TargetControl == null) + { + return true; + } + + verticalChange = IsDragInverted ? -verticalChange : verticalChange; + + if (!IsValidHeight(TargetControl, verticalChange, ActualHeight)) + { + return true; + } + + // Do we need our ContentResizeDirection to be 4 way? Maybe 'Auto' would check the horizontal/vertical alignment of the target??? + TargetControl.Height += verticalChange; + + GripperCursor = CoreCursorType.SizeNorthSouth; + + return false; + } + + /// + /// Process requested horizontal resizing. + /// + /// The requested horizontal change + /// result of processed horizontal change + protected bool HorizontalMove(double horizontalChange) + { + if (TargetControl == null) + { + return true; + } + + horizontalChange = IsDragInverted ? -horizontalChange : horizontalChange; + + if (!IsValidWidth(TargetControl, horizontalChange, ActualWidth)) + { + return true; + } + + TargetControl.Width += horizontalChange; + + GripperCursor = CoreCursorType.SizeWestEast; + + return false; + } + + /// + /// Processes KeyUp event + /// + /// The sender of the event + /// A which contains the event data + protected void SplitBase_KeyUp(object sender, KeyRoutedEventArgs e) + { + if (ResizeDirection == ContentResizeDirection.Vertical) + { + if (e.Key == Windows.System.VirtualKey.Left) + { + HorizontalMove(-GripperKeyboardChange); + } + else if (e.Key == Windows.System.VirtualKey.Right) + { + HorizontalMove(GripperKeyboardChange); + } + } + else + { + if (e.Key == Windows.System.VirtualKey.Up) + { + VerticalMove(-GripperKeyboardChange); + } + else if (e.Key == Windows.System.VirtualKey.Down) + { + VerticalMove(GripperKeyboardChange); + } + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Strings/en-US/Resources.resw b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Strings/en-US/Resources.resw index 49a13464e39..3593b3964d4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Strings/en-US/Resources.resw +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Strings/en-US/Resources.resw @@ -69,4 +69,8 @@ GridSplitter Narrator Resource for GridSplitter control + + Content Sizer + Narrator Resource for ContentSizer control + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Themes/Generic.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Themes/Generic.xaml index fd84465abdd..0dd0bd41c94 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Themes/Generic.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Themes/Generic.xaml @@ -3,6 +3,7 @@ + diff --git a/UITests/UITests.Tests.Shared/Controls/GridSplitterTestPage.xaml b/UITests/UITests.Tests.Shared/Controls/GridSplitterTestPage.xaml index 09236488742..c0e6da4750e 100644 --- a/UITests/UITests.Tests.Shared/Controls/GridSplitterTestPage.xaml +++ b/UITests/UITests.Tests.Shared/Controls/GridSplitterTestPage.xaml @@ -81,14 +81,6 @@ Grid.ColumnSpan="3" Height="16" VerticalAlignment="Top"> - - - diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_ContentSizer.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_ContentSizer.cs new file mode 100644 index 00000000000..6cef47822f8 --- /dev/null +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_ContentSizer.cs @@ -0,0 +1,36 @@ +// 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.Uwp.UI.Automation.Peers; +using Microsoft.Toolkit.Uwp.UI.Controls; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; +using Windows.UI.Xaml.Automation; +using Windows.UI.Xaml.Automation.Peers; + +namespace UnitTests.UWP.UI.Controls +{ + [TestClass] + [TestCategory("Test_ContentSizer")] + public class Test_ContentSizer + { + [UITestMethod] + public void ShouldConfigureContentSizerAutomationPeer() + { + const string automationName = "MyContentSizer"; + const string name = "ContentSizer"; + + var contentSizer = new ContentSizer(); + var contentSizerAutomationPeer = FrameworkElementAutomationPeer.CreatePeerForElement(contentSizer) as ContentSizerAutomationPeer; + + Assert.IsNotNull(contentSizerAutomationPeer, "Verify that the AutomationPeer is ContentSizerAutomationPeer."); + + contentSizer.Name = name; + Assert.IsTrue(contentSizerAutomationPeer.GetName().Contains(name), "Verify that the UIA name contains the given Name of the ContentSizer."); + + contentSizer.SetValue(AutomationProperties.NameProperty, automationName); + Assert.IsTrue(contentSizerAutomationPeer.GetName().Contains(automationName), "Verify that the UIA name contains the given AutomationProperties.Name of the ContentSizer."); + } + } +} diff --git a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj index d50b438032e..771f2a76567 100644 --- a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj +++ b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj @@ -243,6 +243,7 @@ +