Implement grouping for UWP CollectionView (#7697)

* Implement grouping for UWP CollectionView

* Clean up CollectionViewSource on teardown
This commit is contained in:
E.Z. Hart 2019-10-09 11:56:18 -06:00 коммит произвёл Shane Neuville
Родитель 724185eac8
Коммит fd9644f3f2
18 изменённых файлов: 601 добавлений и 130 удалений

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

@ -13,7 +13,8 @@
<CollectionView x:Name="CollectionView" Grid.Row="1">
<CollectionView.ItemsLayout>
<GridItemsLayout Span="3" Orientation="Vertical"></GridItemsLayout>
<!--GridItemsLayout Span="3" Orientation="Vertical"></GridItemsLayout>-->
<LinearItemsLayout Orientation="Vertical"></LinearItemsLayout>
</CollectionView.ItemsLayout>
<CollectionView.EmptyView>
No items match your filter.

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

@ -56,7 +56,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.GroupingGa
}
));
Add(new Team("Fantastic Four",
Add(new Team("Fantastic Four",
new List<Member>
{
new Member("The Thing"),
@ -66,7 +66,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.GroupingGa
}
));
Add(new Team("Defenders",
Add(new Team("Defenders",
new List<Member>
{
new Member("Doctor Strange"),
@ -78,8 +78,8 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.GroupingGa
new Member("Yellowjacket"),
}
));
Add(new Team("Heroes for Hire",
Add(new Team("Heroes for Hire",
new List<Member>
{
new Member("Luke Cage"),
@ -90,7 +90,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.GroupingGa
}
));
Add(new Team("West Coast Avengers",
Add(new Team("West Coast Avengers",
new List<Member>
{
new Member("Hawkeye"),
@ -101,7 +101,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.GroupingGa
}
));
Add(new Team("Great Lakes Avengers",
Add(new Team("Great Lakes Avengers",
new List<Member>
{
new Member("Squirrel Girl"),

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

@ -14,7 +14,7 @@ using Xamarin.Forms.Platform.UAP;
namespace Xamarin.Forms.Platform.UWP
{
public class CollectionViewRenderer : SelectableItemsViewRenderer
public class CollectionViewRenderer : GroupableItemsViewRenderer
{
}
}

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

@ -1,60 +1,68 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using UWPApp = Windows.UI.Xaml.Application;
using UWPControlTemplate = Windows.UI.Xaml.Controls.ControlTemplate;
using UWPControls = Windows.UI.Xaml.Controls;
namespace Xamarin.Forms.Platform.UWP
{
internal class FormsGridView : GridView, IEmptyView
{
int _maximumRowsOrColumns;
int _span;
ItemsWrapGrid _wrapGrid;
ContentControl _emptyViewContentControl;
FrameworkElement _emptyView;
Orientation _orientation;
public FormsGridView()
{
Template = (UWPControlTemplate)UWPApp.Current.Resources["FormsListViewTemplate"];
// Using the full style for this control, because for some reason on 16299 we can't set the ControlTemplate
// (it just fails silently saying it can't find the resource key)
DefaultStyleKey = typeof(FormsGridView);
// TODO hartez 2018/06/06 09:52:16 Do we need to clean this up? If so, where?
RegisterPropertyChangedCallback(ItemsPanelProperty, ItemsPanelChanged);
Loaded += OnLoaded;
}
public int MaximumRowsOrColumns
public int Span
{
get => _maximumRowsOrColumns;
get => _span;
set
{
_maximumRowsOrColumns = value;
_span = value;
if (_wrapGrid != null)
{
_wrapGrid.MaximumRowsOrColumns = MaximumRowsOrColumns;
UpdateItemSize();
}
}
}
public static readonly DependencyProperty EmptyViewVisibilityProperty =
DependencyProperty.Register(nameof(EmptyViewVisibility), typeof(Visibility),
typeof(FormsGridView), new PropertyMetadata(Visibility.Collapsed));
public Visibility EmptyViewVisibility
{
get { return (Visibility)GetValue(EmptyViewVisibilityProperty); }
set { SetValue(EmptyViewVisibilityProperty, value); }
}
public static readonly DependencyProperty EmptyViewVisibilityProperty =
DependencyProperty.Register(nameof(EmptyViewVisibility), typeof(Visibility),
typeof(FormsGridView), new PropertyMetadata(Visibility.Collapsed));
// TODO hartez 2018/06/06 10:01:32 Probably should just create a local enum for this?
public void UseHorizontalItemsPanel()
public Orientation Orientation
{
ItemsPanel =
(ItemsPanelTemplate)UWPApp.Current.Resources["HorizontalGridItemsPanel"];
}
public void UseVerticalItemsPanel()
{
ItemsPanel =
(ItemsPanelTemplate)UWPApp.Current.Resources["VerticalGridItemsPanel"];
get => _orientation;
set
{
_orientation = value;
if (_orientation == Orientation.Horizontal)
{
ItemsPanel = (ItemsPanelTemplate)UWPApp.Current.Resources["HorizontalGridItemsPanel"];
ScrollViewer.SetHorizontalScrollMode(this, ScrollMode.Auto);
ScrollViewer.SetHorizontalScrollBarVisibility(this, UWPControls.ScrollBarVisibility.Auto);
}
else
{
ItemsPanel = (ItemsPanelTemplate)UWPApp.Current.Resources["VerticalGridItemsPanel"];
}
}
}
void FindItemsWrapGrid()
@ -66,7 +74,25 @@ namespace Xamarin.Forms.Platform.UWP
return;
}
_wrapGrid.MaximumRowsOrColumns = MaximumRowsOrColumns;
_wrapGrid.SizeChanged -= WrapGridSizeChanged;
_wrapGrid.SizeChanged += WrapGridSizeChanged;
}
void WrapGridSizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateItemSize();
}
void UpdateItemSize()
{
if (_orientation == Orientation.Horizontal)
{
_wrapGrid.ItemHeight = _wrapGrid.ActualHeight / Span;
}
else
{
_wrapGrid.ItemWidth = _wrapGrid.ActualWidth / Span;
}
}
void ItemsPanelChanged(DependencyObject sender, DependencyProperty dp)
@ -100,5 +126,11 @@ namespace Xamarin.Forms.Platform.UWP
_emptyViewContentControl.Content = _emptyView;
}
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
GroupFooterItemTemplateContext.EnsureSelectionDisabled(element, item);
base.PrepareContainerForItemOverride(element, item);
}
}
}
}

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

@ -15,15 +15,16 @@ namespace Xamarin.Forms.Platform.UWP
Template = (UWPControlTemplate)UWPApp.Current.Resources["FormsListViewTemplate"];
}
public static readonly DependencyProperty EmptyViewVisibilityProperty =
DependencyProperty.Register(nameof(EmptyViewVisibility), typeof(Visibility),
typeof(FormsListView), new PropertyMetadata(Visibility.Collapsed));
public Visibility EmptyViewVisibility
{
get { return (Visibility)GetValue(EmptyViewVisibilityProperty); }
set { SetValue(EmptyViewVisibilityProperty, value); }
}
public static readonly DependencyProperty EmptyViewVisibilityProperty =
DependencyProperty.Register(nameof(EmptyViewVisibility), typeof(Visibility), typeof(FormsListView), new PropertyMetadata(Visibility.Collapsed));
public void SetEmptyView(FrameworkElement emptyView)
{
_emptyView = emptyView;
@ -45,5 +46,11 @@ namespace Xamarin.Forms.Platform.UWP
_emptyViewContentControl.Content = _emptyView;
}
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
GroupFooterItemTemplateContext.EnsureSelectionDisabled(element, item);
base.PrepareContainerForItemOverride(element, item);
}
}
}

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

@ -0,0 +1,23 @@
using Windows.UI.Xaml;
namespace Xamarin.Forms.Platform.UWP
{
internal class GroupFooterItemTemplateContext : ItemTemplateContext
{
public GroupFooterItemTemplateContext(DataTemplate formsDataTemplate, object item,
BindableObject container, double? height = null, double? width = null, Thickness? itemSpacing = null)
: base(formsDataTemplate, item, container, height, width, itemSpacing)
{
}
public static void EnsureSelectionDisabled(DependencyObject element, object item)
{
if (item is GroupFooterItemTemplateContext)
{
// Prevent the group footer from being selectable
(element as FrameworkElement).IsHitTestVisible = false;
}
}
}
}

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

@ -0,0 +1,17 @@
using Windows.UI.Xaml.Controls;
using UWPApp = Windows.UI.Xaml.Application;
using UWPDataTemplate = Windows.UI.Xaml.DataTemplate;
namespace Xamarin.Forms.Platform.UWP
{
internal class GroupHeaderStyleSelector : GroupStyleSelector
{
protected override GroupStyle SelectGroupStyleCore(object group, uint level)
{
return new GroupStyle
{
HeaderTemplate = (UWPDataTemplate)UWPApp.Current.Resources["GroupHeaderTemplate"]
};
}
}
}

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

@ -0,0 +1,50 @@
using System.Collections;
using System.Collections.Generic;
namespace Xamarin.Forms.Platform.UWP
{
internal class GroupTemplateContext
{
public ItemTemplateContext HeaderItemTemplateContext { get; }
public ItemTemplateContext FooterItemTemplateContext { get; }
public object Items { get; }
public GroupTemplateContext(ItemTemplateContext headerItemTemplateContext,
ItemTemplateContext footerItemTemplateContext, object items)
{
HeaderItemTemplateContext = headerItemTemplateContext;
FooterItemTemplateContext = footerItemTemplateContext;
if (footerItemTemplateContext == null)
{
Items = items;
}
else
{
// UWP ListViewBase does not support group footers. So we're going to fake the footer by adding an
// extra item to the ItemsSource so the footer shows up at the end of the group.
if (items is IList itemsList)
{
// If it's already an IList, we want to make sure to keep it that way
itemsList.Add(footerItemTemplateContext);
Items = itemsList;
return;
}
// If the group items are not an IList, then we'll have to append the footer the hard way
var listPlusFooter = new List<object>();
foreach (var item in (items as IEnumerable))
{
listPlusFooter.Add(item);
}
listPlusFooter.Add(footerItemTemplateContext);
Items = listPlusFooter;
}
}
}
}

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

@ -0,0 +1,62 @@
using System.ComponentModel;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
namespace Xamarin.Forms.Platform.UWP
{
public class GroupableItemsViewRenderer : SelectableItemsViewRenderer
{
GroupableItemsView _groupableItemsView;
protected override void SetUpNewElement(ItemsView newElement)
{
_groupableItemsView = Element as GroupableItemsView;
base.SetUpNewElement(newElement);
}
protected override void TearDownOldElement(ItemsView oldElement)
{
base.TearDownOldElement(oldElement);
_groupableItemsView = null;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs changedProperty)
{
base.OnElementPropertyChanged(sender, changedProperty);
if (changedProperty.IsOneOf(GroupableItemsView.IsGroupedProperty,
GroupableItemsView.GroupFooterTemplateProperty, GroupableItemsView.GroupHeaderTemplateProperty))
{
UpdateItemsSource();
}
}
protected override CollectionViewSource CreateCollectionViewSource()
{
if (_groupableItemsView != null && _groupableItemsView.IsGrouped)
{
var itemTemplate = Element.ItemTemplate;
var itemsSource = Element.ItemsSource;
return new CollectionViewSource
{
Source = TemplatedItemSourceFactory.CreateGrouped(itemsSource, itemTemplate,
_groupableItemsView.GroupHeaderTemplate, _groupableItemsView.GroupFooterTemplate, Element),
IsSourceGrouped = true,
ItemsPath = new Windows.UI.Xaml.PropertyPath(nameof(GroupTemplateContext.Items))
};
}
else
{
return base.CreateCollectionViewSource();
}
}
protected override void UpdateItemTemplate()
{
base.UpdateItemTemplate();
ListViewBase.GroupStyleSelector = new GroupHeaderStyleSelector();
}
}
}

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

@ -0,0 +1,166 @@
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace Xamarin.Forms.Platform.UWP
{
internal class GroupedItemTemplateCollection : ObservableCollection<GroupTemplateContext>
{
readonly IEnumerable _itemsSource;
readonly DataTemplate _itemTemplate;
readonly DataTemplate _groupHeaderTemplate;
readonly DataTemplate _groupFooterTemplate;
readonly BindableObject _container;
readonly IList _groupList;
public GroupedItemTemplateCollection(IEnumerable itemsSource, DataTemplate itemTemplate,
DataTemplate groupHeaderTemplate, DataTemplate groupFooterTemplate, BindableObject container)
{
_itemsSource = itemsSource;
_itemTemplate = itemTemplate;
_groupHeaderTemplate = groupHeaderTemplate;
_groupFooterTemplate = groupFooterTemplate;
_container = container;
foreach (var group in _itemsSource)
{
var groupTemplateContext = CreateGroupTemplateContext(group);
Add(groupTemplateContext);
}
if (_itemsSource is IList groupList && _itemsSource is INotifyCollectionChanged incc)
{
_groupList = groupList;
incc.CollectionChanged += GroupsChanged;
}
}
GroupTemplateContext CreateGroupTemplateContext(object group)
{
var groupHeaderTemplateContext = _groupHeaderTemplate != null
? new ItemTemplateContext(_groupHeaderTemplate, group, _container) : null;
var groupFooterTemplateContext = _groupFooterTemplate != null
? new GroupFooterItemTemplateContext(_groupFooterTemplate, group, _container) : null;
// This is where we'll eventually look at GroupItemPropertyName
var groupItemsList = TemplatedItemSourceFactory.Create(group as IEnumerable, _itemTemplate, _container);
return new GroupTemplateContext(groupHeaderTemplateContext, groupFooterTemplateContext, groupItemsList);
}
void GroupsChanged(object sender, NotifyCollectionChangedEventArgs args)
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
Add(args);
break;
case NotifyCollectionChangedAction.Move:
Move(args);
break;
case NotifyCollectionChangedAction.Remove:
Remove(args);
break;
case NotifyCollectionChangedAction.Replace:
Replace(args);
break;
case NotifyCollectionChangedAction.Reset:
Reset();
break;
}
}
void Add(NotifyCollectionChangedEventArgs args)
{
var startIndex = args.NewStartingIndex > -1 ? args.NewStartingIndex : _groupList.IndexOf(args.NewItems[0]);
var count = args.NewItems.Count;
for (int n = 0; n < count; n++)
{
Insert(startIndex, CreateGroupTemplateContext(args.NewItems[n]));
}
}
void Move(NotifyCollectionChangedEventArgs args)
{
var count = args.NewItems.Count;
if (args.OldStartingIndex > args.NewStartingIndex)
{
for (int n = 0; n < count; n++)
{
Move(args.OldStartingIndex + n, args.NewStartingIndex + n);
}
return;
}
for (int n = count - 1; n >= 0; n--)
{
Move(args.OldStartingIndex + n, args.NewStartingIndex + n);
}
}
void Remove(NotifyCollectionChangedEventArgs args)
{
var startIndex = args.OldStartingIndex;
if (startIndex < 0)
{
// INCC implementation isn't giving us enough information to know where the removed items were in the
// collection. So the best we can do is a full Reset.
Reset();
return;
}
var count = args.OldItems.Count;
for (int n = startIndex + count - 1; n >= startIndex; n--)
{
RemoveAt(n);
}
}
void Replace(NotifyCollectionChangedEventArgs args)
{
var newItemCount = args.NewItems.Count;
if (newItemCount == args.OldItems.Count)
{
for (int n = 0; n < newItemCount; n++)
{
var index = args.OldStartingIndex + n;
var oldItem = this[index];
var newItem = CreateGroupTemplateContext(args.NewItems[0]);
Items[index] = newItem;
var update = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, index);
OnCollectionChanged(update);
}
}
else
{
// If we're replacing one set with an equal size set, we can do a soft reset; if not, we have to completely
// rebuild the collection
Reset();
}
}
void Reset()
{
Items.Clear();
_groupList.Clear();
foreach (var group in _itemsSource)
{
var groupTemplateContext = CreateGroupTemplateContext(group);
_groupList.Add(group);
Items.Add(groupTemplateContext);
}
var reset = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
OnCollectionChanged(reset);
}
}
}

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

