Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

[UWP] Allow to dynamically change the CollectionView ItemsLayout #10797

Merged
merged 4 commits into from
Jun 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?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.Issue10482"
Title="Issue 10482">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackLayout Orientation="Vertical" Spacing="5" Grid.Row="0" VerticalOptions="Center">
<Label x:Name="Label" LineBreakMode="WordWrap" Text="Switch between linear and grid layouts. If layouts appear as expected with proper spacing between items, the test passes." HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Button AutomationId="Button5354" Text="Switch to grid layout" HorizontalOptions="Center" VerticalOptions="Center" Clicked="OnButtonLayoutClicked"/>
</StackLayout>
<StackLayout Grid.Row="1">
<Label x:Name="Label2" LineBreakMode="WordWrap" Text="After change the CollectionView ItemsLayout, just verify that everything continues working as expected." HorizontalTextAlignment="Center" VerticalTextAlignment="Center" />
<Button AutomationId="Button5355" Text="Scroll To (10)" HorizontalOptions="Center" VerticalOptions="Center" Clicked="OnScrollButtonClicked" />
</StackLayout>
<CollectionView AutomationId="CollectionView5354" Grid.Row="2" ItemsSource="{Binding Items}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="5"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Vertical" Spacing="10" BackgroundColor="Beige" Padding="10">
<Image Source="{Binding Source}"/>
<Label Text="{Binding Text}" HorizontalTextAlignment="Center" AutomationId="{Binding AutomationId}"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</controls:TestContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System.Collections.ObjectModel;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
using System;

#if UITEST
using Xamarin.UITest;
using Xamarin.UITest.Queries;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif

namespace Xamarin.Forms.Controls.Issues
{
#if UITEST
[Category(UITestCategories.CollectionView)]
#endif
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 10482, "CollectionView ItemsLayout Does Not Update in UWP", PlatformAffected.UWP)]
public partial class Issue10482 : TestContentPage
{
int _count = 0;

#if APP
public Issue10482()
{
InitializeComponent();

BindingContext = new ViewModel10482();
}
#endif

protected override void Init()
{

}

void OnButtonLayoutClicked(object sender, EventArgs e)
{
var button = sender as Button;
var stackLayout = button.Parent as StackLayout;
var grid = stackLayout.Parent as Grid;
var collectionView = grid.Children[2] as CollectionView;

if (_count % 2 == 0)
{
collectionView.ItemsLayout = new GridItemsLayout(ItemsLayoutOrientation.Vertical)
{
Span = 2,
HorizontalItemSpacing = 5,
VerticalItemSpacing = 5
};

button.Text = "Switch to linear layout";
}
else
{
collectionView.ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Vertical)
{
ItemSpacing = 5
};

button.Text = "Switch to grid layout";
}

++_count;
}

void OnScrollButtonClicked(object sender, EventArgs e)
{
var button = sender as Button;
var stackLayout = button.Parent as StackLayout;
var grid = stackLayout.Parent as Grid;
var collectionView = grid.Children[2] as CollectionView;

collectionView.ScrollTo(10);
}
}

[Preserve(AllMembers = true)]
public class ViewModel10482
{
public ObservableCollection<Model10482> Items { get; set; }

public ViewModel10482()
{
var collection = new ObservableCollection<Model10482>();
var pageSize = 50;

for (var i = 0; i < pageSize; i++)
{
collection.Add(new Model10482
{
Text = "Image" + i,
Source = i % 2 == 0 ?
"https://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Kamchatka_Brown_Bear_near_Dvuhyurtochnoe_on_2015-07-23.jpg/320px-Kamchatka_Brown_Bear_near_Dvuhyurtochnoe_on_2015-07-23.jpg" :
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Elephant_%40_kabini.jpg/180px-Elephant_%40_kabini.jpg",
AutomationId = "Image" + i
});
}

Items = collection;
}
}

[Preserve(AllMembers = true)]
public class Model10482
{
public string Text { get; set; }

public string Source { get; set; }

public string AutomationId { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)CollectionViewGroupTypeIssue.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue10482.xaml.cs">
<DependentUpon>Issue10482.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue10110.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue10672.xaml.cs">
<DependentUpon>Issue10672.xaml</DependentUpon>
Expand Down Expand Up @@ -2102,6 +2105,10 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue10482.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue9279.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
Expand Down
29 changes: 13 additions & 16 deletions Xamarin.Forms.Platform.UAP/CollectionView/ItemContentControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Xamarin.Forms.Platform.UWP
{
public class ItemContentControl : ContentControl
{
View _view;
IVisualElementRenderer _renderer;

public ItemContentControl()
Expand All @@ -17,7 +18,7 @@ public ItemContentControl()
}

public static readonly DependencyProperty FormsDataTemplateProperty = DependencyProperty.Register(
nameof(FormsDataTemplate), typeof(DataTemplate), typeof(ItemContentControl),
nameof(FormsDataTemplate), typeof(DataTemplate), typeof(ItemContentControl),
new PropertyMetadata(default(DataTemplate), FormsDataTemplateChanged));

static void FormsDataTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
Expand All @@ -38,7 +39,7 @@ public DataTemplate FormsDataTemplate
}

public static readonly DependencyProperty FormsDataContextProperty = DependencyProperty.Register(
nameof(FormsDataContext), typeof(object), typeof(ItemContentControl),
nameof(FormsDataContext), typeof(object), typeof(ItemContentControl),
new PropertyMetadata(default(object), FormsDataContextChanged));

static void FormsDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
Expand Down Expand Up @@ -103,15 +104,11 @@ protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);

