Implement grouping for UWP CollectionView (#7697)
authorE.Z. Hart <hartez@users.noreply.github.com>
Wed, 9 Oct 2019 17:56:18 +0000 (11:56 -0600)
committerShane Neuville <shneuvil@microsoft.com>
Wed, 9 Oct 2019 17:56:18 +0000 (11:56 -0600)
* Implement grouping for UWP CollectionView

* Clean up CollectionViewSource on teardown

18 files changed:
Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/EmptyViewGalleries/EmptyViewStringGallery.xaml
Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/GroupingGalleries/ViewModel.cs
Xamarin.Forms.Platform.UAP/CollectionView/CollectionViewRenderer.cs
Xamarin.Forms.Platform.UAP/CollectionView/FormsGridView.cs
Xamarin.Forms.Platform.UAP/CollectionView/FormsListView.cs
Xamarin.Forms.Platform.UAP/CollectionView/GroupFooterItemTemplateContext.cs [new file with mode: 0644]
Xamarin.Forms.Platform.UAP/CollectionView/GroupHeaderStyleSelector.cs [new file with mode: 0644]
Xamarin.Forms.Platform.UAP/CollectionView/GroupTemplateContext.cs [new file with mode: 0644]
Xamarin.Forms.Platform.UAP/CollectionView/GroupableItemsViewRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.UAP/CollectionView/GroupedItemTemplateCollection.cs [new file with mode: 0644]
Xamarin.Forms.Platform.UAP/CollectionView/ItemContentControl.cs
Xamarin.Forms.Platform.UAP/CollectionView/ItemTemplateContext.cs
Xamarin.Forms.Platform.UAP/CollectionView/ItemTemplateContextList.cs
Xamarin.Forms.Platform.UAP/CollectionView/ItemsViewRenderer.cs
Xamarin.Forms.Platform.UAP/CollectionView/ItemsViewStyles.xaml
Xamarin.Forms.Platform.UAP/CollectionView/StructuredItemsViewRenderer.cs
Xamarin.Forms.Platform.UAP/CollectionView/TemplatedItemSourceFactory.cs
Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj

index b851a41..143b6c6 100644 (file)
@@ -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.
index 59b2184..c835e75 100644 (file)
@@ -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"),
index be2899b..544ded3 100644 (file)
@@ -14,7 +14,7 @@ using Xamarin.Forms.Platform.UAP;
 
 namespace Xamarin.Forms.Platform.UWP
 {
-       public class CollectionViewRenderer : SelectableItemsViewRenderer
+       public class CollectionViewRenderer : GroupableItemsViewRenderer
        {
        }
 }
\ No newline at end of file
index 7621375..5532099 100644 (file)
@@ -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 Visibility EmptyViewVisibility
-               {
-                       get { return (Visibility)GetValue(EmptyViewVisibilityProperty); }
-                       set { SetValue(EmptyViewVisibilityProperty, value); }
-               }
-
                public static readonly DependencyProperty EmptyViewVisibilityProperty =
-                       DependencyProperty.Register(nameof(EmptyViewVisibility), typeof(Visibility), 
+                       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 Visibility EmptyViewVisibility
                {
-                       ItemsPanel =
-                               (ItemsPanelTemplate)UWPApp.Current.Resources["HorizontalGridItemsPanel"];
+                       get { return (Visibility)GetValue(EmptyViewVisibilityProperty); }
+                       set { SetValue(EmptyViewVisibilityProperty, value); }
                }
 
-               public void UseVerticalItemsPanel()
+               public Orientation Orientation
                {
-                       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);
+               }
        }
-}
\ No newline at end of file
+}
index 95a8e97..cf36d40 100644 (file)
@@ -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);
+               }
        }
 }
diff --git a/Xamarin.Forms.Platform.UAP/CollectionView/GroupFooterItemTemplateContext.cs b/Xamarin.Forms.Platform.UAP/CollectionView/GroupFooterItemTemplateContext.cs
new file mode 100644 (file)
index 0000000..c5b2382
--- /dev/null
@@ -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;
+                       }
+
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.UAP/CollectionView/GroupHeaderStyleSelector.cs b/Xamarin.Forms.Platform.UAP/CollectionView/GroupHeaderStyleSelector.cs
new file mode 100644 (file)
index 0000000..f25f01d
--- /dev/null
@@ -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"]
+                       };
+               }
+       }
+}
diff --git a/Xamarin.Forms.Platform.UAP/CollectionView/GroupTemplateContext.cs b/Xamarin.Forms.Platform.UAP/CollectionView/GroupTemplateContext.cs
new file mode 100644 (file)
index 0000000..3120314
--- /dev/null
@@ -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;
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.UAP/CollectionView/GroupableItemsViewRenderer.cs b/Xamarin.Forms.Platform.UAP/CollectionView/GroupableItemsViewRenderer.cs
new file mode 100644 (file)
index 0000000..f44a582
--- /dev/null
@@ -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();
+               }
+       }
+}
diff --git a/Xamarin.Forms.Platform.UAP/CollectionView/GroupedItemTemplateCollection.cs b/Xamarin.Forms.Platform.UAP/CollectionView/GroupedItemTemplateCollection.cs
new file mode 100644 (file)
index 0000000..59ae036
--- /dev/null
@@ -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);
+               }
+       }
+}
\ No newline at end of file
index 6533dd7..1a03930 100644 (file)
@@ -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;
+
+                               width = Max(width, availableSize.Width);
+                               height = Max(height, availableSize.Height);
 
-                               formsElement.Layout(new Rectangle(0, 0, request.Width, request.Height));
+                               formsElement.Layout(new Rectangle(0, 0, width, height));
 
-                               var wsize = new Windows.Foundation.Size(request.Width, request.Height);
+                               var wsize = new WSize(width, height);
 
-                               (Content as FrameworkElement).Measure(wsize);
+                               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;
+               }
        }
 }
\ No newline at end of file
index 255e64b..925d69c 100644 (file)
@@ -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)
                {
                        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; }
        }
 }
\ No newline at end of file
index 760b9c8..f745ebf 100644 (file)
@@ -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;
 
index 91977b7..ff9c83f 100644 (file)
@@ -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 (ListViewBase != null)
+                       if (CollectionViewSource != null)
                        {
-                               ListViewBase.ItemsSource = null;
+                               CleanUpCollectionViewSource();
                        }
 
-                       if (CollectionViewSource != null)
+                       if (ListViewBase != null)
                        {
-                               CollectionViewSource.Source = null;
+                               ListViewBase.ItemsSource = null;
                        }
-
                }
 
                void UpdateVerticalScrollBarVisibility()
index ebfe707..7c3bd31 100644 (file)
@@ -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 
         </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" 
         </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" />
         </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>
 
index 8ede34c..8dfed02 100644 (file)
@@ -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);
index 9009fc1..090bc6b 100644 (file)
@@ -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);
+               }
        }
 }
\ No newline at end of file
index ceb5d53..0e16d97 100644 (file)
@@ -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>
     <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" />
       <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>
       <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>