Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ItemsControl container generation and implement smooth virtualization #9677

Merged
merged 70 commits into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
692c0c1
Begin refactoring ItemsPresenter.
grokys Nov 24, 2022
c20e3c2
Initial (re)impl of VirtualizingStackPanel.
grokys Nov 25, 2022
5d1f9f4
Removed VirtualizationMode.
grokys Nov 25, 2022
1101f28
Refactored ItemContainerGenerator.
grokys Nov 28, 2022
57b1ba9
Implement INavigableContainer.
grokys Nov 28, 2022
e07e5ce
Fix container generation logic.
grokys Nov 29, 2022
a1d809b
Fix VirtualizingStackPanel navigation logic.
grokys Nov 29, 2022
b3982f1
Create correct TabControl containers.
grokys Nov 29, 2022
fab8f15
Make a few more tests pass.
grokys Nov 29, 2022
14d429e
Added ItemsControl.GetRealizedContainers().
grokys Nov 29, 2022
bbd3d2a
Only remove generated logical children.
grokys Nov 30, 2022
a9ca852
Notify ItemsControl when container index changes.
grokys Nov 30, 2022
c1ee26c
Fixed SelectingItemsControlTests.
grokys Nov 30, 2022
6626a2e
Update TabControl on container index change.
grokys Dec 1, 2022
8bb1349
Setup TabItems.
grokys Dec 1, 2022
2c4572a
Attach to panel before preparing container.
grokys Dec 1, 2022
7e1068f
Fix test.
grokys Dec 1, 2022
004103a
Reimplement DisplayMemberBinding.
grokys Dec 1, 2022
a6423b2
Fix failing test.
grokys Dec 1, 2022
2ae5c9c
Reimplement IChildIndexProvider.
grokys Dec 1, 2022
000f393
Refresh containers when relevant property changes.
grokys Dec 1, 2022
1950e4b
Remove TabControl.HeaderDisplayMemberBinding.
grokys Dec 1, 2022
306c045
Reinstate some commented-out code.
grokys Dec 1, 2022
a7a7103
Remove unneeded tests.
grokys Dec 1, 2022
6f04196
Made TreeView work again and tests pass.
grokys Dec 2, 2022
4a92fad
Fix StyleIncludeTests.
grokys Dec 4, 2022
84abb69
Reinstated DialogsPage.
grokys Dec 4, 2022
56db1db
Remove unneeded change handler.
grokys Dec 4, 2022
c99307c
Use more consistent naming.
grokys Dec 7, 2022
be2da5c
Reimplemented commented-out code.
grokys Dec 7, 2022
e7f3b23
Implement ILogicalScrollable on ItemsPresenter.
grokys Dec 7, 2022
08f90dc
Make logic a bit clearer.
grokys Dec 7, 2022
f0c89a6
Reimplemented Carousel.
grokys Dec 9, 2022
c54e466
Remove `FillMode` on transitions.
grokys Dec 9, 2022
06de4bb
Update comment.
grokys Dec 9, 2022
627d9c5
Make VirtualizationDemo compile again.
grokys Dec 9, 2022
3195df0
Use ItemsSourceView in ItemsControl.
grokys Dec 9, 2022
b678339
TreeNodeCollection needs to implement IList now.
grokys Dec 9, 2022
518391b
Make ItemsSourceView ctors private.
grokys Dec 9, 2022
f042127
Implement a few required members.
grokys Dec 9, 2022
d4a8b98
Invalidate measure less.
grokys Dec 9, 2022
bcb3a6d
Fix side panel width.
grokys Dec 9, 2022
395a90d
Create containers for remaining ItemsControls.
grokys Dec 9, 2022
0b3aa3a
Prepare new containers before adding to tree.
grokys Dec 9, 2022
67d200c
Fix menu theming.
grokys Dec 9, 2022
14df77a
Improve support for variable-sized items.
grokys Dec 9, 2022
6d267f9
Add scroll anchoring support to ItemsControl.
grokys Dec 10, 2022
95e854a
Mark methods obsolete.
grokys Dec 10, 2022
d7201d2
Removed IItemsPresenterHost.
grokys Dec 10, 2022
d5a585d
Tweaked VirtualizingPanel API.
grokys Dec 10, 2022
9610c80
Remove references to "model index".
grokys Dec 10, 2022
3c59202
Merge branch 'master' into refactor/itemcontainergenerator
grokys Dec 12, 2022
f371f18
Use correct submodule commit.
grokys Dec 12, 2022
9781a13
Remove Smooth suffix.
grokys Dec 12, 2022
c55b7a9
Correctly handle control items being removed.
grokys Dec 12, 2022
76b51cf
Fix property name.
grokys Dec 12, 2022
54bc9dd
Fix adorners in DevTools.
grokys Dec 13, 2022
039fb90
Handle item move and replace.
grokys Dec 13, 2022
17f7541
Fix selection in combination with move.
grokys Dec 13, 2022
da14232
Merge branch 'master' into refactor/itemcontainergenerator
grokys Dec 13, 2022
2b02afc
Added ItemsPanelRoot for WPF/UWP compat.
grokys Dec 13, 2022
f5e0869
Merge branch 'master' into refactor/itemcontainergenerator
maxkatz6 Dec 18, 2022
a8df486
Merge branch 'master' into refactor/itemcontainergenerator
grokys Jan 9, 2023
54924fc
Merge branch 'master' into refactor/itemcontainergenerator
grokys Jan 9, 2023
0dcf836
Remove debug code.
grokys Jan 9, 2023
1ef4df1
Fix nits.
grokys Jan 9, 2023
a9fd9f4
A few more tests.
grokys Jan 13, 2023
8600f30
Add redundancy for a problem I can't repro in tests.
grokys Jan 13, 2023
d0f479d
Don't recycle focused element.
grokys Jan 13, 2023
17e1d37
Merge branch 'master' into refactor/itemcontainergenerator
wieslawsoltes Jan 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions samples/ControlCatalog/Pages/CarouselPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</Button>
<Carousel Name="carousel" Grid.Column="1">
<Carousel.PageTransition>
<PageSlide Duration="0.25" Orientation="Vertical" />
<PageSlide Duration="0.25" Orientation="Horizontal" />
</Carousel.PageTransition>
<Image Source="/Assets/delicate-arch-896885_640.jpg"/>
<Image Source="/Assets/hirsch-899118_640.jpg"/>
Expand All @@ -35,7 +35,7 @@

