Use HorizontalGrid and VerticalGrid string values to specify GridItemsLayout in XAML (#8104)

* Convert HorizontalGrid and VerticalGrid strings to ItemsLayout (#5577)

* Support GridLayout in ItemsLayoutDesignTypeConverter #5577

* Add Issue5577 control to test ItemsLayoutConverter

* Optimize ItemsLayoutTypeConverter

* Expect InvalidOperationException, when span is missing in ItemsLayoutTypeConverter

Co-authored-by: E.Z. Hart <hartez@users.noreply.github.com>
This commit is contained in:
Rastislav Novotný 2020-06-06 05:34:17 +08:00 коммит произвёл GitHub
Родитель 3cf328628c
Коммит f06cb1f652
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 386 добавлений и 3 удалений

Просмотреть файл

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<controls:TestContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
x:Class="Xamarin.Forms.Controls.Issues.Issue5577">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" LineBreakMode="WordWrap" Text="Page should display 2 collection view with horizontal and vertical grid layouts"/>
<CollectionView Grid.Row="1" ItemsSource="{Binding Animals}" ItemsLayout="HorizontalGrid, 2" BackgroundColor="Yellow">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Name}"/>
<Label Text="{Binding Location}"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<CollectionView Grid.Row="2" ItemsSource="{Binding Animals}" ItemsLayout="VerticalGrid, 4">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Name}"/>
<Label Text="{Binding Location}"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</controls:TestContentPage>

Просмотреть файл

@ -0,0 +1,113 @@
using System.Collections.ObjectModel;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Xaml;
#if UITEST
using Xamarin.Forms.Core.UITests;
#endif
namespace Xamarin.Forms.Controls.Issues
{
#if UITEST
[NUnit.Framework.Category(UITestCategories.CollectionView)]
#endif
#if APP
[XamlCompilation(XamlCompilationOptions.Compile)]
#endif
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 5577, "CollectionView XAML API suggestion", PlatformAffected.All)]
public partial class Issue5577 : TestContentPage
{
#if APP
public Issue5577()
{
InitializeComponent();
BindingContext = new ViewModel5577();
}
#endif
protected override void Init()
{
}
}
[Preserve(AllMembers = true)]
public class ViewModel5577
{
public ViewModel5577()
{
AddAnimals();
}
public ObservableCollection<Model5577> Animals { get; private set; } = new ObservableCollection<Model5577>();
private void AddAnimals()
{
Animals.Add(new Model5577
{
Name = "Afghan Hound",
Location = "Afghanistan",
});
Animals.Add(new Model5577
{
Name = "Alpine Dachsbracke",
Location = "Austria",
});
Animals.Add(new Model5577
{
Name = "American Bulldog",
Location = "United States",
});
Animals.Add(new Model5577
{
Name = "Bearded Collie",
Location = "Scotland",
});
Animals.Add(new Model5577
{
Name = "Boston Terrier",
Location = "United States",
});
Animals.Add(new Model5577
{
Name = "Canadian Eskimo",
Location = "Canada",
});
Animals.Add(new Model5577
{
Name = "Eurohound",
Location = "Scandinavia",
});
Animals.Add(new Model5577
{
Name = "Irish Terrier",
Location = "Ireland",
});
Animals.Add(new Model5577
{
Name = "Kerry Beagle",
Location = "Ireland",
});
Animals.Add(new Model5577
{
Name = "Norwegian Buhund",
Location = "Norway",
});
}
}
[Preserve(AllMembers = true)]
public class Model5577
{
public string Name { get; set; }
public string Location { get; set; }
public override string ToString()
{
return Name;
}
}
}

Просмотреть файл

@ -121,6 +121,10 @@
<DependentUpon>Issue7048.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue5577.xaml.cs">
<DependentUpon>Issue5577.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue6804.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue7181.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue5367.cs" />
@ -1945,6 +1949,12 @@
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue5577.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8263.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>

Просмотреть файл

