Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
[All] Crash fixes for ListViews (#243)
Browse files Browse the repository at this point in the history
* [Controls] Add repro for 42277

* [Android] No crash if GroupHeaderTemplate=null

* [Android] Fix DataTemplateSelector crash

* [Core] Expose ListProxy on TIL

* [iOS] Fix DataTemplateSelector crash

* [Win] Fix DataTemplateSelector crash

* [Docs] Update docs

* [Core] Implement ListProxy explicitly

Allows ListProxy property to stay internal.

* [Controls] Revert unnecessary change to shproj
  • Loading branch information
samhouts authored and rmarinho committed Jul 1, 2016
1 parent 6e3e218 commit 4d270b6
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System;
using System.Linq;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
using System.Collections.Generic;

#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
#endif

namespace Xamarin.Forms.Controls
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Bugzilla, 42277, "DataTemplate System.InvalidCastException crash in 2.3.1-pre1")]
public class Bugzilla42277 : TestContentPage
{
const string Success1 = "Success1";
const string Success2 = "Success2";
const string Success3 = "GroupedSuccess3";
const string Success4 = "GroupedSuccess4";
const string Success5 = "GroupedSuccess5";
const string Success6 = "GroupedSuccess6";

class MyDataTemplateSelector : DataTemplateSelector
{
DataTemplate _1Template;
DataTemplate _2Template;

DataTemplate _3Template;
DataTemplate _4Template;
DataTemplate _5Template;
DataTemplate _6Template;

public MyDataTemplateSelector()
{
_1Template = new DataTemplate(() =>
{
return new TextCell { Text = Success1 };
});

_2Template = new DataTemplate(() =>
{
return new TextCell { Text = Success2 };
});

_3Template = new DataTemplate(() =>
{
return new TextCell { Text = Success3 };
});

_4Template = new DataTemplate(() =>
{
return new TextCell { Text = Success4 };
});

_5Template = new DataTemplate(() =>
{
return new TextCell { Text = Success5 };
});

_6Template = new DataTemplate(() =>
{
return new TextCell { Text = Success6 };
});
}

protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
int number = (int)item;
switch (number)
{
default:
case 0: return _1Template;
case 1: return _2Template;
case 2: return _3Template;
case 3: return _4Template;
case 4: return _5Template;
case 5: return _6Template;
}
}
}

protected override void Init()
{
//test non-grouped DTS
ListView listView = new ListView(ListViewCachingStrategy.RecycleElement)
{
ItemsSource = Enumerable.Range(0, 2),
ItemTemplate = new MyDataTemplateSelector()
};

//test grouped DTS
ListView groupedListView = new ListView(ListViewCachingStrategy.RecycleElement)
{
ItemsSource = new List<List<int>> { Enumerable.Range(2, 2).ToList(), Enumerable.Range(4, 2).ToList() },
IsGroupingEnabled = true,
ItemTemplate = new MyDataTemplateSelector()
};

Content = new StackLayout { Children = { listView, groupedListView } };

//test collection changed
listView.ItemsSource = Enumerable.Range(0, 2);
groupedListView.ItemsSource = new List<List<int>> { Enumerable.Range(2, 2).ToList(), Enumerable.Range(4, 2).ToList() };
}

#if UITEST
[Test]
public void Bugzilla42277Test()
{
RunningApp.WaitForElement(q => q.Marked(Success1));
RunningApp.WaitForElement(q => q.Marked(Success2));
RunningApp.WaitForElement(q => q.Marked(Success3));
RunningApp.WaitForElement(q => q.Marked(Success4));
RunningApp.WaitForElement(q => q.Marked(Success5));
RunningApp.WaitForElement(q => q.Marked(Success6));
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla39853.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TestPages\ScreenshotConditionalApp.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla41842.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla42277.cs" />
<Compile Include="$(MSBuildThisFileDirectory)_Template.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue1028.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue1075.cs" />
Expand Down
2 changes: 2 additions & 0 deletions Xamarin.Forms.Core/ITemplatedItemsList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public interface ITemplatedItemsList<TItem> : IReadOnlyList<TItem>, INotifyColle
IEnumerable ItemsSource { get; }
IReadOnlyList<string> ShortNames { get; }

IListProxy ListProxy { get; }

int GetGlobalIndexForGroup(ITemplatedItemsList<TItem> group);
int GetGlobalIndexOfItem(object item);
ITemplatedItemsList<TItem> GetGroup(int index);
Expand Down
5 changes: 5 additions & 0 deletions Xamarin.Forms.Core/TemplatedItemsList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ internal IListProxy ListProxy
private set { SetValue(ListProxyPropertyKey, value); }
}

IListProxy ITemplatedItemsList<TItem>.ListProxy
{
get { return ListProxy; }
}