@ -1,7 +1,9 @@
using Windows.UI.Xaml;
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Xamarin.Forms.Internals;
using WThickness = Windows.UI.Xaml.Thickness;
using WSize = Windows.Foundation.Size;
namespace Xamarin.Forms.Platform.UWP
{
@ -148,39 +150,54 @@ namespace Xamarin.Forms.Platform.UWP
InvalidateMeasure();
}
protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize)
protected override WSize MeasureOverride(WSize availableSize)
{
if (_renderer == null)
{
return base.MeasureOverride(availableSize);
}
var frameworkElement = Content as FrameworkElement;
var formsElement = _renderer.Element;
if (ItemHeight != default || ItemWidth != default)
{
formsElement.Layout(new Rectangle(0, 0, ItemWidth, ItemHeight));
var wsize = new Windows.Foundation.Size(ItemWidth, ItemHeight);
var wsize = new WSize(ItemWidth, ItemHeight);
(Content as FrameworkElement).Margin = new WThickness(ItemSpacing.Left, ItemSpacing.Top, ItemSpacing.Right, ItemSpacing.Bottom);
frameworkElement.Margin = new WThickness(ItemSpacing.Left, ItemSpacing.Top, ItemSpacing.Right, ItemSpacing.Bottom);
(Content as FrameworkElement).Measure(wsize);
frameworkElement.Measure(wsize);
return base.MeasureOverride(wsize);
}
else
{
Size request = formsElement.Measure(availableSize.Width, availableSize.Height,
MeasureFlags.IncludeMargins).Request;
var (width, height) = formsElement.Measure(availableSize.Width, availableSize.Height,
MeasureFlags.IncludeMargins).Request;
formsElement.Layout(new Rectangle(0, 0, request.Width, request.Height));
width = Max(width, availableSize.Width);
height = Max(height, availableSize.Height);
var wsize = new Windows.Foundation.Size(request.Width, request.Height);
formsElement.Layout(new Rectangle(0, 0, width, height));
(Content as FrameworkElement).Measure(wsize);
var wsize = new WSize(width, height);
frameworkElement.Measure(wsize);
return base.MeasureOverride(wsize);
}
}
double Max(double requested, double available)
{
return Math.Max(requested, ClampInfinity(available));
}
double ClampInfinity(double value)
{
return double.IsInfinity(value) ? 0 : value;
}
}
}

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

