Implement EmptyView on UWP (#7438)
authorE.Z. Hart <hartez@users.noreply.github.com>
Mon, 16 Sep 2019 13:02:58 +0000 (07:02 -0600)
committerGerald Versluis <gerald.versluis@microsoft.com>
Mon, 16 Sep 2019 13:02:58 +0000 (15:02 +0200)
Xamarin.Forms.Platform.UAP/CollectionView/FormsGridView.cs
Xamarin.Forms.Platform.UAP/CollectionView/FormsListView.cs [new file with mode: 0644]
Xamarin.Forms.Platform.UAP/CollectionView/IEmptyView.cs [new file with mode: 0644]
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/Xamarin.Forms.Platform.UAP.csproj

index 694447a..68781df 100644 (file)
@@ -1,14 +1,17 @@
-using Windows.UI.Xaml;
+using System;
+using System.Globalization;
+using Windows.UI.Xaml;
 using Windows.UI.Xaml.Controls;
 using Xamarin.Forms.Platform.UWP;
 
-namespace Xamarin.Forms.Platform.UAP
+namespace Xamarin.Forms.Platform.UWP
 {
-       // TODO hartez 2018/06/06 10:01:48 Consider whether this should be internal; it might be that we just want to make the ItemsPanel resources configurable in CollectionViewRenderer
-       internal class FormsGridView : GridView
+       internal class FormsGridView : GridView, IEmptyView
        {
                int _maximumRowsOrColumns;
                ItemsWrapGrid _wrapGrid;
+               ContentControl _emptyViewContentControl;
+               FrameworkElement _emptyView;
 
                public FormsGridView()
                {
@@ -30,6 +33,16 @@ namespace Xamarin.Forms.Platform.UAP
                        }
                }
 
+               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()
                {
@@ -37,7 +50,7 @@ namespace Xamarin.Forms.Platform.UAP
                                (ItemsPanelTemplate)Windows.UI.Xaml.Application.Current.Resources["HorizontalGridItemsPanel"];
                }
 
-               public void UseVerticalalItemsPanel()
+               public void UseVerticalItemsPanel()
                {
                        ItemsPanel =
                                (ItemsPanelTemplate)Windows.UI.Xaml.Application.Current.Resources["VerticalGridItemsPanel"];
@@ -64,5 +77,27 @@ namespace Xamarin.Forms.Platform.UAP
                {
                        FindItemsWrapGrid();
                }
+
+               public void SetEmptyView(FrameworkElement emptyView)
+               {
+                       _emptyView = emptyView;
+
+                       if (_emptyViewContentControl != null)
+                       {
+                               _emptyViewContentControl.Content = emptyView;
+                       }
+               }
+
+               protected override void OnApplyTemplate()
+               {
+                       base.OnApplyTemplate();
+
+                       _emptyViewContentControl = GetTemplateChild("EmptyViewContentControl") as ContentControl;
+
+                       if (_emptyView != null)
+                       {
+                               _emptyViewContentControl.Content = _emptyView;
+                       }
+               }
        }
 }
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.UAP/CollectionView/FormsListView.cs b/Xamarin.Forms.Platform.UAP/CollectionView/FormsListView.cs
new file mode 100644 (file)
index 0000000..532a135
--- /dev/null
@@ -0,0 +1,42 @@
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+namespace Xamarin.Forms.Platform.UWP
+{
+       internal class FormsListView : Windows.UI.Xaml.Controls.ListView, IEmptyView
+       {
+               ContentControl _emptyViewContentControl;
+               FrameworkElement _emptyView;
+
+               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;
+
+                       if (_emptyViewContentControl != null)
+                       {
+                               _emptyViewContentControl.Content = emptyView;
+                       }
+               }
+
+               protected override void OnApplyTemplate()
+               {
+                       base.OnApplyTemplate();
+
+                       _emptyViewContentControl = GetTemplateChild("EmptyViewContentControl") as ContentControl;
+
+                       if (_emptyView != null)
+                       {
+                               _emptyViewContentControl.Content = _emptyView;
+                       }
+               }
+       }
+}
diff --git a/Xamarin.Forms.Platform.UAP/CollectionView/IEmptyView.cs b/Xamarin.Forms.Platform.UAP/CollectionView/IEmptyView.cs
new file mode 100644 (file)
index 0000000..9fb4894
--- /dev/null
@@ -0,0 +1,10 @@
+using Windows.UI.Xaml;
+
+namespace Xamarin.Forms.Platform.UWP
+{
+       internal interface IEmptyView
+       {
+               Visibility EmptyViewVisibility { get; set; }
+               void SetEmptyView(FrameworkElement emptyView);
+       }
+}
\ No newline at end of file
index 2477e21..14a6745 100644 (file)
@@ -14,6 +14,7 @@ using Xamarin.Forms.Platform.UAP;
 using UwpScrollBarVisibility = Windows.UI.Xaml.Controls.ScrollBarVisibility;
 using UWPApp = Windows.UI.Xaml.Application;
 using UWPDataTemplate = Windows.UI.Xaml.DataTemplate;
