diff --git a/components/AdvancedCollectionView/OpenSolution.bat b/components/AdvancedCollectionView/OpenSolution.bat new file mode 100644 index 00000000..814a56d4 --- /dev/null +++ b/components/AdvancedCollectionView/OpenSolution.bat @@ -0,0 +1,3 @@ +@ECHO OFF + +powershell ..\..\tooling\ProjectHeads\GenerateSingleSampleHeads.ps1 -componentPath %CD% %* \ No newline at end of file diff --git a/components/AdvancedCollectionView/samples/AdvancedCollectionView.Samples.csproj b/components/AdvancedCollectionView/samples/AdvancedCollectionView.Samples.csproj new file mode 100644 index 00000000..6469f7cf --- /dev/null +++ b/components/AdvancedCollectionView/samples/AdvancedCollectionView.Samples.csproj @@ -0,0 +1,8 @@ + + + AdvancedCollectionView + + + + + diff --git a/components/AdvancedCollectionView/samples/AdvancedCollectionView.md b/components/AdvancedCollectionView/samples/AdvancedCollectionView.md new file mode 100644 index 00000000..c3dfd5da --- /dev/null +++ b/components/AdvancedCollectionView/samples/AdvancedCollectionView.md @@ -0,0 +1,152 @@ +--- +title: AdvancedCollectionView +author: nmetulev +description: The AdvancedCollectionView is a collection view implementation that support filtering, sorting and incremental loading. It's meant to be used in a viewmodel. +keywords: AdvancedCollectionView, data, sorting, filtering +dev_langs: + - csharp +category: Controls +subcategory: Layout +discussion-id: 0 +issue-id: 0 +--- + +# AdvancedCollectionView + +The [AdvancedCollectionView](/dotnet/api/microsoft.toolkit.uwp.ui.advancedcollectionview) is a collection view implementation that support filtering, sorting and incremental loading. It's meant to be used in a viewmodel. + +> [!Sample AdvancedCollectionViewSample] + +## Usage + +In your viewmodel instead of having a public [IEnumerable](/dotnet/core/api/system.collections.generic.ienumerable-1) of some sort to be bound to an eg. [Listview](/uwp/api/Windows.UI.Xaml.Controls.ListView), create a public AdvancedCollectionView and pass your list in the constructor to it. If you've done that you can use the many useful features it provides: + +* sorting your list using the `SortDirection` helper: specify any number of property names to sort on with the direction desired +* filtering your list using a [Predicate](/dotnet/core/api/system.predicate-1): this will automatically filter your list only to the items that pass the check by the predicate provided +* deferring notifications using the `NotificationDeferrer` helper: with a convenient _using_ pattern you can increase performance while doing large-scale modifications in your list by waiting with updates until you've completed your work +* incremental loading: if your source collection supports the feature then AdvancedCollectionView will do as well (it simply forwards the calls) +* live shaping: when constructing the `AdvancedCollectionView` you may specify that the collection use live shaping. This means that the collection will re-filter or re-sort if there are changes to the sort properties or filter properties that are specified using `ObserveFilterProperty` + +## Example + +```csharp +using Microsoft.Toolkit.Uwp.UI; + +// Grab a sample type +public class Person +{ + public string Name { get; set; } +} + +// Set up the original list with a few sample items +var oc = new ObservableCollection +{ + new Person { Name = "Staff" }, + new Person { Name = "42" }, + new Person { Name = "Swan" }, + new Person { Name = "Orchid" }, + new Person { Name = "15" }, + new Person { Name = "Flame" }, + new Person { Name = "16" }, + new Person { Name = "Arrow" }, + new Person { Name = "Tempest" }, + new Person { Name = "23" }, + new Person { Name = "Pearl" }, + new Person { Name = "Hydra" }, + new Person { Name = "Lamp Post" }, + new Person { Name = "4" }, + new Person { Name = "Looking Glass" }, + new Person { Name = "8" }, +}; + +// Set up the AdvancedCollectionView with live shaping enabled to filter and sort the original list +var acv = new AdvancedCollectionView(oc, true); + +// Let's filter out the integers +int nul; +acv.Filter = x => !int.TryParse(((Person)x).Name, out nul); + +// And sort ascending by the property "Name" +acv.SortDescriptions.Add(new SortDescription("Name", SortDirection.Ascending)); + +// Let's add a Person to the observable collection +var person = new Person { Name = "Aardvark" }; +oc.Add(person); + +// Our added person is now at the top of the list, but if we rename this person, we can trigger a re-sort +person.Name = "Zaphod"; // Now a re-sort is triggered and person will be last in the list + +// AdvancedCollectionView can be bound to anything that uses collections. +YourListView.ItemsSource = acv; +``` + +## Properties + +| Property | Type | Description | +| -- | -- | -- | +| CanFilter | bool | Gets a value indicating whether this CollectionView can filter its items | +| CanSort | bool | Gets a value indicating whether this CollectionView can sort its items | +| CollectionGroups | IObservableVector\ | Gets the groups in collection | +| Count | int | Get the count of items | +| CurrentItem | object | Gets or sets the current item | +| CurrentPosition | int | Gets the position of current item | +| Filter | Predicate\ | Gets or sets the predicate used to filter the visible items | +| HasMoreItems | bool | Gets a value indicating whether the source has more items | +| IsCurrentAfterLast | bool | Gets a value indicating whether the current item is after the last visible item | +| IsCurrentBeforeFirst | bool | Gets a value indicating whether the current item is before the first visible item | +| IsReadOnly | bool | Get a value indicating whether this CollectionView is read only | +| SortDescriptions | IList<[SortDescription](/dotnet/api/microsoft.toolkit.uwp.ui.sortdescription)> | Gets SortDescriptions to sort the visible items | +| Source | IEnumerable | Gets or sets the source | +| SourceCollection | IEnumerable | Gets the source collection | +| this[int] | int | Gets or sets the element at the specified index | + +## Methods + +| Methods | Return Type | Description | +| -- | -- | -- | +| Add(Object) | void | Add item | +| Clear() | void | Clear item | +| Contains(Object) | bool | Returns `true` if the given item contained in CollectionView | +| B(float, string) | int | Description | +| DeferRefresh() | IDisposable | Stops refreshing until it is disposed | +| IndexOf(Object) | int | Return index of an item | +| Insert(Int32, Object) | void | Insert an item in a particular place | +| LoadMoreItemsAsync(UInt32) | IAsyncOperation<[LoadMoreItemsResult](/uwp/api/Windows.UI.Xaml.Data.LoadMoreItemsResult)> | Load more items from the source | +| MoveCurrentTo(Object) | bool | Move current index to item. Returns success of operation | +| MoveCurrentToFirst() | bool | Move current item to first item. Returns success of operation | +| MoveCurrentToLast() | bool | Move current item to last item. Returns success of operation | +| MoveCurrentToNext() | bool | Move current item to next item | +| MoveCurrentToPosition(Int32) | bool | Moves selected item to position | +| MoveCurrentToPrevious() | bool | Move current item to previous item | +| Refresh() | void | Manually refresh the view | +| Remove(Object) | bool | Remove item | +| RemoveAt(Int32) | bool | Remove item with index | + +## Events + +| Events | Description | +| -- | -- | +| CurrentChanged | Current item changed event handler | +| CurrentChanging | Current item changing event handler | +| PropertyChanged | Occurs when a property value changes | +| VectorChanged | Occurs when the vector changes | + +## Remarks + +_What source can I use?_ + +It's not necessary to use an eg. [ObservableCollection](/dotnet/core/api/system.collections.objectmodel.observablecollection-1) to use the AdvancedCollectionView. It works as expected even when providing a simple [List](/dotnet/core/api/system.collections.generic.list-1) in the constructor. + +_Any performance guidelines?_ + +If you're removing, modifying or inserting large amounts of items while having filtering and/or sorting set up, it's recommended that you use the `NotificationDeferrer` helper provided. It skips any performance heavy logic while it's in use, and automatically calls the `Refresh` method when disposed. + +```csharp +using (acv.DeferRefresh()) +{ + for (var i = 0; i < 500; i++) + { + acv.Add(new Person { Name = "defer" }); + } +} // acv.Refresh() gets called here +``` diff --git a/components/AdvancedCollectionView/samples/AdvancedCollectionViewSample.cs b/components/AdvancedCollectionView/samples/AdvancedCollectionViewSample.cs new file mode 100644 index 00000000..b9115d59 --- /dev/null +++ b/components/AdvancedCollectionView/samples/AdvancedCollectionViewSample.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using CommunityToolkit.WinUI; + +namespace AdvancedCollectionViewExperiment.Samples; + +[ToolkitSample(id: nameof(AdvancedCollectionViewSample), "AdvancedCollectionView", description: $"A sample for showing how to create and use a {nameof(AdvancedCollectionView)}.")] +public sealed partial class AdvancedCollectionViewSample : Page +{ + public ObservableCollection? oc; + + public AdvancedCollectionViewSample() + { + this.InitializeComponent(); + Setup(); + } + + private void Setup() + { + // left list + oc = new ObservableCollection + { + new Person { Name = "Staff" }, + new Person { Name = "42" }, + new Person { Name = "Swan" }, + new Person { Name = "Orchid" }, + new Person { Name = "15" }, + new Person { Name = "Flame" }, + new Person { Name = "16" }, + new Person { Name = "Arrow" }, + new Person { Name = "Tempest" }, + new Person { Name = "23" }, + new Person { Name = "Pearl" }, + new Person { Name = "Hydra" }, + new Person { Name = "Lamp Post" }, + new Person { Name = "4" }, + new Person { Name = "Looking Glass" }, + new Person { Name = "8" }, + }; + + LeftList.ItemsSource = oc; + + // right list + var acv = new AdvancedCollectionView(oc); + int nul; + acv.Filter = x => !int.TryParse(((Person)x).Name, out nul); + acv.SortDescriptions.Add(new SortDescription("Name", SortDirection.Ascending)); + + RightList.ItemsSource = acv; + } + + private void Add_Click(object sender, RoutedEventArgs e) + { + if (!string.IsNullOrWhiteSpace(NewItemBox.Text)) + { + oc!.Insert(0, new Person { Name = NewItemBox.Text }); + NewItemBox.Text = ""; + } + } + + /// + /// A sample class used to show how to use the interface. + /// + public class Person + { + /// + /// Gets or sets the name of the person. + /// + public string? Name { get; set; } + } +} diff --git a/components/AdvancedCollectionView/samples/AdvancedCollectionViewSample.xaml b/components/AdvancedCollectionView/samples/AdvancedCollectionViewSample.xaml new file mode 100644 index 00000000..15060922 --- /dev/null +++ b/components/AdvancedCollectionView/samples/AdvancedCollectionViewSample.xaml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + +