浏览代码

Merge pull request #1639 from AvaloniaUI/fixes/1569-carousel-collection-changed-handling

Handle all CollectionChanged cases in CarouselPresenter.
danwalmsley 7 年之前
父节点
当前提交
6c13dee48d
共有 2 个文件被更改,包括 182 次插入2 次删除
  1. 12 1
      src/Avalonia.Controls/Presenters/CarouselPresenter.cs
  2. 170 1
      tests/Avalonia.Controls.UnitTests/CarouselTests.cs

+ 12 - 1
src/Avalonia.Controls/Presenters/CarouselPresenter.cs

@@ -106,7 +106,6 @@ namespace Avalonia.Controls.Presenters
         /// <inheritdoc/>
         protected override void ItemsChanged(NotifyCollectionChangedEventArgs e)
         {
-            // TODO: Handle items changing.           
             switch (e.Action)
             {
                 case NotifyCollectionChangedAction.Remove:
@@ -115,9 +114,21 @@ namespace Avalonia.Controls.Presenters
                         var generator = ItemContainerGenerator;
                         var containers = generator.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
                         Panel.Children.RemoveAll(containers.Select(x => x.ContainerControl));
+
+                        MoveToPage(-1, SelectedIndex);
                     }
                     break;
 
+                case NotifyCollectionChangedAction.Reset:
+                    {
+                        var generator = ItemContainerGenerator;
+                        var containers = generator.Containers.ToList();
+                        generator.Clear();
+                        Panel.Children.RemoveAll(containers.Select(x => x.ContainerControl));
+
+                        MoveToPage(-1, SelectedIndex >= 0 ? SelectedIndex : 0);
+                    }
+                    break;     
             }
         }
 

+ 170 - 1
tests/Avalonia.Controls.UnitTests/CarouselTests.cs

@@ -1,11 +1,13 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
+using System.Collections.ObjectModel;
 using System.Linq;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
 using Avalonia.LogicalTree;
+using Avalonia.VisualTree;
 using Xunit;
 
 namespace Avalonia.Controls.UnitTests
@@ -50,7 +52,7 @@ namespace Avalonia.Controls.UnitTests
             Assert.Single(target.GetLogicalChildren());
 
             var child = target.GetLogicalChildren().Single();
-            
+
             Assert.IsType<TextBlock>(child);
             Assert.Equal("Foo", ((TextBlock)child).Text);
         }
@@ -93,6 +95,173 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
         }
 
+        [Fact]
+        public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes()
+        {
+            var items = new ObservableCollection<string>
+            {
+               "Foo",
+               "Bar",
+               "FooBar"
+            };
+
+            var target = new Carousel
+            {
+                Template = new FuncControlTemplate<Carousel>(CreateTemplate),
+                Items = items,
+                IsVirtualized = false
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            Assert.Single(target.GetLogicalChildren());
+
+            var child = target.GetLogicalChildren().Single();
+
+            Assert.IsType<TextBlock>(child);
+            Assert.Equal("Foo", ((TextBlock)child).Text);
+
+            var newItems = items.ToList();
+            newItems.RemoveAt(0);
+
+            target.Items = newItems;
+
+            child = target.GetLogicalChildren().Single();
+
+            Assert.IsType<TextBlock>(child);
+            Assert.Equal("Bar", ((TextBlock)child).Text);
+        }
+
+        [Fact]
+        public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes_And_Virtualized()
+        {
+            var items = new ObservableCollection<string>
+            {
+               "Foo",
+               "Bar",
+               "FooBar"
+            };
+
+            var target = new Carousel
+            {
+                Template = new FuncControlTemplate<Carousel>(CreateTemplate),
+                Items = items
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            Assert.Single(target.GetLogicalChildren());
+
+            var child = target.GetLogicalChildren().Single();
+
+            Assert.IsType<TextBlock>(child);
+            Assert.Equal("Foo", ((TextBlock)child).Text);
+
+            var newItems = items.ToList();
+            newItems.RemoveAt(0);
+
+            target.Items = newItems;
+
+            child = target.GetLogicalChildren().Single();
+
+            Assert.IsType<TextBlock>(child);
+            Assert.Equal("Bar", ((TextBlock)child).Text);
+        }
+
+        [Fact]
+        public void Selected_Index_Is_Maintained_Carousel_Created_With_Non_Zero_SelectedIndex()
+        {
+            var items = new ObservableCollection<string>
+            {
+               "Foo",
+               "Bar",
+               "FooBar"
+            };
+
+            var target = new Carousel
+            {
+                Template = new FuncControlTemplate<Carousel>(CreateTemplate),
+                Items = items,
+                IsVirtualized = false,
+                SelectedIndex = 2
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            Assert.Equal("FooBar", target.SelectedItem);
+
+            var child = target.GetVisualDescendants().LastOrDefault();
+
+            Assert.IsType<TextBlock>(child);
+            Assert.Equal("FooBar", ((TextBlock)child).Text);
+        }
+
+        [Fact]
+        public void Selected_Item_Changes_To_Next_First_Item_When_Item_Removed_From_Beggining_Of_List()
+        {
+            var items = new ObservableCollection<string>
+            {
+               "Foo",
+               "Bar",
+               "FooBar"
+            };
+
+            var target = new Carousel
+            {
+                Template = new FuncControlTemplate<Carousel>(CreateTemplate),
+                Items = items,
+                IsVirtualized = false
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            Assert.Single(target.GetLogicalChildren());
+
+            var child = target.GetLogicalChildren().Single();
+
+            Assert.IsType<TextBlock>(child);
+            Assert.Equal("Foo", ((TextBlock)child).Text);
+
+            items.RemoveAt(0);
+
+            child = target.GetLogicalChildren().Single();
+
+            Assert.IsType<TextBlock>(child);
+            Assert.Equal("Bar", ((TextBlock)child).Text);
+        }
+
+        [Fact]
+        public void Selected_Item_Changes_To_NextAvailable_Item_If_SelectedItem_Is_Removed_From_Middle()
+        {
+            var items = new ObservableCollection<string>
+            {
+               "Foo",
+               "Bar",
+               "FooBar"
+            };
+
+            var target = new Carousel
+            {
+                Template = new FuncControlTemplate<Carousel>(CreateTemplate),
+                Items = items,
+                IsVirtualized = false
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            target.SelectedIndex = 1;
+
+            items.RemoveAt(1);
+
+            Assert.Equal(1, target.SelectedIndex);
+            Assert.Equal("FooBar", target.SelectedItem);
+        }
+
         private Control CreateTemplate(Carousel control)
         {
             return new CarouselPresenter