diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index b6d74da06e..31f8f31875 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/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); } + /// + /// Refreshes the containers displayed by the control. + /// + /// + /// Causes all containers to be unrealized and re-realized. + /// + protected void RefreshContainers() => Presenter?.Refresh(); + /// /// Called when the event is /// raised on . diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs b/src/Avalonia.Controls/Presenters/ItemsPresenter.cs index 6f27349cc1..f11d803fc5 100644 --- a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs +++ b/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(); diff --git a/src/Avalonia.Controls/Presenters/PanelContainerGenerator.cs b/src/Avalonia.Controls/Presenters/PanelContainerGenerator.cs index f71a6c8c1b..8d1e7f0b96 100644 --- a/src/Avalonia.Controls/Presenters/PanelContainerGenerator.cs +++ b/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) diff --git a/src/Avalonia.Controls/VirtualizingPanel.cs b/src/Avalonia.Controls/VirtualizingPanel.cs index 0b7a183728..0d0abe148d 100644 --- a/src/Avalonia.Controls/VirtualizingPanel.cs +++ b/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; } diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs index eeaef3c321..28e88d1e71 100644 --- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs @@ -35,6 +35,31 @@ namespace Avalonia.Controls.UnitTests Assert.IsType(container.Child); } + [Fact] + public void ItemTemplate_Can_Be_Changed() + { + var target = new ItemsControl + { + Template = GetTemplate(), + ItemTemplate = new FuncDataTemplate((_, __) => new Canvas()), + }; + + target.Items = new[] { "Foo" }; + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + var container = (ContentPresenter)target.Presenter.Panel.Children[0]; + container.UpdateChild(); + + Assert.IsType(container.Child); + + target.ItemTemplate = new FuncDataTemplate((_, __) => new Border()); + container = (ContentPresenter)target.Presenter.Panel.Children[0]; + container.UpdateChild(); + + Assert.IsType(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; } }