Skip to content

Commit

Permalink
Add initial ConstrainedPresenter control for AspectRatio and simple S…
Browse files Browse the repository at this point in the history
…ample.

TODO: Tests and trying out more scenarios and better sample.
  • Loading branch information
michael-hawker committed Jul 9, 2021
1 parent 0cb2ea6 commit 5dfb5c8
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
Expand Down Expand Up @@ -277,6 +277,7 @@
<Content Include="SamplePages\Graph\LoginButton.png" />
<Content Include="SamplePages\Graph\PeoplePicker.png" />
<Content Include="SamplePages\Graph\PersonView.png" />
<Content Include="SamplePages\Primitives\ConstrainedPresenter.png" />
<Content Include="SamplePages\Primitives\SwitchPresenter.png" />
<Content Include="SamplePages\TabbedCommandBar\TabbedCommandBar.png" />
<Content Include="SamplePages\Animations\Effects\FadeBehavior.png" />
Expand Down Expand Up @@ -626,6 +627,9 @@
<Content Include="SamplePages\Graph\LoginButtonXaml.bind" />
<Content Include="SamplePages\Graph\PeoplePickerXaml.bind" />
<Content Include="SamplePages\Graph\PersonViewXaml.bind" />
<Content Include="SamplePages\Primitives\ConstrainedPresenter.bind">
<SubType>Designer</SubType>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Include="App.xaml.cs">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Page
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:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
mc:Ignorable="d">

<Grid>
<controls:ConstrainedPresenter AspectRatio="16:3" VerticalAlignment="Top">
<Image Source="/Assets/Photos/WestSeattleView.jpg" Stretch="UniformToFill"/>
</controls:ConstrainedPresenter>
</Grid>
</Page>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<controls:Case IsDefault="True" />
</controls:CaseCollection>
</controls:SwitchPresenter>
<controls:ConstrainedPresenter x:Key="ConstrainedPresenterControl" />
</Page.Resources>

<Grid>
Expand Down
11 changes: 10 additions & 1 deletion Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
"Type": "ScrollHeaderPage",
"Subcategory": "Layout",
"About": "A UI control that works as a ListView or GridView header control with quick return, sticky and fade behavior.",
"CodeUrl" : "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Behaviors/Headers",
"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Behaviors/Headers",
"XamlCodeFile": "ScrollHeaderCode.bind",
"Icon": "/SamplePages/ScrollHeader/ScrollHeader.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/behaviors/HeaderBehaviors.md"
Expand Down Expand Up @@ -475,6 +475,15 @@
"XamlCodeFile": "/SamplePages/Primitives/SwitchPresenter.bind",
"Icon": "/SamplePages/Primitives/SwitchPresenter.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/SwitchPresenter.md"
},
{
"Name": "ConstrainedPresenter",
"Subcategory": "Layout",
"About": "The ConstrainedPresenter is a ContentPresenter which can allow a developer to constrain the scale or aspect ratio of its content.",
"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Primitives/ConstrainedPresenter",
"XamlCodeFile": "/SamplePages/Primitives/ConstrainedPresenter.bind",
"Icon": "/SamplePages/Primitives/ConstrainedPresenter.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/ConstrainedPresenter.md"
}
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 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 Microsoft.Toolkit.Uwp.UI.Controls
{
/// <summary>
/// The <see cref="AspectRatio"/> structure is used by the <see cref="ConstrainedPresenter"/> control to
/// define a specific ratio to restrict its content.
/// </summary>
[Windows.Foundation.Metadata.CreateFromString(MethodName = "Microsoft.Toolkit.Uwp.UI.Controls.AspectRatio.ConvertToAspectRatio")]
public readonly struct AspectRatio
{
/// <summary>
/// Gets the width component of the aspect ratio or the aspect ratio itself (and height will be 1).
/// </summary>
public double Width { get; }

/// <summary>
/// Gets the height component of the aspect ratio.
/// </summary>
public double Height { get; }

/// <summary>
/// Gets the raw numeriucal aspect ratio value itself (Width / Height).
/// </summary>
public double Value => Width / Height;

/// <summary>
/// Initializes a new instance of the <see cref="AspectRatio"/> struct with the provided width and height.
/// </summary>
/// <param name="width">Width side of the ratio.</param>
/// <param name="height">Height side of the ratio.</param>
public AspectRatio(double width, double height)
{
Width = width;
Height = height;
}

/// <summary>
/// Initializes a new instance of the <see cref="AspectRatio"/> struct with the specific numerical aspect ratio.
/// </summary>
/// <param name="ratio">Raw Aspect Ratio, Height will be 1.</param>
public AspectRatio(double ratio)
{
Width = ratio;
Height = 1;
}

/// <summary>
/// Converter to take a string aspect ration like "16:9" and convert it to an <see cref="AspectRatio"/> struct.
/// Used automatically by XAML.
/// </summary>
/// <param name="rawString">The string to be converted in format "Width:Height" or a decimal value.</param>
/// <returns>The <see cref="AspectRatio"/> struct representing that ratio.</returns>
public static AspectRatio ConvertToAspectRatio(string rawString)
{
string[] ratio = rawString.Split(":");

if (ratio.Length == 2)
{
return new AspectRatio(Convert.ToDouble(ratio[0]), Convert.ToDouble(ratio[1]));
}
else if (ratio.Length == 1)
{
return new AspectRatio(Convert.ToDouble(ratio[0]));
}

return new AspectRatio(1);
}

/// <inheritdoc/>
public override string ToString()
{
return Width + ":" + Height;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// 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.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Microsoft.Toolkit.Uwp.UI.Controls
{
/// <summary>
/// The <see cref="ConstrainedPresenter"/> is a <see cref="ContentPresenter"/> control which can restrict the
/// available size for its content based on a scale factor and/or a specific <see cref="AspectRatio"/>.
/// </summary>
public class ConstrainedPresenter : ContentPresenter // TODO: Think it should be a Border? But it's sealed; hopefully can change in WinUI 3.
{
/// <summary>
/// Gets or sets aspect Ratio to use for the contents of the Panel (after scaling).
/// </summary>
public AspectRatio AspectRatio
{
get { return (AspectRatio)GetValue(AspectRatioProperty); }
set { SetValue(AspectRatioProperty, value); }
}

/// <summary>
/// Identifies the <see cref="AspectRatio"/> property.
/// </summary>
public static readonly DependencyProperty AspectRatioProperty =
DependencyProperty.Register(nameof(AspectRatio), typeof(AspectRatio), typeof(ConstrainedPresenter), new PropertyMetadata(null, AspectRatioPropertyChanged));

private static void AspectRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ConstrainedPresenter panel)
{
panel.InvalidateMeasure();
}
}

/// <summary>
/// Gets or sets the scale for the width of the panel. Should be a value between 0-1.0. Default is 1.0.
/// </summary>
public double ScaleX
{
get { return (double)GetValue(ScaleXProperty); }
set { SetValue(ScaleXProperty, value); }
}

/// <summary>
/// Identifies the <see cref="ScaleX"/> property.
/// </summary>
public static readonly DependencyProperty ScaleXProperty =
DependencyProperty.Register(nameof(ScaleX), typeof(double), typeof(ConstrainedPresenter), new PropertyMetadata(1.0, ScalePropertyChanged));

/// <summary>
/// Gets or sets the scale for the height of the panel. Should be a value between 0-1.0. Default is 1.0.
/// </summary>
public double ScaleY
{
get { return (double)GetValue(ScaleYProperty); }
set { SetValue(ScaleYProperty, value); }
}

/// <summary>
/// Identifies the <see cref="ScaleY"/> property.
/// </summary>
public static readonly DependencyProperty ScaleYProperty =
DependencyProperty.Register(nameof(ScaleY), typeof(double), typeof(ConstrainedPresenter), new PropertyMetadata(1.0, ScalePropertyChanged));

private static void ScalePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ConstrainedPresenter panel)
{
panel.InvalidateMeasure();
}
}

/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
return base.MeasureOverride(CalculateConstrainedSize(availableSize));
}