<StackPanel Orientation="Horizontal" Spacing="4">
<TextBlock VerticalAlignment="Center">Orientation</TextBlock>
<ComboBox Name="orientation" SelectedIndex="1" VerticalAlignment="Center">
<ComboBox Name="orientation" SelectedIndex="0" VerticalAlignment="Center">
<ComboBoxItem>Horizontal</ComboBoxItem>
<ComboBoxItem>Vertical</ComboBoxItem>
</ComboBox>
Expand Down
2 changes: 1 addition & 1 deletion samples/ControlCatalog/Pages/DialogsPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
Classes="h2"
IsVisible="False"
Text="Last picker results:" />
<ItemsPresenter x:Name="PickerLastResults" />
<ItemsControl x:Name="PickerLastResults" />

<TextBox Name="BookmarkContainer" Watermark="Bookmark" />
<TextBox Name="OpenedFileContent"
Expand Down
60 changes: 30 additions & 30 deletions samples/ControlCatalog/Pages/DialogsPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public DialogsPage()
{
this.InitializeComponent();

var results = this.Get<ItemsPresenter>("PickerLastResults");
var results = this.Get<ItemsControl>("PickerLastResults");
var resultsVisible = this.Get<TextBlock>("PickerLastResultsVisible");
var bookmarkContainer = this.Get<TextBox>("BookmarkContainer");
var openedFileContent = this.Get<TextBox>("OpenedFileContent");
Expand All @@ -36,29 +36,29 @@ List<FileDialogFilter> GetFilters()
{
if (this.Get<CheckBox>("UseFilters").IsChecked != true)
return new List<FileDialogFilter>();
return new List<FileDialogFilter>
{
new FileDialogFilter
{
Name = "Text files (.txt)", Extensions = new List<string> {"txt"}
},
new FileDialogFilter
{
Name = "All files",
Extensions = new List<string> {"*"}
}
};
return new List<FileDialogFilter>
{
new FileDialogFilter
{
Name = "Text files (.txt)", Extensions = new List<string> {"txt"}
},
new FileDialogFilter
{
Name = "All files",
Extensions = new List<string> {"*"}
}
};
}

List<FilePickerFileType>? GetFileTypes()
{
if (this.Get<CheckBox>("UseFilters").IsChecked != true)
return null;
return new List<FilePickerFileType>
{
FilePickerFileTypes.All,
FilePickerFileTypes.TextPlain
};
{
FilePickerFileTypes.All,
FilePickerFileTypes.TextPlain
};
}

this.Get<Button>("OpenFile").Click += async delegate
Expand Down Expand Up @@ -205,15 +205,15 @@ List<FileDialogFilter> GetFilters()
await using var stream = await file.OpenWriteAsync();
await using var reader = new System.IO.StreamWriter(stream);
#else
using var stream = await file.OpenWriteAsync();
using var reader = new System.IO.StreamWriter(stream);
using var stream = await file.OpenWriteAsync();
using var reader = new System.IO.StreamWriter(stream);
#endif
await reader.WriteLineAsync(openedFileContent.Text);

lastSelectedDirectory = await file.GetParentAsync();
}

