From 8ef5fdfbc1889d91daf9687aa90c1210e3af5231 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 7 Aug 2019 11:21:52 +0200 Subject: [PATCH 1/5] Add "Add Item" button to ItemsRepeaterPage. --- .../Pages/ItemsRepeaterPage.xaml | 5 ++-- .../Pages/ItemsRepeaterPage.xaml.cs | 3 ++- .../ViewModels/ItemsRepeaterPageViewModel.cs | 24 +++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs diff --git a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml index dfe8be2cec..17a00f23fd 100644 --- a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml +++ b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml @@ -6,19 +6,20 @@ ItemsRepeater A data-driven collection control that incorporates a flexible layout system, custom views, and virtualization. - + Stack - Vertical Stack - Horizontal UniformGrid - Vertical UniformGrid - Horizontal + - + diff --git a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs index 214de89253..b56af5d5ea 100644 --- a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs @@ -3,6 +3,7 @@ using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Layout; using Avalonia.Markup.Xaml; +using ControlCatalog.ViewModels; namespace ControlCatalog.Pages { @@ -16,7 +17,7 @@ namespace ControlCatalog.Pages this.InitializeComponent(); _repeater = this.FindControl("repeater"); _scroller = this.FindControl("scroller"); - DataContext = Enumerable.Range(1, 100000).Select(i => $"Item {i}" ).ToArray(); + DataContext = new ItemsRepeaterPageViewModel(); } private void InitializeComponent() diff --git a/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs new file mode 100644 index 0000000000..5304ba1f7d --- /dev/null +++ b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs @@ -0,0 +1,24 @@ +using System.Collections.ObjectModel; +using System.Linq; +using ReactiveUI; + +namespace ControlCatalog.ViewModels +{ + public class ItemsRepeaterPageViewModel : ReactiveObject + { + private int newItemIndex = 1; + + public ItemsRepeaterPageViewModel() + { + Items = new ObservableCollection( + Enumerable.Range(1, 100000).Select(i => $"Item {i}")); + } + + public ObservableCollection Items { get; } + + public void AddItem() + { + Items.Insert(0, $"New Item {newItemIndex++}"); + } + } +} From 5b6a44bd724769dc287c5b829aba4be769ae9d13 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 7 Aug 2019 11:43:37 +0200 Subject: [PATCH 2/5] Fix facepalm in ItemsSourceView. Don't construct a new `List` unless the source isn't already an `IList`. --- src/Avalonia.Controls/Repeater/ItemsSourceView.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ItemsSourceView.cs b/src/Avalonia.Controls/Repeater/ItemsSourceView.cs index 732ba8501c..02ead7ef36 100644 --- a/src/Avalonia.Controls/Repeater/ItemsSourceView.cs +++ b/src/Avalonia.Controls/Repeater/ItemsSourceView.cs @@ -35,9 +35,11 @@ namespace Avalonia.Controls { Contract.Requires(source != null); - _inner = source as IList; - - if (_inner == null && source is IEnumerable objectEnumerable) + if (source is IList list) + { + _inner = list; + } + else if (source is IEnumerable objectEnumerable) { _inner = new List(objectEnumerable); } From e73a5a6357c6dce749baa1ddc81f6efaeedcdd70 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 7 Aug 2019 13:06:30 +0200 Subject: [PATCH 3/5] Fix another facepalm in ItemsRepeater. `Measure` and `Arrange` were swapped. --- src/Avalonia.Controls/Repeater/ItemsRepeater.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs index 44783e2c97..257c1b2399 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs @@ -707,9 +707,9 @@ namespace Avalonia.Controls } } - private void InvalidateArrangeForLayout(object sender, EventArgs e) => InvalidateMeasure(); + private void InvalidateMeasureForLayout(object sender, EventArgs e) => InvalidateMeasure(); - private void InvalidateMeasureForLayout(object sender, EventArgs e) => InvalidateArrange(); + private void InvalidateArrangeForLayout(object sender, EventArgs e) => InvalidateArrange(); private VirtualizingLayoutContext GetLayoutContext() { From 96c1670197aa5bcdd9f0f398097f003a261b56c1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 7 Aug 2019 13:14:34 +0200 Subject: [PATCH 4/5] Remove previous hack. Indroduced in f09683cf93791d9b676983a2e80d6852c63da987. Was probably caused by the issue fixed in e73a5a6357c6dce749baa1ddc81f6efaeedcdd70. --- src/Avalonia.Layout/UniformGridLayoutState.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Avalonia.Layout/UniformGridLayoutState.cs b/src/Avalonia.Layout/UniformGridLayoutState.cs index 4557a78d37..e6d75bcf35 100644 --- a/src/Avalonia.Layout/UniformGridLayoutState.cs +++ b/src/Avalonia.Layout/UniformGridLayoutState.cs @@ -72,12 +72,6 @@ namespace Avalonia.Layout _cachedFirstElement.Measure(availableSize); - // This doesn't need to be done in the UWP version and I'm not sure why. If we - // don't do this here, and we receive a recycled element then it will be shown - // at its previous arrange point, but we don't want it shown at all until its - // arranged. - _cachedFirstElement.Arrange(new Rect(-10000.0, -10000.0, 0, 0)); - SetSize(_cachedFirstElement, layoutItemWidth, LayoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing); // See if we can move ownership to the flow algorithm. If we can, we do not need a local cache. From fa6505123f8488733e71ba2bb12ffe36db142009 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 7 Aug 2019 13:20:26 +0200 Subject: [PATCH 5/5] Allow adding items anywhere in list. By clicking an item in `ItemsRepeater` to select the insertion point. --- samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml | 2 +- samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs | 9 +++++++++ .../ViewModels/ItemsRepeaterPageViewModel.cs | 5 ++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml index 17a00f23fd..d0631d2cbd 100644 --- a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml +++ b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml @@ -19,7 +19,7 @@ - + diff --git a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs index b56af5d5ea..1a607342f3 100644 --- a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs @@ -1,6 +1,8 @@ +using System; using System.Linq; using Avalonia.Controls; using Avalonia.Controls.Primitives; +using Avalonia.Input; using Avalonia.Layout; using Avalonia.Markup.Xaml; using ControlCatalog.ViewModels; @@ -17,6 +19,7 @@ namespace ControlCatalog.Pages this.InitializeComponent(); _repeater = this.FindControl("repeater"); _scroller = this.FindControl("scroller"); + _repeater.PointerPressed += RepeaterClick; DataContext = new ItemsRepeaterPageViewModel(); } @@ -68,5 +71,11 @@ namespace ControlCatalog.Pages break; } } + + private void RepeaterClick(object sender, PointerPressedEventArgs e) + { + var item = (e.Source as TextBlock)?.DataContext as string; + ((ItemsRepeaterPageViewModel)DataContext).SelectedItem = item; + } } } diff --git a/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs index 5304ba1f7d..436a479441 100644 --- a/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs @@ -16,9 +16,12 @@ namespace ControlCatalog.ViewModels public ObservableCollection Items { get; } + public string SelectedItem { get; set; } + public void AddItem() { - Items.Insert(0, $"New Item {newItemIndex++}"); + var index = SelectedItem != null ? Items.IndexOf(SelectedItem) : -1; + Items.Insert(index + 1, $"New Item {newItemIndex++}"); } } }