@ -2,6 +2,13 @@
{
internal class ItemTemplateContext
{
public DataTemplate FormsDataTemplate { get; }
public object Item { get; }
public BindableObject Container { get; }
public double ItemHeight { get; }
public double ItemWidth { get; }
public Thickness ItemSpacing { get; }
public ItemTemplateContext(DataTemplate formsDataTemplate, object item, BindableObject container,
double? height = null, double? width = null, Thickness? itemSpacing = null)
{
@ -18,12 +25,5 @@
if (itemSpacing.HasValue)
ItemSpacing = itemSpacing.Value;
}
public DataTemplate FormsDataTemplate { get; }
public object Item { get; }
public BindableObject Container { get; }
public double ItemHeight { get; }
public double ItemWidth { get; }
public Thickness ItemSpacing { get; }
}
}

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

@ -67,7 +67,7 @@ namespace Xamarin.Forms.Platform.UWP
internal class ItemTemplateContextListEnumerator : IEnumerator<ItemTemplateContext>
{
public ItemTemplateContext Current { get; private set; }
object IEnumerator.Current { get; }
object IEnumerator.Current => Current;
int _currentIndex = -1;
private ItemTemplateContextList _itemTemplateContextList;

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

@ -1,16 +1,9 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.ComponentModel;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Platform.UAP;
using UwpScrollBarVisibility = Windows.UI.Xaml.Controls.ScrollBarVisibility;
using UWPApp = Windows.UI.Xaml.Application;
using UWPDataTemplate = Windows.UI.Xaml.DataTemplate;
@ -78,7 +71,6 @@ namespace Xamarin.Forms.Platform.UWP
return;
}
// TODO hartez 2018-05-22 12:59 PM Handle grouping
CleanUpCollectionViewSource();
@ -109,7 +101,7 @@ namespace Xamarin.Forms.Platform.UWP
}
}
if (Element.ItemsSource == null)
if (Element?.ItemsSource == null)
{
if (CollectionViewSource?.Source is INotifyCollectionChanged incc)
{
@ -213,16 +205,15 @@ namespace Xamarin.Forms.Platform.UWP
// Stop listening for ScrollTo requests
oldElement.ScrollToRequested -= ScrollToRequested;
if (CollectionViewSource != null)
{
CleanUpCollectionViewSource();
}
if (ListViewBase != null)
{
ListViewBase.ItemsSource = null;
}
if (CollectionViewSource != null)
{
CollectionViewSource.Source = null;
}
}
void UpdateVerticalScrollBarVisibility()

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

@ -2,10 +2,10 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Xamarin.Forms.Platform.UWP">
<ItemsPanelTemplate x:Key="HorizontalListItemsPanel">
<ItemsStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="HorizontalListItemsPanel">
<ItemsStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="HorizontalGridItemsPanel">
<!-- Yes, this is counterintuitive. Orientation here means "direction we lay out the items until we hit the
@ -27,6 +27,14 @@
</local:ItemContentControl>
</DataTemplate>
<DataTemplate x:Key="GroupHeaderTemplate">
<local:ItemContentControl
x:Name="ItemContentControl" DataContext="{Binding HeaderItemTemplateContext}"
FormsDataTemplate="{Binding FormsDataTemplate}" FormsDataContext="{Binding Item}"
FormsContainer="{Binding Container}">
</local:ItemContentControl>
</DataTemplate>
<DataTemplate x:Key="CarouselItemsViewDefaultTemplate">
<local:ItemContentControl
x:Name="ItemContentControl"
@ -79,35 +87,77 @@
</Border>
</ControlTemplate>
<ControlTemplate x:Key="FormsGridViewTemplate" TargetType="local:FormsGridView">
<Border BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentControl x:Name="EmptyViewContentControl" Visibility="{TemplateBinding EmptyViewVisibility}"></ContentControl>
<ScrollViewer x:Name="ScrollViewer"
TabNavigation="{TemplateBinding TabNavigation}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
AutomationProperties.AccessibilityView="Raw">
<ItemsPresenter Header="{TemplateBinding Header}"
HeaderTemplate="{TemplateBinding HeaderTemplate}"
HeaderTransitions="{TemplateBinding HeaderTransitions}"
Footer="{TemplateBinding Footer}"
FooterTemplate="{TemplateBinding FooterTemplate}"
FooterTransitions="{TemplateBinding FooterTransitions}"
Padding="{TemplateBinding Padding}" />
</ScrollViewer>
</Grid>
</Border>
</ControlTemplate>
<!-- We much rather just use a ControlTemplate for this the way we do with FormsListView, but unfortunately
16299 (and presumably lower) can't find and set the control template. So we use the entire style instead. If we
get to a point where we don't have to support these earlier versions, we can replace this style with just the
template. (See also FormsGridView.cs) -->
<Style TargetType="local:FormsGridView">
<Setter Property="Padding" Value="0,0,0,10" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="TabNavigation" Value="Once" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
<Setter Property="ScrollViewer.IsHorizontalRailEnabled" Value="False" />
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Enabled" />
<Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="True" />
<Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
<Setter Property="ScrollViewer.BringIntoViewOnFocusChange" Value="True" />
<Setter Property="IsSwipeEnabled" Value="True" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="FocusVisualMargin" Value="-2" />
<Setter Property="ItemContainerTransitions">
<Setter.Value>
<TransitionCollection>
<AddDeleteThemeTransition />
<ContentThemeTransition />
<ReorderThemeTransition />
<EntranceThemeTransition IsStaggeringEnabled="False" />
</TransitionCollection>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:FormsGridView">
<Border BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentControl x:Name="EmptyViewContentControl" Visibility="{TemplateBinding EmptyViewVisibility}"></ContentControl>
<ScrollViewer x:Name="ScrollViewer"
TabNavigation="{TemplateBinding TabNavigation}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
AutomationProperties.AccessibilityView="Raw">
<ItemsPresenter Header="{TemplateBinding Header}"
HeaderTemplate="{TemplateBinding HeaderTemplate}"
HeaderTransitions="{TemplateBinding HeaderTransitions}"
Footer="{TemplateBinding Footer}"
FooterTemplate="{TemplateBinding FooterTemplate}"
FooterTransitions="{TemplateBinding FooterTransitions}"
Padding="{TemplateBinding Padding}" />
</ScrollViewer>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="HorizontalCarouselListStyle" TargetType="local:FormsListView">
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
@ -142,5 +192,61 @@
</Setter>
</Style>
<!-- Custom version of the style which removes the horizontal rule below the header -->
<Style TargetType="ListViewHeaderItem">
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{ThemeResource ListViewHeaderItemThemeFontSize}" />
<Setter Property="Background" Value="{ThemeResource ListViewHeaderItemBackground}" />
<Setter Property="Margin" Value="0,0,0,4" />
<Setter Property="Padding" Value="12,8,12,0" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="MinHeight" Value="{ThemeResource ListViewHeaderItemMinHeight}" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewHeaderItem">
<StackPanel Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter x:Name="ContentPresenter"
Margin="{TemplateBinding Padding}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Custom version of the style which removes the horizontal rule below the header -->
<Style TargetType="GridViewHeaderItem">
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{ThemeResource GridViewHeaderItemThemeFontSize}" />
<Setter Property="Background" Value="{ThemeResource GridViewHeaderItemBackground}" />
<Setter Property="Margin" Value="0,0,0,4" />
<Setter Property="Padding" Value="12,8,12,0" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="MinHeight" Value="{ThemeResource GridViewHeaderItemMinHeight}" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewHeaderItem">
<StackPanel Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter x:Name="ContentPresenter"
Margin="{TemplateBinding Padding}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

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

@ -1,6 +1,6 @@
using System.ComponentModel;
using Windows.UI.Xaml.Controls;
using Xamarin.Forms.Platform.UAP;
using UWPApp = Windows.UI.Xaml.Application;
namespace Xamarin.Forms.Platform.UWP
{
@ -160,42 +160,29 @@ namespace Xamarin.Forms.Platform.UWP
{
if (ListViewBase is FormsGridView formsGridView)
{
formsGridView.MaximumRowsOrColumns = ((GridItemsLayout)Layout).Span;
formsGridView.Span = ((GridItemsLayout)Layout).Span;
}
}
}
static ListViewBase CreateGridView(GridItemsLayout gridItemsLayout)
{
var gridView = new FormsGridView();
if (gridItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal)
return new FormsGridView
{
gridView.UseHorizontalItemsPanel();
Orientation = gridItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal
? Orientation.Horizontal
: Orientation.Vertical,
// TODO hartez 2018/06/06 12:13:38 Should this logic just be built into FormsGridView?
ScrollViewer.SetHorizontalScrollMode(gridView, ScrollMode.Auto);
ScrollViewer.SetHorizontalScrollBarVisibility(gridView,
Windows.UI.Xaml.Controls.ScrollBarVisibility.Auto);
}
else
{
gridView.UseVerticalItemsPanel();
}
gridView.MaximumRowsOrColumns = gridItemsLayout.Span;
return gridView;
Span = gridItemsLayout.Span
};
}
static ListViewBase CreateHorizontalListView()
{
// TODO hartez 2018/06/05 16:18:57 Is there any performance benefit to caching the ItemsPanelTemplate lookup?
// TODO hartez 2018/05/29 15:38:04 Make sure the ItemsViewStyles.xaml xbf gets into the nuspec
var horizontalListView = new Windows.UI.Xaml.Controls.ListView()
{
ItemsPanel =
(ItemsPanelTemplate)Windows.UI.Xaml.Application.Current.Resources["HorizontalListItemsPanel"]
(ItemsPanelTemplate)UWPApp.Current.Resources["HorizontalListItemsPanel"]
};
ScrollViewer.SetHorizontalScrollMode(horizontalListView, ScrollMode.Auto);

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

@ -5,7 +5,8 @@ namespace Xamarin.Forms.Platform.UWP
{
internal static class TemplatedItemSourceFactory
{
internal static object Create(IEnumerable itemsSource, DataTemplate itemTemplate, BindableObject container, double? itemHeight = null, double? itemWidth = null, Thickness? itemSpacing = null)
internal static object Create(IEnumerable itemsSource, DataTemplate itemTemplate, BindableObject container,
double? itemHeight = null, double? itemWidth = null, Thickness? itemSpacing = null)
{
switch (itemsSource)
{
@ -17,5 +18,11 @@ namespace Xamarin.Forms.Platform.UWP
return new ItemTemplateContextEnumerable(itemsSource, itemTemplate, container, itemHeight, itemWidth, itemSpacing);
}
internal static object CreateGrouped(IEnumerable itemsSource, DataTemplate itemTemplate,
DataTemplate groupHeaderTemplate, DataTemplate groupFooterTemplate, BindableObject container)
{
return new GroupedItemTemplateCollection(itemsSource, itemTemplate, groupHeaderTemplate, groupFooterTemplate, container);
}
}
}

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

@ -13,7 +13,7 @@
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion>10.0.16299.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.16299.0</TargetPlatformMinVersion>
<SkipMicrosoftUIXamlCheckTargetPlatformVersion>true</SkipMicrosoftUIXamlCheckTargetPlatformVersion>
<SkipMicrosoftUIXamlCheckTargetPlatformVersion>true</SkipMicrosoftUIXamlCheckTargetPlatformVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
@ -47,6 +47,11 @@
<Compile Include="CollectionView\ItemsViewRenderer.cs" />
<Compile Include="CollectionView\ItemTemplateContextList.cs" />
<Compile Include="CollectionView\ScrollHelpers.cs" />
<Compile Include="CollectionView\GroupedItemTemplateCollection.cs" />
<Compile Include="CollectionView\GroupFooterItemTemplateContext.cs" />
<Compile Include="CollectionView\GroupHeaderStyleSelector.cs" />
<Compile Include="CollectionView\GroupTemplateContext.cs" />
<Compile Include="CollectionView\GroupableItemsViewRenderer.cs" />
<Compile Include="CollectionView\SelectableItemsViewRenderer.cs" />
<Compile Include="CollectionView\StructuredItemsViewRenderer.cs" />
<Compile Include="ColorExtensions.cs" />
@ -213,6 +218,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="CollectionView\ItemsViewStyles.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="FormsCheckBoxStyle.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@ -234,10 +243,6 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="CollectionView\ItemsViewStyles.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="MasterDetailControlStyle.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>