Skip to content

Commit

Permalink
Add new WrapLayout and StaggeredLayout controls (#3160)
Browse files Browse the repository at this point in the history
* Update dev/7.0.0 branch version number

* Add a new project for layout controls

* First port of the StaggeredPanel to the new StaggeredLayout

* Create a new class that will hold layout information

* Early exit if we don't have any items

* Early exit for no realization area to render to

* Don't work with the UIElement directly within the measure method. Instead use a new StaggeredItem to track the size of the element

* Stop measuring elements when we are outside the realization rect

* Track the top of the element on the StaggeredItem

* Track the column layout and use that to arrange the items after measure

* Make sure the top of the elemet accounts for the spacing between items

* Move the column width to the state

* Remove horizontal alignment calculations. I don't believe this should be part of a layout. The ItemsRepeater will handle that

* No need to arrange anything if there is no realization bounds

* No need to keep arranging if we are outside the bounds

* Return the column for the given index

* Return the final size always

* Get the item in the column layout, not the index

* Stop measuring after we have measured all columns that fit the bounds

* If the column width is different from what was previously there then clear the items because we will need to recalculate the size of the items

* Manually recycle elements that fall outside of the bounds

* since we are staying within one column we do not need to track the height and number of items in each column

* Make sure we remove the state

* Rename method to clear all the things

* When the width of the panel changes we could get more columns to use. In that case clear out the cache of columns

* We ALWAYS want to measure an item that will be in the bounds

* Calculate the height based on the number of average number of items we have or, if we have all of the items use the total height

* Remove manual recycling logic

* Remove padding property to be in line with the WinUI layouts

* Little code Clean up

* Add some comments

* Remove method not used anymore

* We've already calculated the top. Use that value for placement

* Remove items from the layout state when an item is removed from the layout itself

* Clear the columns after the removal item

* We don't need to clear any of the cache if the an item was added/removed where we haven't cached

* Clear cached items when an item is added

* Clear the entire cache when the collection has been reset

* No need to calculate the number of columns. We have it in our layout state

* First port of the WrapPanel to be a Layout

* Stop measuring items when we are outside the bounds

* Only arrange items within the bounds

* The bottom of the items needs to be within the top bounds.

* Keep track of the element measure and only get the element when we need to

* break out of the arrange when we have passed the realization bounds

* Always measure items that are within the bounds

* Add the <inheritdoc /> tag

* Clear items when the collection changes

* When the Orientation changes, clear the cache

* No need to clear the entire collection. We can just switch the values

* Only check if we're past the bounds when we move to the next row

* Remove Padding to be in line with the WinUI Layouts

* Add debugger info

* Store the position of items instead of calculating each time

* Reset the position of items whenever a layout property changes

* Clear positions whenever the available U value changes

* Clear the available U whenever orientation changes

* Remove property not used

* Make sure to null the position when orientation changes

* Remove the attached property and store the spacing in the state

* Calculate the height of the items

* Don't average items that haven't had their position calculated yet

* Fix type in variable

* Make sure elements get recycled when they are outside the scope of the bounds. Move getting the elements to the layout

* Ignore row spacing for determining which column an items goes to

* This array holds ints, not doubles

* Add height to the debugger display

* We need to recalculate the height when the RowSpacing changes

* Revert the change to not account for row spacing when finding the column. Items will jump around when playing with RowSpacing but that's not a common use case and items were not placed in the right column

* Use the laest version of WinUI

* Make sure elements that are above the bounds are recycled

* Recycle an element if it is below the bounds

* Make method static

* Add case handling for items being replaced

* Switch to a switch statement

* Add a comment as to what is happening when the height is zero

* Add required space after brace

* Handle moving of items

* Recycle elements when they are replaced or moved to ensure the correct context

* Switch to a swtich statement for the items changing

* Add support for moving an item in the wrap layout

* Do not clear the items from the collection. zero out their measure values so they get recalculated

* Add handling for an item being replaced in the wrap layout

* A move could effect the position of items after it. So just clear them out

* Add header to file

* Remove unused usings

* Add summary comment for the class

* Add a simple WrapLayout sample

* Add a simple sample for the new staggered layout

* Put the property on different lines

* Remove trailing white space

* Remove empty Item group

* recycle elements that are below the bounds

* Rename local method

* Only measure an item if it wasn't already measured. This helps speed up items when first being measured

* Increae the size of the items

* Add the location for code and documentation

* Track the elements that are used and make sure they recycled when they are outside of the bounds of the realization area

* Change the category for the layouts

* When an item changes size we need to set a new height for the item and reset everything after it

* If the current measure of the item does not match then the size of the item changed. We need to recalculate layout for everything after it

* Need to check changes in V also

* Just use the handy Equals method!

* No need to remove items that are above index

* Move items to a new row if the size makes them larger than the given space

Co-authored-by: Michael Hawker MSFT (XAML Llama) <michael.hawker@outlook.com>
Co-authored-by: Kyaa-dost <35208324+Kyaa-dost@users.noreply.github.com>
  • Loading branch information
3 people committed May 15, 2020
1 parent 9e7ab5d commit c25f25b
Show file tree
Hide file tree
Showing 25 changed files with 1,509 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
<Version>6.1.0-build.6</Version>
</PackageReference>
<PackageReference Include="Microsoft.UI.Xaml">
<Version>2.2.190917002</Version>
<Version>2.3.200213001</Version>
</PackageReference>
<PackageReference Include="Monaco.Editor">
<Version>0.7.0-alpha</Version>
Expand Down Expand Up @@ -273,6 +273,7 @@
<Content Include="SamplePages\OnDevice\OnDevice.png" />
<Content Include="SamplePages\RemoteDeviceHelper\RemoteDeviceHelper.png" />
<Content Include="SamplePages\ImageCropper\ImageCropper.png" />
<Content Include="SamplePages\StaggeredLayout\StaggeredLayout.png" />
<Content Include="SamplePages\TabView\TabView.png" />
<Content Include="SamplePages\PeoplePicker\PeoplePicker.png" />
<Content Include="SamplePages\PersonView\PersonView.png" />
Expand Down Expand Up @@ -367,6 +368,7 @@
<Content Include="SamplePages\ViewportBehavior\ViewportBehavior.png" />
<Content Include="SamplePages\Visual Extensions\VisualExtensions.png" />
<Content Include="SamplePages\Weibo Service\WeiboLogo.png" />
<Content Include="SamplePages\WrapLayout\WrapLayout.png" />
<Content Include="SamplePages\WrapPanel\WrapPanel.png" />
<Content Include="landingPageLinks.json" />
<Content Include="Assets\mtns.csv">
Expand Down Expand Up @@ -531,10 +533,16 @@
<Compile Include="SamplePages\ImageCropper\ImageCropperPage.xaml.cs">
<DependentUpon>ImageCropperPage.xaml</DependentUpon>
</Compile>
<Compile Include="SamplePages\StaggeredLayout\StaggeredLayoutPage.xaml.cs">
<DependentUpon>StaggeredLayoutPage.xaml</DependentUpon>
</Compile>
<Compile Include="SamplePages\TokenizingTextBox\SampleDataType.cs" />
<Compile Include="SamplePages\TokenizingTextBox\TokenizingTextBoxPage.xaml.cs">
<DependentUpon>TokenizingTextBoxPage.xaml</DependentUpon>
</Compile>
<Compile Include="SamplePages\WrapLayout\WrapLayoutPage.xaml.cs">
<DependentUpon>WrapLayoutPage.xaml</DependentUpon>
</Compile>
<Compile Include="Shell.Search.cs" />
<Compile Include="Shell.SamplePicker.cs" />
<Content Include="SamplePages\ViewportBehavior\ViewportBehaviorXaml.bind" />
Expand All @@ -545,6 +553,8 @@
<Content Include="SamplePages\Eyedropper\EyedropperXaml.bind" />
<Content Include="SamplePages\Eyedropper\EyedropperCode.bind" />
<Content Include="SamplePages\TokenizingTextBox\TokenizingTextBoxCode.bind" />
<Content Include="SamplePages\WrapLayout\WrapLayout.bind" />
<Content Include="SamplePages\StaggeredLayout\StaggeredLayout.bind" />
<Content Include="SamplePages\ObservableGroup\ObservableGroup.bind" />
</ItemGroup>
<ItemGroup>
Expand Down Expand Up @@ -959,6 +969,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="SamplePages\StaggeredLayout\StaggeredLayoutPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="SamplePages\TabView\TabViewPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
Expand Down Expand Up @@ -1335,6 +1349,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="SamplePages\WrapLayout\WrapLayoutPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="SamplePages\WrapPanel\WrapPanelPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
Expand Down Expand Up @@ -1408,6 +1426,10 @@
<Project>{daeb9cec-c817-33b2-74b2-bc379380db72}</Project>
<Name>Microsoft.Toolkit.Uwp.UI.Controls.DataGrid</Name>
</ProjectReference>
<ProjectReference Include="..\Microsoft.Toolkit.Uwp.UI.Controls.Layout\Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj">
<Project>{cb444381-18ba-4a51-bb32-3a498bcc1e99}</Project>
<Name>Microsoft.Toolkit.Uwp.UI.Controls.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\Microsoft.Toolkit.Uwp.UI.Controls\Microsoft.Toolkit.Uwp.UI.Controls.csproj">
<Project>{e9faabfb-d726-42c1-83c1-cb46a29fea81}</Project>
<Name>Microsoft.Toolkit.Uwp.UI.Controls</Name>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<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"
xmlns:winui="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d">

<Page.Resources>
<DataTemplate x:Key="StaggeredTemplate">
<Grid Background="{ThemeResource Brush-Grey-04}">
<Border Width="{Binding Width}" Height="{Binding Height}">
<Border.Background>
<SolidColorBrush Color="{Binding Color}"/>
</Border.Background>
<TextBlock Text="{Binding Index}" FontSize="20"/>
</Border>
</Grid>

</DataTemplate>
</Page.Resources>

<Grid Padding="48">
<ScrollViewer >
<winui:ItemsRepeater x:Name="StaggeredRepeater"
ItemTemplate="{StaticResource StaggeredTemplate}">
<winui:ItemsRepeater.Layout>
<controls:StaggeredLayout DesiredColumnWidth="@[DesiredColumnWidth:Slider:250:50-400]"
ColumnSpacing="@[ColumnSpacing:Slider:5:0-50]@"
RowSpacing="@[RowSpacing:Slider:5:0-50]@"/>
</winui:ItemsRepeater.Layout>
</winui:ItemsRepeater>
</ScrollViewer>
</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
@@ -0,0 +1,14 @@
<Page
x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.StaggeredLayoutPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Microsoft.Toolkit.Uwp.SampleApp.SamplePages.StaggeredLayout"
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>

</Grid>
</Page>
Original file line number Diff line number Diff line change
@@ -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 System;
using System.Collections.ObjectModel;
using Microsoft.Toolkit.Uwp.UI.Extensions;
using Microsoft.UI.Xaml.Controls;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class StaggeredLayoutPage : Page, IXamlRenderListener
{
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
private Random _random;

public StaggeredLayoutPage()
{
this.InitializeComponent();

_random = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < _random.Next(1000, 5000); i++)
{
var item = new Item { Index = i, Width = _random.Next(50, 250), Height = _random.Next(50, 250), Color = Color.FromArgb(255, (byte)_random.Next(0, 255), (byte)_random.Next(0, 255), (byte)_random.Next(0, 255)) };
_items.Add(item);
}
}

public void OnXamlRendered(FrameworkElement control)
{
var repeater = control.FindChildByName("StaggeredRepeater") as ItemsRepeater;

if (repeater != null)
{
repeater.ItemsSource = _items;
}
}

private class Item
{
public int Index { get; internal set; }

public int Width { get; internal set; }

public int Height { get; internal set; }

public Color Color { get; internal set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<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"
xmlns:winui="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d">

<Page.Resources>
<DataTemplate x:Key="WrapTemplate">
<Border Width="{Binding Width}" Height="50">
<Border.Background>
<SolidColorBrush Color="{Binding Color}"/>
</Border.Background>
<TextBlock Text="{Binding Index}" FontSize="20"/>
</Border>
</DataTemplate>
</Page.Resources>

<Grid Padding="48">
<ScrollViewer >
<winui:ItemsRepeater x:Name="WrapRepeater"
Background="{ThemeResource Brush-Grey-04}"
ItemTemplate="{StaticResource WrapTemplate}">
<winui:ItemsRepeater.Layout>
<controls:WrapLayout x:Name="Wrap"
VerticalSpacing="@[VerticalSpacing:Slider:5:0-200]@"
HorizontalSpacing="@[HorizontalSpacing:Slider:5:0-200]@"/>
</winui:ItemsRepeater.Layout>
</winui:ItemsRepeater>
</ScrollViewer>
</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
@@ -0,0 +1,15 @@
<Page
x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.WrapLayoutPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Microsoft.Toolkit.Uwp.SampleApp.SamplePages.WrapLayout"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:winui="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<Grid>

</Grid>
</Page>
Original file line number Diff line number Diff line change
@@ -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 System;
using System.Collections.ObjectModel;
using Microsoft.Toolkit.Uwp.UI.Extensions;
using Microsoft.UI.Xaml.Controls;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class WrapLayoutPage : Page, IXamlRenderListener
{
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
private Random _random;

public WrapLayoutPage()
{
this.InitializeComponent();

_random = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < _random.Next(1000, 5000); i++)
{
var item = new Item { Index = i, Width = _random.Next(50, 250), Height = _random.Next(50, 250), Color = Color.FromArgb(255, (byte)_random.Next(0, 255), (byte)_random.Next(0, 255), (byte)_random.Next(0, 255)) };
_items.Add(item);
}
}

public void OnXamlRendered(FrameworkElement control)
{
var repeater = control.FindChildByName("WrapRepeater") as ItemsRepeater;

if (repeater != null)
{
repeater.ItemsSource = _items;
}
}

private class Item
{
public int Index { get; internal set; }

public int Width { get; internal set; }

public int Height { get; internal set; }

public Color Color { get; internal set; }
}
}
}
22 changes: 21 additions & 1 deletion Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,16 @@
"Icon": "/SamplePages/WrapPanel/WrapPanel.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/WrapPanel.md"
},
{
"Name": "WrapLayout",
"Type": "WrapLayoutPage",
"Subcategory": "Layout - ItemsRepeater",
"About": "The WrapLayout virtualizes child elements in sequential position from left to right, breaking content to the next line at the edge of the containing box.",
"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout",
"XamlCodeFile": "WrapLayout.bind",
"Icon": "/SamplePages/WrapLayout/WrapLayout.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/layout/WrapLayout.md"
},
{
"Name": "OrbitView",
"Type": "OrbitViewPage",
Expand Down Expand Up @@ -302,6 +312,16 @@
"Icon": "/SamplePages/StaggeredPanel/StaggeredPanel.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/StaggeredPanel.md"
},
{
"Name": "StaggeredLayout",
"Type": "StaggeredLayoutPage",
"Subcategory": "Layout - ItemsRepeater",
"About": "The StaggeredLayout virtualizes items in a column approach where an item will be added to whichever column has used the least amount of space.",
"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout",
"XamlCodeFile": "StaggeredLayout.bind",
"Icon": "/SamplePages/StaggeredLayout/StaggeredLayout.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/layout/StaggeredLayout.md"
},
{
"Name": "LayoutTransformControl",
"Type": "LayoutTransformControlPage",
Expand Down Expand Up @@ -424,7 +444,7 @@
"XamlCodeFile": "FocusTrackerXaml.bind",
"Icon": "/SamplePages/FocusTracker/FocusTracker.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/developer-tools/FocusTracker.md"
}//,
} //,
//{
// "Name": "TokenizingTextBox",
// "Type": "TokenizingTextBoxPage",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Project Sdk="MSBuild.Sdk.Extras">