if (oldContent is FrameworkElement oldElement)
{
oldElement.Loaded -= ContentLoaded;
}
if (oldContent != null && _view != null)
_view.MeasureInvalidated -= OnViewMeasureInvalidated;

if (newContent is FrameworkElement newElement)
{
newElement.Loaded += ContentLoaded;
}
if (newContent != null && _view != null)
_view.MeasureInvalidated += OnViewMeasureInvalidated;
}

internal void Realize()
Expand All @@ -132,14 +129,14 @@ internal void Realize()
return;
}

var view = FormsDataTemplate.CreateContent(dataContext, container) as View;
view.BindingContext = dataContext;
_renderer = Platform.CreateRenderer(view);
Platform.SetRenderer(view, _renderer);
_view = FormsDataTemplate.CreateContent(dataContext, container) as View;
_view.BindingContext = dataContext;
_renderer = Platform.CreateRenderer(_view);
Platform.SetRenderer(_view, _renderer);

Content = _renderer.ContainerElement;

itemsView?.AddLogicalChild(view);
itemsView?.AddLogicalChild(_view);
}

internal void UpdateIsSelected(bool isSelected)
Expand All @@ -154,7 +151,7 @@ internal void UpdateIsSelected(bool isSelected)
: VisualStateManager.CommonStates.Normal);
}

void ContentLoaded(object sender, RoutedEventArgs e)
void OnViewMeasureInvalidated(object sender, EventArgs e)
{
InvalidateMeasure();
}
Expand Down
33 changes: 29 additions & 4 deletions Xamarin.Forms.Platform.UAP/CollectionView/ItemsViewRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Xamarin.Forms.Internals;
using UwpApp = Windows.UI.Xaml.Application;
using UwpDataTemplate = Windows.UI.Xaml.DataTemplate;
using UwpScrollBarVisibility = Windows.UI.Xaml.Controls.ScrollBarVisibility;
using UWPApp = Windows.UI.Xaml.Application;
using UWPDataTemplate = Windows.UI.Xaml.DataTemplate;

namespace Xamarin.Forms.Platform.UWP
{
Expand All @@ -27,8 +27,8 @@ public abstract class ItemsViewRenderer<TItemsView> : ViewRenderer<TItemsView, L
internal double _previousVerticalOffset;

protected ListViewBase ListViewBase { get; private set; }
protected UWPDataTemplate ViewTemplate => (UWPDataTemplate)UWPApp.Current.Resources["View"];
protected UWPDataTemplate ItemsViewTemplate => (UWPDataTemplate)UWPApp.Current.Resources["ItemsViewDefaultTemplate"];
protected UwpDataTemplate ViewTemplate => (UwpDataTemplate)UwpApp.Current.Resources["View"];
protected UwpDataTemplate ItemsViewTemplate => (UwpDataTemplate)UwpApp.Current.Resources["ItemsViewDefaultTemplate"];

protected ItemsViewRenderer()
{
Expand Down Expand Up @@ -392,6 +392,31 @@ protected virtual void UpdateEmptyView()
UpdateEmptyViewVisibility();
}

protected virtual void UpdateItemsLayout()
{
if (_scrollViewer != null)
_scrollViewer.ViewChanged -= OnScrollViewChanged;

if (ListViewBase != null)
{
ListViewBase.ItemsSource = null;
ListViewBase = null;
}

ListViewBase = SelectListViewBase();
ListViewBase.IsSynchronizedWithCurrentItem = false;

FindScrollViewer(ListViewBase);

SetNativeControl(ListViewBase);

UpdateItemTemplate();
UpdateItemsSource();
UpdateVerticalScrollBarVisibility();
UpdateHorizontalScrollBarVisibility();
UpdateEmptyView();
}

FrameworkElement RealizeEmptyViewTemplate(object bindingContext, DataTemplate emptyViewTemplate)
{
if (emptyViewTemplate == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE
{
UpdateFooter();
}
else if (changedProperty.Is(StructuredItemsView.ItemsLayoutProperty))
{
UpdateItemsLayout();
}
}

protected override ListViewBase SelectListViewBase()
Expand Down