Преглед изворни кода

Refresh containers when relevant property changes.

When `ItemsControl.DisplayMemberBinding` or `ItemTemplate` changes, refresh the containers.
Steven Kirk пре 2 година
родитељ
комит
000f393be7

+ 10 - 0
src/Avalonia.Controls/ItemsControl.cs

@@ -426,12 +426,14 @@ namespace Avalonia.Controls
             {
                 if (change.NewValue is not null && DisplayMemberBinding is not null)
                     throw new InvalidOperationException("Cannot set both DisplayMemberBinding and ItemTemplate.");
+                RefreshContainers();
             }
             else if (change.Property == DisplayMemberBindingProperty)
             {
                 if (change.NewValue is not null && ItemTemplate is not null)
                     throw new InvalidOperationException("Cannot set both DisplayMemberBinding and ItemTemplate.");
                 _displayMemberItemTemplate = null;
+                RefreshContainers();
             }
         }
 
@@ -455,6 +457,14 @@ namespace Avalonia.Controls
             SubscribeToItems(newValue);
         }
 
+        /// <summary>
+        /// Refreshes the containers displayed by the control.
+        /// </summary>
+        /// <remarks>
+        /// Causes all containers to be unrealized and re-realized.
+        /// </remarks>
+        protected void RefreshContainers() => Presenter?.Refresh();
+
         /// <summary>
         /// Called when the <see cref="INotifyCollectionChanged.CollectionChanged"/> event is
         /// raised on <see cref="Items"/>.

+ 8 - 0
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

@@ -84,6 +84,14 @@ namespace Avalonia.Controls.Presenters
             }
         }
 
+        internal void Refresh()
+        {
+            if (Panel is VirtualizingPanel v)
+                v.Refresh();
+            else
+                _generator?.Refresh();
+        }
+
         private void ResetState()
         {
             _generator?.Dispose();

+ 2 - 0
src/Avalonia.Controls/Presenters/PanelContainerGenerator.cs

@@ -45,6 +45,8 @@ namespace Avalonia.Controls.Presenters
             _presenter.Panel?.Children.Clear();
         }
 
+        internal void Refresh() => OnItemsChanged(null, CollectionUtils.ResetEventArgs);
+
         private void OnItemsControlPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
         {
             if (e.Property == ItemsControl.ItemsProperty)

+ 3 - 1
src/Avalonia.Controls/VirtualizingPanel.cs

@@ -176,6 +176,8 @@ namespace Avalonia.Controls
             Children.Clear();
         }
 
+        internal void Refresh() => OnItemsControlItemsChanged(null, CollectionUtils.ResetEventArgs);
+
         private ItemsControl EnsureItemsControl()
         {
             if (ItemsControl is null)
@@ -189,7 +191,7 @@ namespace Avalonia.Controls
             {
                 if (e.OldValue is INotifyCollectionChanged inccOld)
                     inccOld.CollectionChanged -= OnItemsControlItemsChanged;
-                OnItemsControlItemsChanged(null, CollectionUtils.ResetEventArgs);
+                Refresh();
                 if (e.NewValue is INotifyCollectionChanged inccNew)
                     inccNew.CollectionChanged += OnItemsControlItemsChanged;
             }

+ 58 - 0
tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs

@@ -35,6 +35,31 @@ namespace Avalonia.Controls.UnitTests
             Assert.IsType<Canvas>(container.Child);
         }
 
+        [Fact]
+        public void ItemTemplate_Can_Be_Changed()
+        {
+            var target = new ItemsControl
+            {
+                Template = GetTemplate(),
+                ItemTemplate = new FuncDataTemplate<string>((_, __) => new Canvas()),
+            };
+
+            target.Items = new[] { "Foo" };
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            var container = (ContentPresenter)target.Presenter.Panel.Children[0];
+            container.UpdateChild();
+
+            Assert.IsType<Canvas>(container.Child);
+
+            target.ItemTemplate = new FuncDataTemplate<string>((_, __) => new Border());
+            container = (ContentPresenter)target.Presenter.Panel.Children[0];
+            container.UpdateChild();
+
+            Assert.IsType<Border>(container.Child);
+        }
+
         [Fact]
         public void Panel_Should_Have_TemplatedParent_Set_To_ItemsControl()
         {
@@ -708,6 +733,32 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal(container.Child!.GetValue(TextBlock.TextProperty), "3");
         }
 
+        [Fact]
+        public void DisplayMemberBinding_Can_Be_Changed()
+        {
+            var target = new ItemsControl
+            {
+                Template = GetTemplate(),
+                DisplayMemberBinding = new Binding("Value")
+            };
+
+            target.Items = new[] { new Item("Foo", "Bar") };
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            var container = (ContentPresenter)target.Presenter.Panel.Children[0];
+            container.UpdateChild();
+
+            Assert.Equal(container.Child!.GetValue(TextBlock.TextProperty), "Bar");
+
+            target.DisplayMemberBinding = new Binding("Caption");
+            
+            container = (ContentPresenter)target.Presenter.Panel.Children[0];
+            container.UpdateChild();
+
+            Assert.Equal(container.Child!.GetValue(TextBlock.TextProperty), "Foo");
+        }
+
         [Fact]
         public void Cannot_Set_Both_DisplayMemberBinding_And_ItemTemplate_1()
         {
@@ -740,6 +791,13 @@ namespace Avalonia.Controls.UnitTests
                 Value = value;
             }
 
+            public Item(string caption, string value)
+            {
+                Caption = caption;
+                Value = value;
+            }
+
+            public string Caption { get; }
             public string Value { get; }
         }