+using System.Collections.Specialized;
 
 namespace Xamarin.Forms.Platform.UWP
 {
@@ -28,6 +29,9 @@ namespace Xamarin.Forms.Platform.UWP
                protected UWPDataTemplate ViewTemplate => (UWPDataTemplate)UWPApp.Current.Resources["View"];
                protected UWPDataTemplate ItemsViewTemplate => (UWPDataTemplate)UWPApp.Current.Resources["ItemsViewDefaultTemplate"];
 
+               FrameworkElement _emptyView;
+               View _formsEmptyView;
+
                protected ItemsControl ItemsControl { get; private set; }
 
                protected override void OnElementChanged(ElementChangedEventArgs<ItemsView> args)
@@ -57,6 +61,10 @@ namespace Xamarin.Forms.Platform.UWP
                        {
                                UpdateVerticalScrollBarVisibility();
                        }
+                       else if (changedProperty.IsOneOf(ItemsView.EmptyViewProperty, ItemsView.EmptyViewTemplateProperty))
+                       {
+                               UpdateEmptyView();
+                       }
                }
 
                protected abstract ListViewBase SelectListViewBase();
@@ -76,6 +84,11 @@ namespace Xamarin.Forms.Platform.UWP
 
                        if (itemsSource == null)
                        {
+                               if (_collectionViewSource?.Source is INotifyCollectionChanged incc)
+                               {
+                                       incc.CollectionChanged -= ItemsChanged;
+                               }
+
                                _collectionViewSource = null;
                                return;
                        }
@@ -97,6 +110,11 @@ namespace Xamarin.Forms.Platform.UWP
                                        Source = TemplatedItemSourceFactory.Create(itemsSource, itemTemplate, Element),
                                        IsSourceGrouped = false
                                };
+
+                               if (_collectionViewSource?.Source is INotifyCollectionChanged incc)
+                               {
+                                       incc.CollectionChanged += ItemsChanged;
+                               }
                        }
                        else
                        {
@@ -108,6 +126,13 @@ namespace Xamarin.Forms.Platform.UWP
                        }
                        
                        ListViewBase.ItemsSource = _collectionViewSource.View;
+
+                       UpdateEmptyViewVisibility();
+               }
+
+               void ItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       UpdateEmptyViewVisibility();
                }
 
                protected virtual void UpdateItemTemplate()
@@ -148,6 +173,7 @@ namespace Xamarin.Forms.Platform.UWP
                        UpdateItemsSource();
                        UpdateVerticalScrollBarVisibility();
                        UpdateHorizontalScrollBarVisibility();
+                       UpdateEmptyView();
 
                        // Listen for ScrollTo requests
                        newElement.ScrollToRequested += ScrollToRequested;
@@ -357,5 +383,75 @@ namespace Xamarin.Forms.Platform.UWP
                                await JumpTo(list, targetItem, args.ScrollToPosition);
                        }
                }