DataTemplate ItemTemplate
{
get { return (DataTemplate)_itemsView.GetValue(_itemTemplateProperty); }
Expand Down
6 changes: 3 additions & 3 deletions Xamarin.Forms.Platform.Android/Renderers/ListViewAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ public override int GetItemViewType(int position)
{
object item = null;
if (_listView.IsGroupingEnabled)
item = ((ITemplatedItemsView<Cell>)TemplatedItemsView.TemplatedItems.GetGroup(group)).ListProxy[row];
item = TemplatedItemsView.TemplatedItems.GetGroup(group).ListProxy[row];
else
item = ((ITemplatedItemsView<Cell>)TemplatedItemsView.TemplatedItems).ListProxy[position];
item = TemplatedItemsView.TemplatedItems.ListProxy[position];
itemTemplate = selector.SelectTemplate(item, _listView);
}
int key;
Expand Down Expand Up @@ -405,7 +405,7 @@ List<Cell> GetCellsFromPosition(int position, int take)
{
if (_listView.CachingStrategy == ListViewCachingStrategy.RecycleElement)
{
var groupContent = _listView.TemplatedItems.GroupHeaderTemplate.CreateContent(group.ItemsSource, _listView) as Cell;
var groupContent = _listView.TemplatedItems.GroupHeaderTemplate?.CreateContent(group.ItemsSource, _listView) as Cell;
if (groupContent != null)
{
groupContent.Parent = _listView;
Expand Down
2 changes: 1 addition & 1 deletion Xamarin.Forms.Platform.WinRT/ListViewRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ async void OnViewChangeCompleted(object sender, SemanticZoomViewChangedEventArgs
if (Device.Idiom == TargetIdiom.Phone)
await Task.Delay(1);

IListProxy listProxy = ((ITemplatedItemsView<Cell>)til).ListProxy;
IListProxy listProxy = til.ListProxy;
ScrollTo(listProxy.ProxiedEnumerable, listProxy[0], ScrollToPosition.Start, true, true);
}

Expand Down
10 changes: 5 additions & 5 deletions Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ void OnFooterMeasureInvalidated(object sender, EventArgs eventArgs)

void OnGroupedCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var til = (ITemplatedItemsList<Cell>)sender;
var til = (TemplatedItemsList<ItemsView<Cell>, Cell>)sender;

var templatedItems = TemplatedItemsView.TemplatedItems;
var groupIndex = templatedItems.IndexOf(til.HeaderContent);
Expand Down Expand Up @@ -919,7 +919,7 @@ protected Cell GetCellForPath(NSIndexPath indexPath)
{
var templatedItems = TemplatedItemsView.TemplatedItems;
if (List.IsGroupingEnabled)
templatedItems = (ITemplatedItemsList<Cell>)((IList)templatedItems)[indexPath.Section];
templatedItems = (TemplatedItemsList<ItemsView<Cell>, Cell>)((IList)templatedItems)[indexPath.Section];

var cell = templatedItems[indexPath.Row];
return cell;
Expand All @@ -934,7 +934,7 @@ void OnSectionPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var currentSelected = _uiTableView.IndexPathForSelectedRow;

var til = (ITemplatedItemsView<Cell>)sender;
var til = (TemplatedItemsList<ItemsView<Cell>, Cell>)sender;
var groupIndex = ((IList)TemplatedItemsView.TemplatedItems).IndexOf(til);
if (groupIndex == -1)
{
Expand Down Expand Up @@ -968,9 +968,9 @@ int TemplateIdForPath(NSIndexPath indexPath)

var templatedList = TemplatedItemsView.TemplatedItems;
if (List.IsGroupingEnabled)
templatedList = (ITemplatedItemsList<Cell>)((IList)templatedList)[indexPath.Section];
templatedList = (TemplatedItemsList<ItemsView<Cell>, Cell>)((IList)templatedList)[indexPath.Section];

var item = ((ITemplatedItemsView<Cell>)templatedList).ListProxy[indexPath.Row];
var item = templatedList.ListProxy[indexPath.Row];

itemTemplate = selector.SelectTemplate(item, List);
int key;
Expand Down
16 changes: 16 additions & 0 deletions docs/Xamarin.Forms.Core/Xamarin.Forms/ITemplatedItemsList`1.xml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,22 @@
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="ListProxy">
<MemberSignature Language="C#" Value="public Xamarin.Forms.IListProxy ListProxy { get; }" />
<MemberSignature Language="ILAsm" Value=".property instance class Xamarin.Forms.IListProxy ListProxy" />
<MemberType>Property</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Xamarin.Forms.IListProxy</ReturnType>
</ReturnValue>
<Docs>
<summary>To be added.</summary>
<value>To be added.</value>
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="Name">
<MemberSignature Language="C#" Value="public string Name { get; set; }" />
<MemberSignature Language="ILAsm" Value=".property instance string Name" />
Expand Down

0 comments on commit 4d270b6

Please sign in to comment.