[UWP] implement Replace without reloading ListView (#3792)
authorShane Neuville <shane94@hotmail.com>
Wed, 19 Sep 2018 18:46:38 +0000 (12:46 -0600)
committerGitHub <noreply@github.com>
Wed, 19 Sep 2018 18:46:38 +0000 (12:46 -0600)
-fixes #3788
* [UWP] implement Replace without reloading ListView
* [UWP] extract bindingcontext of new item for replace

Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue3788.cs [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
Xamarin.Forms.Platform.UAP/ListViewRenderer.cs

diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue3788.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue3788.cs
new file mode 100644 (file)
index 0000000..425a04a
--- /dev/null
@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Text;
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+
+
+#if UITEST
+using Xamarin.UITest;
+using NUnit.Framework;
+using Xamarin.Forms.Core.UITests;
+#endif
+
+namespace Xamarin.Forms.Controls.Issues
+{
+       [Preserve(AllMembers = true)]
+       [Issue(IssueTracker.Github, 3788, "[UWP] ListView with observable collection always seems to refresh the entire list",
+               PlatformAffected.UWP)]
+#if UITEST
+       [NUnit.Framework.Category(UITestCategories.ListView)]
+#endif
+       public class Issue3788 : TestContentPage
+       {
+               const string _replaceMe = "Replace Me";
+               const string _last = "Last";
+               const string _buttonText = "Scroll down and click me";
+
+               protected override void Init()
+               {
+                       ChangingList data =
+                               new ChangingList(Enumerable.Range(0, 1000).Select(_ => new TestModel()));
+
+                       data.Add(new TestModel() { Text = _replaceMe });
+                       ListView view = new ListView();
+
+                       view.ItemTemplate = new DataTemplate(() =>
+                       {
+                               ViewCell cell = new ViewCell();
+                               Label label = new Label();
+                               label.SetBinding(Label.TextProperty, "Text");
+                               cell.View = label;
+                               return cell;
+                       });
+
+                       view.ItemsSource = data;
+                       view.VerticalOptions = LayoutOptions.StartAndExpand;
+                       view.ScrollTo(data.Last(), ScrollToPosition.End, false);
+                       Content = new StackLayout()
+                       {
+                               Children =
+                                       {
+                                               view,
+                                               new Button()
+                                               {
+                                                       Text = _buttonText,
+                                                       Command = new Command(() =>
+                                                       {
+                                                               data.Test();
+                                                       })
+                                               }
+                                       }
+                       };
+               }
+
+               [Preserve(AllMembers = true)]
+               public class ChangingList : List<TestModel>, INotifyCollectionChanged
+               {
+                       public ChangingList(IEnumerable<TestModel> collection) : base(collection)
+                       {
+                       }
+
+                       public event NotifyCollectionChangedEventHandler CollectionChanged;
+
+                       public void Test()
+                       {
+                               var oldItem = this[this.Count - 1];
+                               this[this.Count - 1] = new TestModel() { Text = _last };
+                               CollectionChanged?.Invoke(this,
+                                       new NotifyCollectionChangedEventArgs(
+                                                       NotifyCollectionChangedAction.Replace,
+                                                       this[this.Count - 1], oldItem, this.Count - 1
+                                               ));
+                       }
+               }
+
+               [Preserve(AllMembers = true)]
+               public class TestModel
+               {
+                       public string Text { get; set; } = Guid.NewGuid().ToString();
+                       public override string ToString()
+                       {
+                               return Text;
+                       }
+               }
+
+#if UITEST
+               [Test]
+               public void ReplaceItemScrollsListToTop()
+               {
+                       RunningApp.WaitForElement(_replaceMe);
+                       RunningApp.Tap(_buttonText);
+                       RunningApp.WaitForElement(_last);
+               }
+#endif
+       }
+}
index 08a3872..449e6e7 100644 (file)
@@ -9,6 +9,7 @@
     <Import_RootNamespace>Xamarin.Forms.Controls.Issues</Import_RootNamespace>
   </PropertyGroup>
   <ItemGroup>
+    <Compile Include="$(MSBuildThisFileDirectory)Issue3788.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue2894.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue3524.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue2004.cs" />
index 37f80a3..20ce034 100644 (file)
@@ -64,7 +64,7 @@ namespace Xamarin.Forms.Platform.UWP
                                                GroupStyleSelector = (GroupStyleSelector)WApp.Current.Resources["ListViewGroupSelector"]
                                        };
 
-                                       List.SelectionChanged += OnControlSelectionChanged;     
+                                       List.SelectionChanged += OnControlSelectionChanged;
                                }
 
                                ReloadData();
@@ -117,7 +117,7 @@ namespace Xamarin.Forms.Platform.UWP
                                Source = _collection,
                                IsSourceGrouped = Element.IsGroupingEnabled
                        };
-                       
+
                        List.ItemsSource = _collectionViewSource.View;
                }
 
@@ -152,22 +152,34 @@ namespace Xamarin.Forms.Platform.UWP
                                                        _collection.RemoveAt(e.OldStartingIndex);
                                                break;
                                        case NotifyCollectionChangedAction.Move:
-                                               for (var i = 0; i < e.OldItems.Count; i++)
                                                {
-                                                       var oldi = e.OldStartingIndex;
-                                                       var newi = e.NewStartingIndex;
+                                                       var collection = (ObservableCollection<object>)_collection;
+                                                       for (var i = 0; i < e.OldItems.Count; i++)
+                                                       {
+                                                               var oldi = e.OldStartingIndex;
+                                                               var newi = e.NewStartingIndex;
 
-                                                       if (e.NewStartingIndex < e.OldStartingIndex)
+                                                               if (e.NewStartingIndex < e.OldStartingIndex)
+                                                               {
+                                                                       oldi += i;
+                                                                       newi += i;
+                                                               }
+
+                                                               collection.Move(oldi, newi);
+                                                       }
+                                               }
+                                               break;
+                                       case NotifyCollectionChangedAction.Replace:
+                                               {
+                                                       var collection = (ObservableCollection<object>)_collection;
+                                                       var newi = e.NewStartingIndex;
+                                                       for (var i = 0; i < e.NewItems.Count; i++)
                                                        {
-                                                               oldi += i;
                                                                newi += i;
+                                                               collection[newi] = (e.NewItems[i] as BindableObject).BindingContext;
                                                        }
-
-                                                       // we know that wrapped collection is an ObservableCollection<object>
-                                                       ((ObservableCollection<object>)_collection).Move(oldi, newi);
                                                }
                                                break;
-                                       case NotifyCollectionChangedAction.Replace:
                                        case NotifyCollectionChangedAction.Reset:
                                        default:
                                                ClearSizeEstimate();