@ -32,7 +32,7 @@
{
if (Values == null)
{
var names = new List<string>() { "VerticalList", "HorizontalList" };
var names = new List<string>() { "VerticalList", "HorizontalList", "VerticalGrid", "HorizontalGrid" };
Values = new StandardValuesCollection(names);
}

Просмотреть файл

@ -0,0 +1,188 @@
using System;
using NUnit.Framework;
namespace Xamarin.Forms.Core.UnitTests
{
[TestFixture]
public class ItemsLayoutTypeConverterTests : BaseTestFixture
{
[Test]
public void HorizontalListShouldReturnLinearItemsLayout()
{
var converter = new ItemsLayoutTypeConverter();
var result = converter.ConvertFromInvariantString("HorizontalList");
Assert.AreSame(LinearItemsLayout.Horizontal, result);
}
[Test]
public void VerticalListShouldReturnLinearItemsLayout()
{
var converter = new ItemsLayoutTypeConverter();
var result = converter.ConvertFromInvariantString("VerticalList");
Assert.AreSame(LinearItemsLayout.Vertical, result);
}
[Test]
public void HorizontalGridShouldReturnGridItemsLayout()
{
var converter = new ItemsLayoutTypeConverter();
var result = converter.ConvertFromInvariantString("HorizontalGrid");
Assert.IsInstanceOf<GridItemsLayout>(result);
var gridItemsLayout = (GridItemsLayout)result;
Assert.AreEqual(ItemsLayoutOrientation.Horizontal, gridItemsLayout.Orientation);
Assert.AreEqual(1, gridItemsLayout.Span);
}
[Test]
public void VerticalGridShouldReturnGridItemsLayout()
{
var converter = new ItemsLayoutTypeConverter();
var result = converter.ConvertFromInvariantString("VerticalGrid");
Assert.IsInstanceOf<GridItemsLayout>(result);
var gridItemsLayout = (GridItemsLayout)result;
Assert.AreEqual(ItemsLayoutOrientation.Vertical, gridItemsLayout.Orientation);
Assert.AreEqual(1, gridItemsLayout.Span);
}
[Test]
public void HorizontalGridWithSpan4ShouldReturnGridItemsLayout()
{
var converter = new ItemsLayoutTypeConverter();
var result = converter.ConvertFromInvariantString("HorizontalGrid, 4");
Assert.IsInstanceOf<GridItemsLayout>(result);
var gridItemsLayout = (GridItemsLayout)result;
Assert.AreEqual(ItemsLayoutOrientation.Horizontal, gridItemsLayout.Orientation);
Assert.AreEqual(4, gridItemsLayout.Span);
}
[Test]
public void VerticalGridWithSpan2ShouldReturnGridItemsLayout()
{
var converter = new ItemsLayoutTypeConverter();
var result = converter.ConvertFromInvariantString("VerticalGrid,\t\t2");
Assert.IsInstanceOf<GridItemsLayout>(result);
var gridItemsLayout = (GridItemsLayout)result;
Assert.AreEqual(ItemsLayoutOrientation.Vertical, gridItemsLayout.Orientation);
Assert.AreEqual(2, gridItemsLayout.Span);
}
[Test]
public void HorizontalGridWithSpan987654ShouldReturnGridItemsLayout()
{
var converter = new ItemsLayoutTypeConverter();
var result = converter.ConvertFromInvariantString("HorizontalGrid,98654");
Assert.IsInstanceOf<GridItemsLayout>(result);
var gridItemsLayout = (GridItemsLayout)result;
Assert.AreEqual(ItemsLayoutOrientation.Horizontal, gridItemsLayout.Orientation);
Assert.AreEqual(98654, gridItemsLayout.Span);
}
[Test]
public void VerticalGridWithSpan1234ShouldReturnGridItemsLayout()
{
var converter = new ItemsLayoutTypeConverter();
var result = converter.ConvertFromInvariantString("VerticalGrid, \t 1234");
Assert.IsInstanceOf<GridItemsLayout>(result);
var gridItemsLayout = (GridItemsLayout)result;
Assert.AreEqual(ItemsLayoutOrientation.Vertical, gridItemsLayout.Orientation);
Assert.AreEqual(1234, gridItemsLayout.Span);
}
[Test]
public void HorizontalGridWithSpan0ShouldShouldThrowArgumentException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<ArgumentException>(() => converter.ConvertFromInvariantString("HorizontalGrid, 0"));
}
[Test]
public void VerticalGridWithSpan0ShouldShouldThrowArgumentException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<ArgumentException>(() => converter.ConvertFromInvariantString("VerticalGrid, 0"));
}
[Test]
public void HorizontalGridWithoutSpanShouldShouldThrowFormatException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<InvalidOperationException>(() => converter.ConvertFromInvariantString("HorizontalGrid,"));
}
[Test]
public void VerticalGridWithoutSpanShouldShouldThrowFormatException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<InvalidOperationException>(() => converter.ConvertFromInvariantString("VerticalGrid,"));
}
[Test]
public void HorizontalGridWithSpanIsNotStringShouldShouldThrowFormatException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<FormatException>(() => converter.ConvertFromInvariantString("HorizontalGrid,test"));
}
[Test]
public void VerticalGridWithSpanIs1point5ShouldShouldThrowFormatException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<FormatException>(() => converter.ConvertFromInvariantString("VerticalGrid, 1.5"));
}
[Test]
public void VerticalGridWith2ArgumentsShouldShouldThrowFormatException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<FormatException>(() => converter.ConvertFromInvariantString("VerticalGrid, 2, 3"));
}
[Test]
public void HorizontalGridWithSemicolonShouldShouldThrowInvalidOperationException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<InvalidOperationException>(() => converter.ConvertFromInvariantString("HorizontalGrid; 2"));
}
[Test]
public void LinearItemsLayoutShouldThrowInvalidOperationException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<InvalidOperationException>(() => converter.ConvertFromInvariantString("LinearItemsLayout"));
}
[Test]
public void HorizontalListWithArgumentShouldShouldThrowInvalidOperationException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<InvalidOperationException>(() => converter.ConvertFromInvariantString("HorizontalList, 1"));
}
[Test]
public void VerticalGridWithArgumentShouldShouldThrowInvalidOperationException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<InvalidOperationException>(() => converter.ConvertFromInvariantString("VerticalList, 2"));
}
[Test]
public void EmptyStringShouldThrowInvalidOperationException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<InvalidOperationException>(() => converter.ConvertFromInvariantString(string.Empty));
}
[Test]
public void NullShouldThrowArgumentNullException()
{
var converter = new ItemsLayoutTypeConverter();
Assert.Throws<ArgumentNullException>(() => converter.ConvertFromInvariantString(null));
}
}
}

