[iOS] ViewCells will respond to ForceUpdateSize in RecycleElement mode (#809)
authorSamantha Houts <samantha@teamredwall.com>
Tue, 14 Mar 2017 10:30:22 +0000 (03:30 -0700)
committerRui Marinho <me@ruimarinho.net>
Tue, 14 Mar 2017 10:30:22 +0000 (10:30 +0000)
* Add reproduction for 44525

* Clean up gallery page

* [iOS] Get index path from Cell in RecycleElement

* Fix obsolete code

Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla44525.cs [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
Xamarin.Forms.Controls/ControlGalleryPages/CellForceUpdateSizeGalleryPage.cs
Xamarin.Forms.Platform.iOS/Cells/CellRenderer.cs

diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla44525.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla44525.cs
new file mode 100644 (file)
index 0000000..e159aa7
--- /dev/null
@@ -0,0 +1,104 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+using System.ComponentModel;
+
+namespace Xamarin.Forms.Controls
+{
+       [Preserve(AllMembers = true)]
+       [Issue(IssueTracker.Bugzilla, 44525, "Listview Row Height Does Not Adapt In iOS")]
+       public class Bugzilla44525 : TestContentPage
+       {
+               List<Person> _DataSource;
+
+               [Preserve(AllMembers = true)]
+               class CustomCell : ViewCell
+               {
+                       public CustomCell()
+                       {
+                               Label age = new Label();
+                               Label name = new Label();
+                               StackLayout cellWrapper = new StackLayout();
+
+                               age.SetBinding(Label.TextProperty, "Age");
+                               name.SetBinding(Label.TextProperty, "Name");
+
+                               age.PropertyChanged += UpdateCell;
+                               name.PropertyChanged += UpdateCell;
+
+                               cellWrapper.Children.Add(age);
+                               cellWrapper.Children.Add(name);
+
+                               View = cellWrapper;
+                       }
+
+                       void UpdateCell(object sender, PropertyChangedEventArgs e)
+                       {
+                               if (e.PropertyName == Label.TextProperty.PropertyName)
+                               {
+                                       ForceUpdateSize();
+                               }
+                       }
+               }
+
+               class Person : ViewModelBase
+               {
+                       private string _Name;
+                       public string Name
+                       {
+                               get
+                               {
+                                       return _Name;
+                               }
+                               set
+                               {
+                                       if (_Name == value)
+                                               return;
+
+                                       _Name = value;
+                                       OnPropertyChanged();
+                               }
+                       }
+
+                       private string _Age;
+                       public string Age
+                       {
+                               get
+                               {
+                                       return _Age;
+                               }
+                               set
+                               {
+                                       if (_Age == value)
+                                               return;
+
+                                       _Age = value;
+                                       OnPropertyChanged();
+                               }
+                       }
+               }
+
+               protected override void Init()
+               {
+                       _DataSource = Enumerable.Range(1, 100).Select(c => new Person { Name = $"Person {c}", Age = $"{c} year(s) old" }).ToList();
+
+                       var listView = new ListView(ListViewCachingStrategy.RecycleElement)
+                       {
+                               ItemTemplate = new DataTemplate(typeof(CustomCell)),
+                               ItemsSource = _DataSource,
+                               HasUnevenRows = true
+                       };
+
+                       var button = new Button { Text = "Click me" };
+                       button.Clicked += (sender, e) =>
+                       {
+                               var target = _DataSource[1];
+                               target.Name = "I am an exceptionally long string that should cause the label to wrap, thus increasing the size of the ViewCell such that the entirety of the string is readable by human eyes. Hurrah.";
+                       };
+
+                       Content = new StackLayout { Children = { button, listView } };
+               }
+       }
+}
index 99f2bf8..38fc48a 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla40722.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla41153.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla44129.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Bugzilla44525.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla28650.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla37431.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla44777.cs" />
index 6e64b0d..5d8b3fe 100644 (file)
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
+using System.Threading.Tasks;
 using Xamarin.Forms.Internals;
 
 namespace Xamarin.Forms.Controls
 {
-       [Preserve (AllMembers = true)]
-       public class CellForceUpdateSizeGalleryPage : TabbedPage
+       [Preserve(AllMembers = true)]
+       public class CellForceUpdateSizeGalleryPage : NavigationPage
        {
-               public class ViewCellPage : ContentPage
+               [Preserve(AllMembers = true)]
+               public class MyViewCell : ViewCell
                {
-                       [Preserve (AllMembers = true)]
-                       public class MyViewCell : ViewCell
-                       {
-                               public MyViewCell ()
-                               {
-                                       var image = new Image {
-                                               Source = ImageSource.FromFile ("crimson.jpg"),
-                                               BackgroundColor = Color.Gray,
-                                               HeightRequest = 50,
-                                               VerticalOptions = LayoutOptions.Fill,
-                                               HorizontalOptions = LayoutOptions.Fill
-                                       };
-
-                                       var button = new Button { Text = "+" };
-                                       button.Clicked += (object sender, EventArgs e) =>
-                                       {
-                                               image.HeightRequest = image.Height + 100;
-                                               ForceUpdateSize ();
-                                       };
+                       Label _Label = new Label();
+                       VariableHeightItem _DataItem => BindingContext as VariableHeightItem;
 
-                                       Tapped += (object sender, EventArgs e) =>
-                                       {
-                                               image.HeightRequest = image.Height - 100;
-                                               ForceUpdateSize ();
-                                       };
+                       public MyViewCell()
+                       {
+                               _Label.SetBinding(Label.TextProperty, nameof(_DataItem.Text));
+                               _Label.PropertyChanged += UpdateCell;
 
-                                       View = new StackLayout { Orientation = StackOrientation.Horizontal, Children = { image, button } };
-                               }
+                               View = new StackLayout { Children = { _Label } };
                        }
 
-                       public ViewCellPage ()
+                       void UpdateCell(object sender, PropertyChangedEventArgs e)
                        {
-                               var listview = new ListView {
-                                       HasUnevenRows = true,
-                               };
-                               var items = Enumerable.Range (0, 10);
-                               listview.ItemsSource = items;
-                               listview.ItemTemplate = new DataTemplate (typeof (MyViewCell));
-                               Content = listview;
-                               Title = "View Cell";
+                               if (e.PropertyName == Label.TextProperty.PropertyName)
+                               {
+                                       ForceUpdateSize();
+                               }
                        }
                }
 
-               public class ImageCellPage : ContentPage
+               [Preserve(AllMembers = true)]
+               public class MyImageCell : ImageCell
                {
-                       [Preserve (AllMembers = true)]
-                       public class MyImageCell : ImageCell
+                       VariableHeightItem _DataItem => BindingContext as VariableHeightItem;
+
+                       public MyImageCell()
                        {
-                               public MyImageCell ()
-                               {
-                                       ImageSource = ImageSource.FromFile ("crimson.jpg");
-                                       Height = 20;
-                                       Command = new Command (() =>
-                                       {
-                                               Height += 20;
-                                               ForceUpdateSize ();
-                                       });
-                               }
+                               SetBinding(TextProperty, new Binding(nameof(_DataItem.Text)));
+                               PropertyChanged += UpdateCell;
                        }
-                       public ImageCellPage ()
+
+                       void UpdateCell(object sender, PropertyChangedEventArgs e)
                        {
-                               var listview = new ListView {
-                                       HasUnevenRows = true,
-                               };
-                               var items = Enumerable.Range (0, 10);
-                               listview.ItemsSource = items;
-                               listview.ItemTemplate = new DataTemplate (typeof (MyImageCell));
-                               Content = listview;
-                               Title = "Image Cell";
+                               if (e.PropertyName == ImageCell.TextProperty.PropertyName)
+                               {
+                                       (sender as ImageCell).Height += 100;
+                                       ForceUpdateSize();
+                               }
                        }
                }
 
-               public class TextCellPage : ContentPage
+               [Preserve(AllMembers = true)]
+               public class MyTextCell : TextCell
                {
-                       [Preserve (AllMembers = true)]
-                       public class MyTextCell : TextCell
+                       VariableHeightItem _DataItem => BindingContext as VariableHeightItem;
+
+                       public MyTextCell()
+                       {
+                               SetBinding(TextProperty, new Binding(nameof(_DataItem.Text)));
+                               PropertyChanged += UpdateCell;
+                       }
+
+                       void UpdateCell(object sender, PropertyChangedEventArgs e)
                        {
-                               public MyTextCell ()
+                               if (e.PropertyName == TextCell.TextProperty.PropertyName)
                                {
-                                       Text = "I am a TextCell, short and stout.";
-                                       Height = 20;
-                                       Command = new Command (() =>
-                                       {
-                                               Height += 20;
-                                               ForceUpdateSize ();
-                                       });
+                                       (sender as TextCell).Height += 100;
+                                       ForceUpdateSize();
                                }
                        }
+               }
+
+               [Preserve(AllMembers = true)]
+               public class MyEntryCell : EntryCell
+               {
+                       VariableHeightItem _DataItem => BindingContext as VariableHeightItem;
 
-                       public TextCellPage ()
+                       public MyEntryCell()
                        {
-                               var listview = new ListView {
-                                       HasUnevenRows = true,
-                               };
-                               var items = Enumerable.Range (0, 10);
-                               listview.ItemsSource = items;
-                               listview.ItemTemplate = new DataTemplate (typeof (MyTextCell));
-                               Content = listview;
-                               Title = "Text Cell";
+                               SetBinding(TextProperty, new Binding(nameof(_DataItem.Text)));
+                               PropertyChanged += UpdateCell;
+                       }
+
+                       void UpdateCell(object sender, PropertyChangedEventArgs e)
+                       {
+                               if (e.PropertyName == EntryCell.TextProperty.PropertyName)
+                               {
+                                       (sender as EntryCell).Height += 100;
+                                       ForceUpdateSize();
+                               }
                        }
                }
 
-               public class EntryCellPage : ContentPage
+               [Preserve(AllMembers = true)]
+               public class MySwitchCell : SwitchCell
                {
-                       [Preserve (AllMembers = true)]
-                       public class MyEntryCell : EntryCell
+                       VariableHeightItem _DataItem => BindingContext as VariableHeightItem;
+
+                       public MySwitchCell()
+                       {
+                               SetBinding(TextProperty, new Binding(nameof(_DataItem.Text)));
+                               PropertyChanged += UpdateCell;
+                       }
+
+                       void UpdateCell(object sender, PropertyChangedEventArgs e)
                        {
-                               public MyEntryCell ()
+                               if (e.PropertyName == SwitchCell.TextProperty.PropertyName)
                                {
-                                       Text = "I am an EntryCell, short and stout.";
-                                       Height = 20;
-                                       Tapped += (object sender, EventArgs e) =>
-                                       {
-                                               Height += 20;
-                                               ForceUpdateSize ();
-                                       };
-                                       Completed += (object sender, EventArgs e) =>
-                                       {
-                                               Height -= 20;
-                                               ForceUpdateSize ();
-                                       };
+                                       (sender as SwitchCell).Height += 100;
+                                       ForceUpdateSize();
                                }
                        }
+               }
 
-                       public EntryCellPage ()
+               public class MyPage<T> : ContentPage where T : Cell
+               {
+                       private List<VariableHeightItem> _DataSource;
+                       public MyPage(ListViewCachingStrategy strategy)
                        {
-                               var listview = new ListView {
-                                       HasUnevenRows = true,
+                               _DataSource = Enumerable.Range(0, 10).Select(n => new VariableHeightItem()).ToList();
+                               var listView = new ListView(strategy) { HasUnevenRows = true, ItemsSource = _DataSource, ItemTemplate = new DataTemplate(typeof(T)) };
+                               var button = new Button { Text = "Click me" };
+                               button.Clicked += async (sender, e) =>
+                               {
+                                       for (int i = 0; i < _DataSource.Count; i++)
+                                       {
+                                               var target = _DataSource[i];
+
+                                               if (Device.RuntimePlatform == Device.iOS)
+                                               {
+                                                       if (typeof(T) == typeof(MyViewCell))
+                                                               target.Text = "I am an exceptionally long string that should cause the label to wrap, thus increasing the size of the cell such that the entirety of the string is readable by human eyes. Hurrah.";
+                                                       else if (strategy == ListViewCachingStrategy.RetainElement)
+                                                               target.Text = "Look, I'm taller!";
+                                                       else
+                                                               target.Text = $"I'm only taller in {ListViewCachingStrategy.RetainElement} mode. :(";
+                                               }
+                                               else
+                                               {
+                                                       if (typeof(T) == typeof(MyViewCell))
+                                                               target.Text = "I am an exceptionally long string that should cause the label to wrap, thus increasing the size of the cell such that the entirety of the string is readable by human eyes. Hurrah.";
+                                                       else
+                                                               target.Text = "Look, I'm taller!";
+                                               }
+
+                                               await Task.Delay(1000);
+                                       }
                                };
-                               var items = Enumerable.Range (0, 10);
-                               listview.ItemsSource = items;
-                               listview.ItemTemplate = new DataTemplate (typeof (MyEntryCell));
-                               Content = listview;
-                               Title = "Entry Cell";
+
+                               Content = new StackLayout { Children = { button, listView } };
+                               Title = $"{typeof(T).Name} {strategy}";
                        }
                }
 
-               public class SwitchCellPage : ContentPage
+               class VariableHeightItem : ViewModelBase
                {
-                       [Preserve (AllMembers = true)]
-                       public class MySwitchCell : SwitchCell
+                       public VariableHeightItem()
                        {
-                               public MySwitchCell ()
-                               {
-                                       Text = "I am a SwitchCell, short and stout.";
-                                       Height = 20;
-                                       Tapped += (object sender, EventArgs e) =>
-                                       {
-                                               Height += 20;
-                                               ForceUpdateSize ();
-                                       };
-                                       OnChanged += (object sender, ToggledEventArgs e) =>
-                                       {
-                                               Height -= 20;
-                                               ForceUpdateSize ();
-                                       };
-                               }
+                               Text = "This is a line of text.";
                        }
 
-                       public SwitchCellPage ()
+                       public string Text
                        {
-                               var listview = new ListView {
-                                       HasUnevenRows = true,
-                               };
-                               var items = Enumerable.Range (0, 10);
-                               listview.ItemsSource = items;
-                               listview.ItemTemplate = new DataTemplate (typeof (MySwitchCell));
-                               Content = listview;
-                               Title = "Switch Cell";
+                               get { return GetProperty<string>(); }
+                               set { SetProperty(value); }
                        }
                }
 
-               public CellForceUpdateSizeGalleryPage ()
+               public CellForceUpdateSizeGalleryPage()
                {
-                       Children.Add (new ViewCellPage ());
-                       Children.Add (new ImageCellPage ());
-                       Children.Add (new TextCellPage ());
-                       Children.Add (new EntryCellPage ());
-                       Children.Add (new SwitchCellPage ());
+                       var stackLayout = new StackLayout();
+
+                       stackLayout.Children.Add(GetButton<MyViewCell>(ListViewCachingStrategy.RetainElement));
+                       stackLayout.Children.Add(GetButton<MyImageCell>(ListViewCachingStrategy.RetainElement));
+                       stackLayout.Children.Add(GetButton<MyEntryCell>(ListViewCachingStrategy.RetainElement));
+                       stackLayout.Children.Add(GetButton<MyTextCell>(ListViewCachingStrategy.RetainElement));
+                       stackLayout.Children.Add(GetButton<MySwitchCell>(ListViewCachingStrategy.RetainElement));
+
+                       stackLayout.Children.Add(GetButton<MyViewCell>(ListViewCachingStrategy.RecycleElement));
+                       stackLayout.Children.Add(GetButton<MyImageCell>(ListViewCachingStrategy.RecycleElement));
+                       stackLayout.Children.Add(GetButton<MyEntryCell>(ListViewCachingStrategy.RecycleElement));
+                       stackLayout.Children.Add(GetButton<MyTextCell>(ListViewCachingStrategy.RecycleElement));
+                       stackLayout.Children.Add(GetButton<MySwitchCell>(ListViewCachingStrategy.RecycleElement));
+
+                       Navigation.PushAsync(new ContentPage { Content = stackLayout });
+               }
+
+               Button GetButton<T>(ListViewCachingStrategy strategy) where T : Cell
+               {
+                       return new Button
+                       {
+                               Text = $"{typeof(T).Name} {strategy}",
+                               Command = new Command(async () => await Navigation.PushAsync(new MyPage<T>(strategy)))
+                       };
                }
        }
 }
index ea0e8d3..0abf7f8 100644 (file)
@@ -48,9 +48,9 @@ namespace Xamarin.Forms.Platform.iOS
                {
                        cell.ForceUpdateSizeRequested -= _onForceUpdateSizeRequested;
 
-                       _onForceUpdateSizeRequested = (sender, e) => 
+                       _onForceUpdateSizeRequested = (sender, e) =>
                        {
-                               var index = tableView.IndexPathForCell(nativeCell);
+                               var index = tableView?.IndexPathForCell(nativeCell) ?? (sender as Cell)?.GetIndexPath();
                                if (index != null)
                                        tableView.ReloadRows(new[] { index }, UITableViewRowAnimation.None);
                        };