--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<controls:TestContentPage
+ xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
+ xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ x:Class="Xamarin.Forms.Controls.Issues.CollectionViewBindingErrors">
+ <StackLayout>
+ <Label Text="The label below should read 'Binding Errors: 0'; if the number of binding errors is greater than zero, this test has failed."></Label>
+
+ <Label x:Name="BindingErrorCount" Text="Binding Errors: 0"></Label>
+
+ <CollectionView x:Name="CollectionView" ItemsSource="{Binding ItemsList}">
+ <CollectionView.ItemTemplate>
+ <DataTemplate>
+ <StackLayout>
+ <Image Source="{Binding Image}" WidthRequest="100" HeightRequest="100"/>
+ <Label Text="{Binding Caption}"></Label>
+ </StackLayout>
+ </DataTemplate>
+ </CollectionView.ItemTemplate>
+ </CollectionView>
+ </StackLayout>
+</controls:TestContentPage>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.Xaml;
+
+#if UITEST
+using Xamarin.UITest;
+using NUnit.Framework;
+using Xamarin.Forms.Core.UITests;
+#endif
+
+namespace Xamarin.Forms.Controls.Issues
+{
+#if UITEST
+ [Category(UITestCategories.CollectionView)]
+#endif
+#if APP
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+#endif
+ [Preserve(AllMembers = true)]
+ [Issue(IssueTracker.None, 68000, "Binding errors when CollectionView ItemsSource is set with a binding",
+ PlatformAffected.Android)]
+ public partial class CollectionViewBindingErrors : TestContentPage
+ {
+ public CollectionViewBindingErrors()
+ {
+#if APP
+ Device.SetFlags(new List<string> { CollectionView.CollectionViewExperimental });
+
+ InitializeComponent();
+
+ var listener = new CountBindingErrors(BindingErrorCount);
+ Log.Listeners.Add(listener);
+ Disappearing += (obj, args) => { Log.Listeners.Remove(listener); };
+
+ BindingContext = new BindingErrorsViewModel();
+#endif
+ }
+
+ protected override void Init()
+ {
+
+ }
+
+#if UITEST
+ [Test]
+ public void CollectionViewBindingErrorsShouldBeZero()
+ {
+ RunningApp.WaitForElement("Binding Errors: 0");
+ }
+#endif
+ }
+
+ [Preserve(AllMembers = true)]
+ public class CollectionViewGalleryTestItem
+ {
+ public DateTime Date { get; set; }
+ public string Caption { get; set; }
+ public string Image { get; set; }
+ public int Index { get; set; }
+
+ public CollectionViewGalleryTestItem(DateTime date, string caption, string image, int index)
+ {
+ Date = date;
+ Caption = caption;
+ Image = image;
+ Index = index;
+ }
+
+ public override string ToString()
+ {
+ return $"Item: {Index}";
+ }
+ }
+
+ [Preserve(AllMembers = true)]
+ internal class CountBindingErrors : LogListener
+ {
+ private readonly Label _errorCount;
+ int _count;
+
+ public CountBindingErrors(Label errorCount)
+ {
+ _errorCount = errorCount;
+ }
+
+ public override void Warning(string category, string message)
+ {
+ if (category == "Binding")
+ {
+ _count += 1;
+ }
+
+ _errorCount.Text = $"Binding Errors: {_count}";
+ }
+ }
+
+ [Preserve(AllMembers = true)]
+ internal class BindingErrorsViewModel
+ {
+ readonly string[] _imageOptions = {
+ "cover1.jpg",
+ "oasis.jpg",
+ "photo.jpg",
+ "Vegetables.jpg",
+ "Fruits.jpg",
+ "FlowerBuds.jpg",
+ "Legumes.jpg"
+ };
+
+ List<CollectionViewGalleryTestItem> GenerateList()
+ {
+ var items = new List<CollectionViewGalleryTestItem>();
+ var images = _imageOptions;
+
+ for (int n = 0; n < 100; n++)
+ {
+ items.Add(new CollectionViewGalleryTestItem(DateTime.Now.AddDays(n),
+ $"Item: {n}", images[n % images.Length], n));
+ }
+
+ return items;
+ }
+
+ List<CollectionViewGalleryTestItem> _items;
+
+ public List<CollectionViewGalleryTestItem> ItemsList
+ {
+ get
+ {
+ if (_items == null)
+ {
+ _items = GenerateList();
+ }
+
+ return _items;
+ }
+ }
+ }
+}
\ No newline at end of file
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue4919.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue5461.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)CollectionViewBindingErrors.xaml.cs">
+ <DependentUpon>CollectionViewBindingErrors.xaml</DependentUpon>
+ <SubType>Code</SubType>
+ </Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue2102.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue1588.xaml.cs">
<DependentUpon>Issue1588.xaml</DependentUpon>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="$(MSBuildThisFileDirectory)CollectionViewBindingErrors.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </EmbeddedResource>
+ </ItemGroup>
</Project>
\ No newline at end of file
--- /dev/null
+using System;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public class CollectionViewGalleryTestItem
+ {
+ public DateTime Date { get; set; }
+ public string Caption { get; set; }
+ public string Image { get; set; }
+ public int Index { get; set; }
+
+ public CollectionViewGalleryTestItem(DateTime date, string caption, string image, int index)
+ {
+ Date = date;
+ Caption = caption;
+ Image = image;
+ Index = index;
+ }
+
+ public override string ToString()
+ {
+ return $"Item: {Index}";
+ }
+ }
+}
\ No newline at end of file
GalleryBuilder.NavButton("ItemSizing Strategy", () =>
new VariableSizeTemplateGridGallery (ItemsLayoutOrientation.Horizontal), Navigation),
-
+
+ GalleryBuilder.NavButton("DataTemplateSelector", () =>
+ new DataTemplateSelectorGallery(), Navigation),
}
- }
+ }
};
}
}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:local="clr-namespace:Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries;assembly=Xamarin.Forms.Controls"
+ x:Class="Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.DataTemplateSelectorGallery">
+
+ <ContentPage.Resources>
+ <ResourceDictionary>
+ <DataTemplate x:Key="DefaultTemplate">
+ <Grid HeightRequest="50">
+ <Grid.RowDefinitions>
+ <RowDefinition></RowDefinition>
+ <RowDefinition></RowDefinition>
+ </Grid.RowDefinitions>
+
+ <Image Source="coffee.png" AutomationId="weekday"/>
+
+ <Label Grid.Row="1" Text="{Binding Date, StringFormat='{}{0:dddd}'}"></Label>
+ </Grid>
+ </DataTemplate>
+ <DataTemplate x:Key="WeekendTemplate">
+ <Grid HeightRequest="50">
+ <Grid.RowDefinitions>
+ <RowDefinition></RowDefinition>
+ <RowDefinition></RowDefinition>
+ </Grid.RowDefinitions>
+
+ <Image Source="oasis.jpg" AutomationId="weekend"/>
+
+ <Label Grid.Row="1" Text="It's the weekend! Woot!"></Label>
+ </Grid>
+ </DataTemplate>
+
+ <DataTemplate x:Key="EmptyTemplate">
+ <StackLayout>
+ <Label Text="{Binding ., StringFormat='({0}) does not match any day of the week.'}"></Label>
+ </StackLayout>
+ </DataTemplate>
+
+ <DataTemplate x:Key="SymbolsTemplate">
+ <StackLayout BackgroundColor="Red">
+ <Label Text="{Binding ., StringFormat='({0}) _definitely_ does not match any day of the week.'}"></Label>
+ </StackLayout>
+ </DataTemplate>
+
+ <local:WeekendSelector x:Key="WeekendSelector"
+ DefaultTemplate="{StaticResource DefaultTemplate}"
+ FridayTemplate="{StaticResource WeekendTemplate}" />
+
+ <local:SearchTermSelector x:Key="SearchTermSelector"
+ DefaultTemplate="{StaticResource EmptyTemplate}"
+ SymbolsTemplate="{StaticResource SymbolsTemplate}" />
+
+ </ResourceDictionary>
+ </ContentPage.Resources>
+
+ <ContentPage.Content>
+
+ <StackLayout>
+
+ <SearchBar x:Name="SearchBar" Placeholder="Day of Week Filter" />
+
+ <CollectionView x:Name="CollectionView" ItemTemplate="{StaticResource WeekendSelector}"
+ EmptyViewTemplate="{StaticResource SearchTermSelector}"/>
+
+ </StackLayout>
+ </ContentPage.Content>
+</ContentPage>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
+{
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class DataTemplateSelectorGallery : ContentPage
+ {
+ DemoFilteredItemSource _demoFilteredItemSource;
+
+ public DataTemplateSelectorGallery()
+ {
+ InitializeComponent();
+
+ _demoFilteredItemSource = new DemoFilteredItemSource(filter: ItemMatches);
+
+ CollectionView.ItemsSource = _demoFilteredItemSource.Items;
+
+ SearchBar.SearchCommand = new Command(() =>
+ {
+ _demoFilteredItemSource.FilterItems(SearchBar.Text);
+ CollectionView.EmptyView = SearchBar.Text;
+ });
+ }
+
+ private bool ItemMatches(string filter, CollectionViewGalleryTestItem item)
+ {
+ if (String.IsNullOrEmpty(filter))
+ {
+ return true;
+ }
+
+ return item.Date.DayOfWeek.ToString().ToLower().Contains(filter.ToLower());
+ }
+ }
+
+ public class WeekendSelector : DataTemplateSelector
+ {
+ public DataTemplate FridayTemplate { get; set; }
+ public DataTemplate DefaultTemplate { get; set; }
+
+ protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
+ {
+ var dow = ((CollectionViewGalleryTestItem)item).Date.DayOfWeek;
+
+ return dow == DayOfWeek.Saturday || dow == DayOfWeek.Sunday
+ ? FridayTemplate
+ : DefaultTemplate;
+ }
+ }
+
+ public class SearchTermSelector : DataTemplateSelector
+ {
+ public DataTemplate DefaultTemplate { get; set; }
+ public DataTemplate SymbolsTemplate { get; set; }
+
+ protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
+ {
+ var search = ((string)item);
+
+ return search.Any(c => !char.IsLetter(c))
+ ? SymbolsTemplate
+ : DefaultTemplate;
+ }
+ }
+}
\ No newline at end of file
internal class DemoFilteredItemSource
{
readonly List<CollectionViewGalleryTestItem> _source;
+ private readonly Func<string, CollectionViewGalleryTestItem, bool> _filter;
public ObservableCollection<CollectionViewGalleryTestItem> Items { get; }
- public DemoFilteredItemSource(int count = 50)
+ public DemoFilteredItemSource(int count = 50, Func<string, CollectionViewGalleryTestItem, bool> filter = null)
{
_source = new List<CollectionViewGalleryTestItem>();
$"{images[n % images.Length]}, {n}", images[n % images.Length], n));
}
Items = new ObservableCollection<CollectionViewGalleryTestItem>(_source);
+
+ _filter = filter ?? ItemMatches;
+ }
+
+ private bool ItemMatches(string filter, CollectionViewGalleryTestItem item)
+ {
+ return item.Caption.ToLower().Contains(filter.ToLower());
}
public void FilterItems(string filter)
{
- var filteredItems = _source.Where(item => item.Caption.ToLower().Contains(filter.ToLower())).ToList();
+ var filteredItems = _source.Where(item => _filter(filter, item)).ToList();
foreach (CollectionViewGalleryTestItem collectionViewGalleryTestItem in _source)
{
--- /dev/null
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.EmptyViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public class EmptyViewGalleryFilterInfo : INotifyPropertyChanged
+ {
+ string _filter;
+
+ public string Filter
+ {
+ get => _filter;
+ set
+ {
+ _filter = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
\ No newline at end of file
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
-using Xamarin.Forms.Internals;
-using Xamarin.Forms.Xaml;
+using Xamarin.Forms.Xaml;
namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.EmptyViewGalleries
{
});
}
}
-
- [Preserve(AllMembers = true)]
- public class EmptyViewGalleryFilterInfo : INotifyPropertyChanged
- {
- string _filter;
-
- public string Filter
- {
- get => _filter;
- set
- {
- _filter = value;
- OnPropertyChanged();
- }
- }
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- void OnPropertyChanged([CallerMemberName] string propertyName = null)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
- }
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
-using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
{
- [Preserve(AllMembers = true)]
- public class CollectionViewGalleryTestItem
- {
- public DateTime Date { get; set; }
- public string Caption { get; set; }
- public string Image { get; set; }
- public int Index { get; set; }
-
- public CollectionViewGalleryTestItem(DateTime date, string caption, string image, int index)
- {
- Date = date;
- Caption = caption;
- Image = image;
- Index = index;
- }
-
- public override string ToString()
- {
- return $"Item: {Index}";
- }
- }
-
- internal enum ItemsSourceType
+ internal enum ItemsSourceType
{
List,
ObservableCollection,
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="GalleryPages\CollectionViewGalleries\SelectionGalleries\PreselectedItemsGallery.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\CollectionViewGalleries\DataTemplateSelectorGallery.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="GalleryPages\MapWithItemsSourceGallery.xaml">
}
}
- [TestCase("EmptyView", "EmptyView (load simulation)", "photo")]
+ [TestCase("EmptyView", "EmptyView (load simulation)", "photo")]
public void VisitAndCheckItem(string collectionTestName, string subgallery, string item)
{
VisitInitialGallery(collectionTestName);
App.WaitForElement(t => t.Marked(item));
}
+
+ [TestCase("DataTemplate Galleries", "DataTemplateSelector")]
+ void VisitAndCheckForItems(string collectionTestName, string subGallery)
+ {
+ VisitInitialGallery(collectionTestName);
+
+ App.WaitForElement(t => t.Marked(subGallery));
+ App.Tap(t => t.Marked(subGallery));
+
+ App.WaitForElement("weekend");
+ App.WaitForElement("weekday");
+ }
+
}
}
\ No newline at end of file
// So we give it an alternate delegate for creating the views
ItemsViewAdapter = new ItemsViewAdapter(ItemsView,
- (renderer, context) => new SizedItemContentView(renderer, context, () => Width, () => Height));
+ (view, context) => new SizedItemContentView(context, () => Width, () => Height));
SwapAdapter(ItemsViewAdapter, false);
}
using Android.Support.V7.Widget;
using Android.Views;
using Android.Widget;
+using Java.Lang;
+using Object = Java.Lang.Object;
namespace Xamarin.Forms.Platform.Android
{
{
public object EmptyView { get; set; }
public DataTemplate EmptyViewTemplate { get; set; }
+ protected readonly ItemsView ItemsView;
public override int ItemCount => 1;
- public EmptyViewAdapter()
+ public EmptyViewAdapter(ItemsView itemsView)
{
CollectionView.VerifyCollectionViewFlagEnabled(nameof(EmptyViewAdapter));
+ ItemsView = itemsView;
+ }
+
+ public override void OnViewRecycled(Object holder)
+ {
+ if (holder is TemplatedItemViewHolder templatedItemViewHolder)
+ {
+ templatedItemViewHolder.Recycle(ItemsView);
+ }
+
+ base.OnViewRecycled(holder);
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
return;
}
+ if (holder is TemplatedItemViewHolder templatedItemViewHolder)
+ {
+ // Use EmptyView as the binding context for the template
+ templatedItemViewHolder.Bind(EmptyView, ItemsView);
+ }
+
if (!(holder is EmptyViewHolder emptyViewHolder))
{
return;
}
-
- // Use EmptyView as the binding context for the template
- BindableObject.SetInheritedBindingContext(emptyViewHolder.View, EmptyView);
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
var context = parent.Context;
- if (EmptyViewTemplate == null)
+ var template = EmptyViewTemplate;
+
+ if (template == null)
{
if (!(EmptyView is View formsView))
{
}
// EmptyView is a Forms View; display that
- var itemContentControl = new SizedItemContentView(CreateRenderer(formsView, context), context,
- () => parent.Width, () => parent.Height);
+ var itemContentControl = new SizedItemContentView(context, () => parent.Width, () => parent.Height);
+ itemContentControl.RealizeContent(formsView);
return new EmptyViewHolder(itemContentControl, formsView);
}
- // We have a template, so create a view from it
- var templateElement = EmptyViewTemplate.CreateContent() as View;
- var templatedItemContentControl = new SizedItemContentView(CreateRenderer(templateElement, context),
- context, () => parent.Width, () => parent.Height);
- return new EmptyViewHolder(templatedItemContentControl, templateElement);
- }
-
- IVisualElementRenderer CreateRenderer(View view, Context context)
- {
- if (view == null)
- {
- throw new ArgumentNullException(nameof(view));
- }
-
- var renderer = Platform.CreateRenderer(view, context);
- Platform.SetRenderer(view, renderer);
-
- return renderer;
+ var itemContentView = new SizedItemContentView(parent.Context, () => parent.Width, () => parent.Height);
+ return new TemplatedItemViewHolder(itemContentView, template);
}
static TextView CreateTextView(string text, Context context)
+using System;
using Android.Content;
using Android.Views;
{
internal class ItemContentView : ViewGroup
{
- protected readonly IVisualElementRenderer Content;
+ protected IVisualElementRenderer Content;
- public ItemContentView(IVisualElementRenderer content, Context context) : base(context)
+ public ItemContentView(Context context) : base(context)
{
- Content = content;
- AddContent();
}
- void AddContent()
+ internal void RealizeContent(View view)
{
+ Content = CreateRenderer(view, Context);
AddView(Content.View);
}
+ internal void Recycle()
+ {
+ RemoveView(Content.View);
+ Content = null;
+ }
+
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
+ if (Content == null)
+ {
+ return;
+ }
+
var size = Context.FromPixels(r - l, b - t);
Content.Element.Layout(new Rectangle(Point.Zero, size));
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
+ if (Content == null)
+ {
+ SetMeasuredDimension(0, 0);
+ return;
+ }
+
int pixelWidth = MeasureSpec.GetSize(widthMeasureSpec);
int pixelHeight = MeasureSpec.GetSize(heightMeasureSpec);
SetMeasuredDimension(pixelWidth, pixelHeight);
}
+
+ static IVisualElementRenderer CreateRenderer(View view, Context context)
+ {
+ if (view == null)
+ {
+ throw new ArgumentNullException(nameof(view));
+ }
+
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ var renderer = Platform.CreateRenderer(view, context);
+ Platform.SetRenderer(view, renderer);
+
+ return renderer;
+ }
}
}
\ No newline at end of file
namespace Xamarin.Forms.Platform.Android
{
- // TODO hartez 2018/07/25 14:43:04 Experiment with an ItemSource property change as _adapter.notifyDataSetChanged
+ // TODO hartez 2018/07/25 14:43:04 Experiment with an ItemSource property change as _adapter.notifyDataSetChanged
public class ItemsViewAdapter : RecyclerView.Adapter
{
protected readonly ItemsView ItemsView;
- readonly Func<IVisualElementRenderer, Context, AView> _createView;
+ readonly Func<View, Context, ItemContentView> _createItemContentView;
internal readonly IItemsViewSource ItemsSource;
bool _disposed;
- internal ItemsViewAdapter(ItemsView itemsView, Func<IVisualElementRenderer, Context, AView> createView = null)
+ internal ItemsViewAdapter(ItemsView itemsView, Func<View, Context, ItemContentView> createItemContentView = null)
{
CollectionView.VerifyCollectionViewFlagEnabled(nameof(ItemsViewAdapter));
ItemsView = itemsView;
- _createView = createView;
+ _createItemContentView = createItemContentView;
ItemsSource = ItemsSourceFactory.Create(itemsView.ItemsSource, this);
- if (_createView == null)
+ if (_createItemContentView == null)
{
- _createView = (renderer, context) => new ItemContentView(renderer, context);
+ _createItemContentView = (view, context) => new ItemContentView(context);
}
}
{
if (holder is TemplatedItemViewHolder templatedItemViewHolder)
{
- ItemsView.RemoveLogicalChild(templatedItemViewHolder.View);
+ templatedItemViewHolder.Recycle(ItemsView);
}
base.OnViewRecycled(holder);
textViewHolder.TextView.Text = ItemsSource[position].ToString();
break;
case TemplatedItemViewHolder templatedItemViewHolder:
- BindableObject.SetInheritedBindingContext(templatedItemViewHolder.View, ItemsSource[position]);
+ templatedItemViewHolder.Bind(ItemsSource[position], ItemsView);
break;
}
}
return new TextViewHolder(view);
}
- // Realize the content, create a renderer out of it, and use that
- var templateElement = (View)template.CreateContent();
- ItemsView.AddLogicalChild(templateElement);
- var itemContentControl = _createView(CreateRenderer(templateElement, context), context);
+ var itemContentView = new ItemContentView(parent.Context);
+ return new TemplatedItemViewHolder(itemContentView, template);
+ }
+
+ public override int ItemCount => ItemsSource.Count;
- return new TemplatedItemViewHolder(itemContentControl, templateElement);
+ public override int GetItemViewType(int position)
+ {
+ // TODO hartez We might be able to turn this to our own purposes
+ // We might be able to have the CollectionView signal the adapter if the ItemTemplate property changes
+ // And as long as it's null, we return a value to that effect here
+ // Then we don't have to check _itemsView.ItemTemplate == null in OnCreateViewHolder, we can just use
+ // the viewType parameter.
+ return 42;
}
protected override void Dispose(bool disposing)
}
}
- static IVisualElementRenderer CreateRenderer(View view, Context context)
- {
- if (view == null)
- {
- throw new ArgumentNullException(nameof(view));
- }
-
- if (context == null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- var renderer = Platform.CreateRenderer(view, context);
- Platform.SetRenderer(view, renderer);
-
- return renderer;
- }
-
- public override int ItemCount => ItemsSource.Count;
-
- public override int GetItemViewType(int position)
- {
- // TODO hartez We might be able to turn this to our own purposes
- // We might be able to have the CollectionView signal the adapter if the ItemTemplate property changes
- // And as long as it's null, we return a value to that effect here
- // Then we don't have to check _itemsView.ItemTemplate == null in OnCreateViewHolder, we can just use
- // the viewType parameter.
- return 42;
- }
-
public virtual int GetPositionForItem(object item)
{
for (int n = 0; n < ItemsSource.Count; n++)
EmptyViewAdapter _emptyViewAdapter;
DataChangeObserver _dataChangeViewObserver;
+ bool _watchingForEmpty;
public ItemsViewRenderer(Context context) : base(context)
{
// Stop watching the old adapter to see if it's empty (if we _are_ watching)
Unwatch(ItemsViewAdapter ?? GetAdapter());
-
+
UpdateAdapter();
UpdateEmptyView();
void Unwatch(Adapter adapter)
{
- if (adapter != null && _dataChangeViewObserver != null)
+ if (_watchingForEmpty && adapter != null && _dataChangeViewObserver != null)
{
adapter.UnregisterAdapterDataObserver(_dataChangeViewObserver);
}
+
+ _watchingForEmpty = false;
}
// TODO hartez 2018/10/24 19:25:14 I don't like these method names; too generic
// TODO hartez 2018/11/05 22:37:42 Also, thinking all the EmptyView stuff should be moved to a helper
void Watch(Adapter adapter)
{
+ if (_watchingForEmpty)
+ {
+ return;
+ }
+
if (_dataChangeViewObserver == null)
{
_dataChangeViewObserver = new DataChangeObserver(UpdateEmptyViewVisibility);
}
adapter.RegisterAdapterDataObserver(_dataChangeViewObserver);
+ _watchingForEmpty = true;
}
protected virtual void SetUpNewElement(ItemsView newElement)
protected virtual void UpdateEmptyView()
{
- if (ItemsViewAdapter == null)
+ if (ItemsViewAdapter == null || ItemsView == null)
{
return;
}
{
if (_emptyViewAdapter == null)
{
- _emptyViewAdapter = new EmptyViewAdapter();
+ _emptyViewAdapter = new EmptyViewAdapter(ItemsView);
}
_emptyViewAdapter.EmptyView = emptyView;
protected readonly SelectableItemsView SelectableItemsView;
List<SelectableViewHolder> _currentViewHolders = new List<SelectableViewHolder>();
- internal SelectableItemsViewAdapter(SelectableItemsView selectableItemsView,
- Func<IVisualElementRenderer, Context, global::Android.Views.View> createView = null) : base(selectableItemsView, createView)
+ internal SelectableItemsViewAdapter(SelectableItemsView selectableItemsView,
+ Func<View, Context, ItemContentView> createView = null) : base(selectableItemsView, createView)
{
SelectableItemsView = selectableItemsView;
}
readonly Func<int> _width;
readonly Func<int> _height;
- public SizedItemContentView(IVisualElementRenderer content, Context context, Func<int> width, Func<int> height)
- : base(content, context)
+ public SizedItemContentView(Context context, Func<int> width, Func<int> height)
+ : base(context)
{
_width = width;
_height = height;
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
+ if (Content == null)
+ {
+ SetMeasuredDimension(0, 0);
+ return;
+ }
+
var targetWidth = _width();
var targetHeight = _height();
+using System;
+using Android.Content;
+using Xamarin.Forms.Internals;
+
namespace Xamarin.Forms.Platform.Android
{
internal class TemplatedItemViewHolder : SelectableViewHolder
{
- public View View { get; }
+ private readonly ItemContentView _itemContentView;
+ private readonly DataTemplate _template;
+
+ public View View { get; private set; }
- public TemplatedItemViewHolder(global::Android.Views.View itemView, View rootElement) : base(itemView)
+ public TemplatedItemViewHolder(ItemContentView itemContentView, DataTemplate template) : base(itemContentView)
{
- View = rootElement;
+ _itemContentView = itemContentView;
+ _template = template;
}
protected override void OnSelectedChanged()
{
base.OnSelectedChanged();
+ if (View == null)
+ {
+ return;
+ }
+
VisualStateManager.GoToState(View, IsSelected
? VisualStateManager.CommonStates.Selected
: VisualStateManager.CommonStates.Normal);
}
+
+ public void Recycle(ItemsView itemsView)
+ {
+ itemsView.RemoveLogicalChild(View);
+ _itemContentView.Recycle();
+ }
+
+ public void Bind(object itemBindingContext, ItemsView itemsView)
+ {
+ var template = _template.SelectDataTemplate(itemBindingContext, itemsView);
+
+ View = (View)template.CreateContent();
+ _itemContentView.RealizeContent(View);
+
+ // Set the binding context before we add it as a child of the ItemsView; otherwise, it will
+ // inherit the ItemsView's binding context
+ View.BindingContext = itemBindingContext;
+
+ itemsView.AddLogicalChild(View);
+ }
}
}
\ No newline at end of file
using System.Collections.Generic;
using Foundation;
using UIKit;
+using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Platform.iOS
{
void ApplyTemplateAndDataContext(TemplatedCell cell, NSIndexPath indexPath)
{
- // We need to create a renderer, which means we need a template
- var view = _itemsView.ItemTemplate.CreateContent() as View;
- _itemsView.AddLogicalChild(view);
+ var template = _itemsView.ItemTemplate;
+ var item = _itemsSource[indexPath.Row];
+
+ // Run this through the extension method in case it's really a DataTemplateSelector
+ template = template.SelectDataTemplate(item, _itemsView);
+
+ // Create the content and renderer for the view and
+ var view = template.CreateContent() as View;
var renderer = CreateRenderer(view);
- BindableObject.SetInheritedBindingContext(view, _itemsSource[indexPath.Row]);
cell.SetRenderer(renderer);
+
+ // Bind the view to the data item
+ view.BindingContext = _itemsSource[indexPath.Row];
+
+ // And make sure it's a "child" of the ItemsView
+ _itemsView.AddLogicalChild(view);
}
internal void RemoveLogicalChild(UICollectionViewCell cell)
{
if (emptyViewTemplate != null)
{
+ // Run this through the extension method in case it's really a DataTemplateSelector
+ emptyViewTemplate = emptyViewTemplate.SelectDataTemplate(emptyView, _itemsView);
+
// We have a template; turn it into a Forms view
var templateElement = emptyViewTemplate.CreateContent() as View;
var renderer = CreateRenderer(templateElement);
void CreatePlaceholderLabel()
{
if (Control == null)
+ {
return;
+ }
Control.AddSubview(_placeholderLabel);