<PropertyGroup>
<TargetFramework>uap10.0.17134</TargetFramework>
<TargetPlatformVersion Condition="'$(TargetPlatformVersion)' == '' ">10.0.18362.0</TargetPlatformVersion>
<Title>Windows Community Toolkit Layout</Title>
<Description>
This library provides XAML layout controls. It is part of the Windows Community Toolkit.
</Description>
<RootNamespace>Microsoft.Toolkit.Uwp.UI.Controls</RootNamespace>
</PropertyGroup>

<ItemGroup>
<None Include="VisualStudioToolsManifest.xml" Pack="true" PackagePath="tools" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.UI.Xaml" Version="2.3.200213001" />
</ItemGroup>

<Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />

<!-- https://weblogs.asp.net/rweigelt/disable-warnings-in-generated-c-files-of-uwp-app -->
<Target Name="PragmaWarningDisablePrefixer" AfterTargets="MarkupCompilePass2">
<ItemGroup>
<GeneratedCSFiles Include="**\*.g.cs;**\*.g.i.cs" />
</ItemGroup>
<Message Text="CSFiles: @(GeneratedCSFiles->'&quot;%(Identity)&quot;')" />
<Exec Command="for %%f in (@(GeneratedCSFiles->'&quot;%(Identity)&quot;')) do echo #pragma warning disable &gt; %%f.temp &amp;&amp; type %%f &gt;&gt; %%f.temp &amp;&amp; move /y %%f.temp %%f &gt; NUL" />
</Target>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">

</wpf:ResourceDictionary>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 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.Resources;
using System.Runtime.CompilerServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: InternalsVisibleTo("UnitTests")]
[assembly: NeutralResourcesLanguage("en-US")]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Library Name="Microsoft.Toolkit.Uwp.UI.Controls.Layout">
</Library>
</Directives>
Loading

0 comments on commit c25f25b

Please sign in to comment.