/// <inheritdoc/>
protected override Size ArrangeOverride(Size finalSize)
{
return base.ArrangeOverride(CalculateConstrainedSize(finalSize));
}

private Size CalculateConstrainedSize(Size initialSize)
{
var availableSize = new Size(initialSize.Width * ScaleX, initialSize.Height * ScaleY);

// If we don't have an Aspect Ratio, just return the scaled value.
if (ReadLocalValue(AspectRatioProperty) == DependencyProperty.UnsetValue)
{
return availableSize;
}

// Calculate the Aspect Ratio constraint based on the newly scaled size.
var currentAspect = availableSize.Width / availableSize.Height;
var desiredAspect = AspectRatio.Value;

if (currentAspect >= desiredAspect)
{
return new Size(availableSize.Height * desiredAspect, availableSize.Height);
}
else
{
return new Size(availableSize.Width, availableSize.Width / desiredAspect);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public Case CurrentCase
}

/// <summary>
/// Indicates the <see cref="CurrentCase"/> property.
/// Identifies the <see cref="CurrentCase"/> property.
/// </summary>
public static readonly DependencyProperty CurrentCaseProperty =
DependencyProperty.Register(nameof(CurrentCase), typeof(Case), typeof(SwitchPresenter), new PropertyMetadata(null));
Expand All @@ -43,7 +43,7 @@ public CaseCollection SwitchCases
}

/// <summary>
/// Indicates the <see cref="SwitchCases"/> property.
/// Identifies the <see cref="SwitchCases"/> property.
/// </summary>
public static readonly DependencyProperty SwitchCasesProperty =
DependencyProperty.Register(nameof(SwitchCases), typeof(CaseCollection), typeof(SwitchPresenter), new PropertyMetadata(null, OnSwitchCasesPropertyChanged));
Expand All @@ -58,7 +58,7 @@ public object Value
}

/// <summary>
/// Indicates the <see cref="Value"/> property.
/// Identifies the <see cref="Value"/> property.
/// </summary>
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(nameof(Value), typeof(object), typeof(SwitchPresenter), new PropertyMetadata(null, OnValuePropertyChanged));
Expand All @@ -73,7 +73,7 @@ public Type TargetType
}

/// <summary>
/// Indicates the <see cref="TargetType"/> property.
/// Identifies the <see cref="TargetType"/> property.
/// </summary>
public static readonly DependencyProperty TargetTypeProperty =
DependencyProperty.Register(nameof(TargetType), typeof(Type), typeof(SwitchPresenter), new PropertyMetadata(null));
Expand Down

0 comments on commit 5dfb5c8

Please sign in to comment.