+
+               protected virtual void UpdateEmptyView()
+               {
+                       if (Element == null || ListViewBase == null)
+                       {
+                               return;
+                       }
+
+                       var emptyView = Element.EmptyView;
+
+                       if (emptyView == null)
+                       {
+                               return;
+                       }
+
+                       switch (emptyView)
+                       {
+                               case string text:
+                                       _emptyView = new TextBlock { Text = text };
+                                       break;
+                               case View view:
+                                       _emptyView = RealizeEmptyView(view);
+                                       break;
+                               default:
+                                       _emptyView = RealizeEmptyViewTemplate(emptyView, Element.EmptyViewTemplate);
+                                       break;
+                       }
+
+                       (ListViewBase as IEmptyView)?.SetEmptyView(_emptyView);
+
+                       UpdateEmptyViewVisibility();
+               }
+
+               FrameworkElement RealizeEmptyViewTemplate(object bindingContext, DataTemplate emptyViewTemplate)
+               {
+                       if (emptyViewTemplate == null)
+                       {
+                               return new TextBlock { Text = bindingContext.ToString() };
+                       }
+
+                       var template = emptyViewTemplate.SelectDataTemplate(bindingContext, null);
+                       var view = template.CreateContent() as View;
+
+                       view.BindingContext = bindingContext;
+                       return RealizeEmptyView(view);
+               }
+
+               FrameworkElement RealizeEmptyView(View view)
+               {
+                       _formsEmptyView = view;
+                       return view.GetOrCreateRenderer().ContainerElement;
+               }
+
+               protected virtual void UpdateEmptyViewVisibility()
+               {
+                       if (_emptyView != null && ListViewBase is IEmptyView emptyView)
+                       {
+                               emptyView.EmptyViewVisibility = (_collectionViewSource?.View?.Count ?? 0) == 0
+                                       ? Visibility.Visible
+                                       : Visibility.Collapsed;
+
+                               if (emptyView.EmptyViewVisibility == Visibility.Visible)
+                               {
+                                       if (ActualWidth >= 0 && ActualHeight >= 0)
+                                       {
+                                               _formsEmptyView?.Layout(new Rectangle(0, 0, ActualWidth, ActualHeight));
+                                       }
+                               }
+                       }
+               }
        }
-}
\ No newline at end of file
+}
index 5e07646..9d5f321 100644 (file)
@@ -2,8 +2,8 @@
     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">
+
+    <ItemsPanelTemplate x:Key="HorizontalListItemsPanel">
                <VirtualizingStackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
 
                </Setter>
        </Style>
 
+    <Style TargetType="local:FormsListView">
+        <Setter Property="IsTabStop" Value="False" />
+        <Setter Property="TabNavigation" Value="Once" />
+        <Setter Property="IsSwipeEnabled" Value="True" />
+        <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="UseSystemFocusVisuals" Value="True" />
+        <Setter Property="ItemContainerTransitions">
+            <Setter.Value>
+                <TransitionCollection>
+                    <AddDeleteThemeTransition />
+                    <ContentThemeTransition />
+                    <ReorderThemeTransition />
+                    <EntranceThemeTransition IsStaggeringEnabled="False" />
+                </TransitionCollection>
+            </Setter.Value>
+        </Setter>
+        <Setter Property="ItemsPanel">
+            <Setter.Value>
+                <ItemsPanelTemplate>
+                    <ItemsStackPanel Orientation="Vertical" />
+                </ItemsPanelTemplate>
+            </Setter.Value>
+        </Setter>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="local:FormsListView">
+                    <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 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>
+
 </ResourceDictionary>
 
index 90870be..8ede34c 100644 (file)
@@ -53,7 +53,7 @@ namespace Xamarin.Forms.Platform.UWP
                        }
 
                        // Default to a plain old vertical ListView
-                       return new Windows.UI.Xaml.Controls.ListView();
+                       return new FormsListView();
                }
 
                protected virtual void UpdateHeader()
@@ -180,7 +180,7 @@ namespace Xamarin.Forms.Platform.UWP
                        }
                        else
                        {
-                               gridView.UseVerticalalItemsPanel();
+                               gridView.UseVerticalItemsPanel();
                        }
 
                        gridView.MaximumRowsOrColumns = gridItemsLayout.Span;
index 4c136a0..be7349f 100644 (file)
@@ -40,6 +40,8 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="AccessibilityExtensions.cs" />
+    <Compile Include="CollectionView\FormsListView.cs" />
+    <Compile Include="CollectionView\IEmptyView.cs" />
     <Compile Include="CollectionView\ItemsViewRenderer.cs" />
     <Compile Include="CollectionView\SelectableItemsViewRenderer.cs" />
     <Compile Include="CollectionView\StructuredItemsViewRenderer.cs" />