From 8b346079e298024428d4a418b3bcdf09aef300f7 Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Mon, 16 Sep 2019 07:02:58 -0600 Subject: [PATCH] Implement EmptyView on UWP (#7438) --- .../CollectionView/FormsGridView.cs | 45 ++++++- .../CollectionView/FormsListView.cs | 42 ++++++ .../CollectionView/IEmptyView.cs | 10 ++ .../CollectionView/ItemsViewRenderer.cs | 98 +++++++++++++- .../CollectionView/ItemsViewStyles.xaml | 146 ++++++++++++++++++++- .../CollectionView/StructuredItemsViewRenderer.cs | 4 +- .../Xamarin.Forms.Platform.UAP.csproj | 2 + 7 files changed, 337 insertions(+), 10 deletions(-) create mode 100644 Xamarin.Forms.Platform.UAP/CollectionView/FormsListView.cs create mode 100644 Xamarin.Forms.Platform.UAP/CollectionView/IEmptyView.cs diff --git a/Xamarin.Forms.Platform.UAP/CollectionView/FormsGridView.cs b/Xamarin.Forms.Platform.UAP/CollectionView/FormsGridView.cs index 694447a..68781df 100644 --- a/Xamarin.Forms.Platform.UAP/CollectionView/FormsGridView.cs +++ b/Xamarin.Forms.Platform.UAP/CollectionView/FormsGridView.cs @@ -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 index 0000000..532a135 --- /dev/null +++ b/Xamarin.Forms.Platform.UAP/CollectionView/FormsListView.cs @@ -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 index 0000000..9fb4894 --- /dev/null +++ b/Xamarin.Forms.Platform.UAP/CollectionView/IEmptyView.cs @@ -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 diff --git a/Xamarin.Forms.Platform.UAP/CollectionView/ItemsViewRenderer.cs b/Xamarin.Forms.Platform.UAP/CollectionView/ItemsViewRenderer.cs index 2477e21..14a6745 100644 --- a/Xamarin.Forms.Platform.UAP/CollectionView/ItemsViewRenderer.cs +++ b/Xamarin.Forms.Platform.UAP/CollectionView/ItemsViewRenderer.cs @@ -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 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 +} diff --git a/Xamarin.Forms.Platform.UAP/CollectionView/ItemsViewStyles.xaml b/Xamarin.Forms.Platform.UAP/CollectionView/ItemsViewStyles.xaml index 5e07646..9d5f321 100644 --- a/Xamarin.Forms.Platform.UAP/CollectionView/ItemsViewStyles.xaml +++ b/Xamarin.Forms.Platform.UAP/CollectionView/ItemsViewStyles.xaml @@ -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"> - - + + @@ -37,5 +37,147 @@ + + + + diff --git a/Xamarin.Forms.Platform.UAP/CollectionView/StructuredItemsViewRenderer.cs b/Xamarin.Forms.Platform.UAP/CollectionView/StructuredItemsViewRenderer.cs index 90870be..8ede34c 100644 --- a/Xamarin.Forms.Platform.UAP/CollectionView/StructuredItemsViewRenderer.cs +++ b/Xamarin.Forms.Platform.UAP/CollectionView/StructuredItemsViewRenderer.cs @@ -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; diff --git a/Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj b/Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj index 4c136a0..be7349f 100644 --- a/Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj +++ b/Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj @@ -40,6 +40,8 @@ + + -- 2.7.4