await SetPickerResult(file is null ? null : new [] {file});
await SetPickerResult(file is null ? null : new[] { file });
};
this.Get<Button>("OpenFolderPicker").Click += async delegate
{
Expand Down Expand Up @@ -243,7 +243,7 @@ List<FileDialogFilter> GetFilters()
: null;

await SetPickerResult(folder is null ? null : new[] { folder });

lastSelectedDirectory = folder;
};

Expand All @@ -260,23 +260,23 @@ async Task SetPickerResult(IReadOnlyCollection<IStorageItem>? items)

var props = await item.GetBasicPropertiesAsync();
resultText += @$"Size: {props.Size}
DateCreated: {props.DateCreated}
DateModified: {props.DateModified}
CanBookmark: {item.CanBookmark}
";
DateCreated: {props.DateCreated}
DateModified: {props.DateModified}
CanBookmark: {item.CanBookmark}
";
if (item is IStorageFile file)
{
resultText += @$"
CanOpenRead: {file.CanOpenRead}
CanOpenWrite: {file.CanOpenWrite}
Content:
";
CanOpenRead: {file.CanOpenRead}
CanOpenWrite: {file.CanOpenWrite}
Content:
";
if (file.CanOpenRead)
{
#if NET6_0_OR_GREATER
await using var stream = await file.OpenReadAsync();
#else
using var stream = await file.OpenReadAsync();
using var stream = await file.OpenReadAsync();
#endif
using var reader = new System.IO.StreamReader(stream);

Expand Down
2 changes: 1 addition & 1 deletion samples/ControlCatalog/Pages/TabControlPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
<TabControl
Items="{Binding Tabs}"
Margin="0 16"
HeaderDisplayMemberBinding="{Binding Header, x:DataType=viewModels:TabControlPageViewModelItem}"
DisplayMemberBinding="{Binding Header, x:DataType=viewModels:TabControlPageViewModelItem}"
TabStripPlacement="{Binding TabPlacement}">
<TabControl.ContentTemplate>
<DataTemplate x:DataType="viewModels:TabControlPageViewModelItem">
Expand Down
4 changes: 1 addition & 3 deletions samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,7 @@
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
<ItemsPresenter Name="PART_ItemsPresenter"
HorizontalAlignment="Stretch"
ItemTemplate="{TemplateBinding ItemTemplate}"
Items="{TemplateBinding Items}">
HorizontalAlignment="Stretch">
<ItemsPresenter.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel x:Name="HamburgerItemsPanel"
Expand Down
5 changes: 1 addition & 4 deletions samples/VirtualizationDemo/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@
<DockPanel LastChildFill="True" Margin="16">
<StackPanel DockPanel.Dock="Right"
Margin="16 0 0 0"
MinWidth="150"
Width="150"
Spacing="4">
<ComboBox Items="{Binding VirtualizationModes}"
SelectedItem="{Binding VirtualizationMode}"/>
<ComboBox Items="{Binding Orientations}"
SelectedItem="{Binding Orientation}"/>
<TextBox Watermark="Item Count"
Expand Down Expand Up @@ -49,7 +47,6 @@
Items="{Binding Items}"
Selection="{Binding Selection}"
SelectionMode="Multiple"
VirtualizationMode="{Binding VirtualizationMode}"
ScrollViewer.HorizontalScrollBarVisibility="{Binding HorizontalScrollBarVisibility, Mode=TwoWay}"
ScrollViewer.VerticalScrollBarVisibility="{Binding VerticalScrollBarVisibility, Mode=TwoWay}">
<ListBox.ItemsPanel>
Expand Down
10 changes: 0 additions & 10 deletions samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ internal class MainWindowViewModel : ViewModelBase
private ScrollBarVisibility _horizontalScrollBarVisibility = ScrollBarVisibility.Auto;
private ScrollBarVisibility _verticalScrollBarVisibility = ScrollBarVisibility.Auto;
private Orientation _orientation = Orientation.Vertical;
private ItemVirtualizationMode _virtualizationMode = ItemVirtualizationMode.Simple;

public MainWindowViewModel()
{
Expand Down Expand Up @@ -81,15 +80,6 @@ public ScrollBarVisibility VerticalScrollBarVisibility
public IEnumerable<ScrollBarVisibility> ScrollBarVisibilities =>
Enum.GetValues(typeof(ScrollBarVisibility)).Cast<ScrollBarVisibility>();

public ItemVirtualizationMode VirtualizationMode
{
get { return _virtualizationMode; }
set { this.RaiseAndSetIfChanged(ref _virtualizationMode, value); }
}

public IEnumerable<ItemVirtualizationMode> VirtualizationModes =>
Enum.GetValues(typeof(ItemVirtualizationMode)).Cast<ItemVirtualizationMode>();

public MiniCommand AddItemCommand { get; private set; }
public MiniCommand RecreateCommand { get; private set; }
public MiniCommand RemoveItemCommand { get; private set; }
Expand Down
2 changes: 0 additions & 2 deletions src/Avalonia.Base/Animation/PageSlide.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ public virtual async Task Start(Visual? from, Visual? to, bool forward, Cancella
var animation = new Animation
{
Easing = SlideOutEasing,
FillMode = FillMode.Forward,
Children =
{
new KeyFrame
Expand Down Expand Up @@ -110,7 +109,6 @@ public virtual async Task Start(Visual? from, Visual? to, bool forward, Cancella
to.IsVisible = true;
var animation = new Animation
{
FillMode = FillMode.Forward,
Easing = SlideInEasing,
Children =
{
Expand Down
2 changes: 0 additions & 2 deletions src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ KeyFrame CreateKeyFrame(double cue, double rotation, int zIndex, bool isVisible
{
Easing = SlideOutEasing,
Duration = Duration,
FillMode = FillMode.Forward,
Children =
{
CreateKeyFrame(0d, 0d, 2),
Expand All @@ -90,7 +89,6 @@ KeyFrame CreateKeyFrame(double cue, double rotation, int zIndex, bool isVisible
{
Easing = SlideInEasing,
Duration = Duration,
FillMode = FillMode.Forward,
Children =
{
CreateKeyFrame(0d, 90d * (forward ? 1 : -1), 1),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,8 @@
IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}"
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}">
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
ItemTemplate="{TemplateBinding ItemTemplate}"
Margin="{TemplateBinding Padding}"
VirtualizationMode="{TemplateBinding VirtualizationMode}" />
Margin="{TemplateBinding Padding}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,8 @@
IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}"
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}">
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
ItemTemplate="{TemplateBinding ItemTemplate}"
Margin="{TemplateBinding Padding}"
VirtualizationMode="{TemplateBinding VirtualizationMode}" />
Margin="{TemplateBinding Padding}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
Expand Down
52 changes: 31 additions & 21 deletions src/Avalonia.Controls/Carousel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils;
using Avalonia.Input;

namespace Avalonia.Controls
{
Expand All @@ -11,12 +10,6 @@ namespace Avalonia.Controls
/// </summary>
public class Carousel : SelectingItemsControl
{
/// <summary>
/// Defines the <see cref="IsVirtualized"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsVirtualizedProperty =
AvaloniaProperty.Register<Carousel, bool>(nameof(IsVirtualized), true);

/// <summary>
/// Defines the <see cref="PageTransition"/> property.
/// </summary>
Expand All @@ -28,7 +21,9 @@ public class Carousel : SelectingItemsControl
/// <see cref="Carousel"/>.
/// </summary>
private static readonly ITemplate<Panel> PanelTemplate =
new FuncTemplate<Panel>(() => new Panel());
new FuncTemplate<Panel>(() => new VirtualizingCarouselPanel());

private IScrollable? _scroller;

/// <summary>
/// Initializes static members of the <see cref="Carousel"/> class.
Expand All @@ -38,18 +33,6 @@ static Carousel()
SelectionModeProperty.OverrideDefaultValue<Carousel>(SelectionMode.AlwaysSelected);
ItemsPanelProperty.OverrideDefaultValue<Carousel>(PanelTemplate);
}

/// <summary>
/// Gets or sets a value indicating whether the items in the carousel are virtualized.
/// </summary>
/// <remarks>
/// When the carousel is virtualized, only the active page is held in memory.
/// </remarks>
public bool IsVirtualized
{
get { return GetValue(IsVirtualizedProperty); }
set { SetValue(IsVirtualizedProperty, value); }
}

/// <summary>
/// Gets or sets the transition to use when moving between pages.
Expand All @@ -65,7 +48,7 @@ public IPageTransition? PageTransition
/// </summary>
public void Next()
{
if (SelectedIndex < Items.Count() - 1)
if (SelectedIndex < ItemCount - 1)
{
++SelectedIndex;
}
Expand All @@ -81,5 +64,32 @@ public void Previous()
--SelectedIndex;
}
}

protected override Size ArrangeOverride(Size finalSize)
{
var result = base.ArrangeOverride(finalSize);

if (_scroller is not null)
_scroller.Offset = new(SelectedIndex, 0);

return result;
}

protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_scroller = e.NameScope.Find<IScrollable>("PART_ScrollViewer");
}

protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);

if (change.Property == SelectedIndexProperty && _scroller is not null)
{
var value = change.GetNewValue<int>();
_scroller.Offset = new(value, 0);
}
}
}
}
Loading