Просмотреть файл

@ -76,6 +76,7 @@
<Compile Include="CommandSourceTests.cs" />
<Compile Include="CommandTests.cs" />
<Compile Include="DependencyResolutionTests.cs" />
<Compile Include="ItemsLayoutTypeConverterTests.cs" />
<Compile Include="MultiBindingTests.cs" />
<Compile Include="Markup\DefaultBindablePropertiesTests.cs" />
<Compile Include="Markup\BindableObjectExtensionsTests.cs" />

Просмотреть файл

@ -1,4 +1,5 @@
using System;
using System.Globalization;
namespace Xamarin.Forms
{
@ -7,15 +8,46 @@ namespace Xamarin.Forms
{
public override object ConvertFromInvariantString(string value)
{
if (value == "HorizontalList")
if (value == null)
{
return LinearItemsLayout.Horizontal;
throw new ArgumentNullException(nameof(value));
}
ItemsLayoutOrientation? orientation = default(ItemsLayoutOrientation?);
int identifierLength = 0;
if (value == "VerticalList")
{
return LinearItemsLayout.Vertical;
}
else if (value == "HorizontalList")
{
return LinearItemsLayout.Horizontal;
}
else if (value.StartsWith("VerticalGrid", StringComparison.Ordinal))
{
orientation = ItemsLayoutOrientation.Vertical;
identifierLength = "VerticalGrid".Length;
}
else if (value.StartsWith("HorizontalGrid", StringComparison.Ordinal))
{
orientation = ItemsLayoutOrientation.Horizontal;
identifierLength = "HorizontalGrid".Length;
}
if (orientation.HasValue)
{
if (value.Length == identifierLength)
{
return new GridItemsLayout(orientation.Value);
}
else if (value.Length > identifierLength + 1 && value[identifierLength] == ',')
{
var argument = value.Substring(identifierLength + 1);
var span = int.Parse(argument, NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture);
return new GridItemsLayout(span, orientation.Value);
}
}
throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(IItemsLayout)}");
}