-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add observable group API #3201
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
Add observable group API #3201
Conversation
|
Thanks vgromfeld for opening a Pull Request! The reviewers will test the PR and highlight if there is any conflict or changes required. If the PR is approved we will proceed to merge the pull request 🙌 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great, nice work! 😄
I had a very similar version of these same APIs in my own apps, and I know of other UWP devs that have basically rebuilt pretty much the same APIs from scratch as well, so it'll be nice to have them available right from the toolkit! I just have a couple of suggestions out of experience of what I noticed while working with these APIs in my own app, and a general questions in the 3rd review comment.
Other than that, here's also another feature suggestion, not adding this as a review comment since it's not directly tied to any file in particular. I noticed that adding new items to a grouped collection can get quite verbose, especially if you find yourself adding new groups with a single item. Basically, in that case you'd need to write the following:
Source.Add(new ObservableGroup<SomeKeyType, SomeGroupItemValueType>(key, value));Which especially when done often, makes the code a bit crowded. I came up with the extension class that I've been using in my app, would it make sense to add it to this PR as well?
/// <summary>
/// An extension <see langword="class"/> for <see cref="ObservableCollection{T}"/> type.
/// </summary>
public static class ObservableCollectionExtensions
{
/// <summary>
/// Adds a key-value <see cref="ObservableGroup{TKey,TElement}"/> item into a target <see cref="ObservableCollection{T}"/>.
/// </summary>
/// <typeparam name="TKey">The type of the group key.</typeparam>
/// <typeparam name="TElement">The type of the elements in the group.</typeparam>
/// <param name="source">The source <see cref="ObservableCollection{T}"/> instance.</param>
/// <param name="key">The key to add.</param>
/// <param name="element">The element to add.</param>
public static void AddGroup<TKey, TElement>(
this ObservableCollection<ObservableGroup<TKey, TElement>> source,
TKey key,
TElement element)
{
Add(source, key, new [] { element });
}
/// <summary>
/// Adds a key-collection <see cref="ObservableGroup{TKey,TElement}"/> item into a target <see cref="ObservableCollection{T}"/>.
/// </summary>
/// <typeparam name="TKey">The type of the group key.</typeparam>
/// <typeparam name="TElement">The type of the elements in the group.</typeparam>
/// <param name="source">The source <see cref="ObservableCollection{T}"/> instance.</param>
/// <param name="key">The key to add.</param>
/// <param name="collection">The collection to add.</param>
public static void AddGroup<TKey, TElement>(
this ObservableCollection<ObservableGroup<TKey, TElement>> source,
TKey key,
IEnumerable<TElement> collection)
{
source.Add(new ObservableGroup<TKey, TElement>(key, collection));
}
}It's just a very small syntactic sugar pair of extensions, really, which let you write the following:
Source.Add(key, value);Just an idea, very ncie job again with the PR! 👏
Microsoft.Toolkit/Observables/ReadOnlyObservableGroupedCollection.cs
Outdated
Show resolved
Hide resolved
|
@Sergio0694 +1 for the extensions. This will go in another PR. |
Microsoft.Toolkit/Observables/ReadOnlyObservableGroupedCollection.cs
Outdated
Show resolved
Hide resolved
|
Ah that's great then, yeah having all that part in another PR would make it easier to review I guess, glad you have that planned as well though, this couple PRs look very useful! 😊 |
|
This PR has been marked as "needs attention 👋" and awaiting a response from the team. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few more small notes 😄
Thank you for adding the IReadOnlyObservableGroup interface!
Also, just making sure my last comment was lost since the parent thread was already marked as resolved, let me know what you think in this comment! 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vgromfeld this is going to be great addition to the Toolkit, just started looking at this, but I know when I added grouping in XAML Studio, I was surprised there was no default implementation for the IGrouping interface! I also encountered issues with accessibility as the Key's wouldn't have names by default.
For reference to compare against your implementation (just in case we're missing any use-cases), this was my implementation I had planned to contribute back into the Toolkit:
public class Grouping<TKey, TElement> : IGrouping<TKey, TElement>, IEnumerable<TElement>, IEnumerable, ICollectionViewGroup
{
public object Group => Key;
public IObservableVector<object> GroupItems => (IObservableVector<object>)new ObservableCollection<object>((IEnumerable<object>)this);
public TKey Key { get; private set; }
private IEnumerable<TElement> Items { get; }
public Grouping(TKey key, IEnumerable<TElement> items)
{
Key = key;
Items = items;
}
public IEnumerator<TElement> GetEnumerator()
{
return Items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return Items.GetEnumerator();
}
public override string ToString()
{
return Key?.ToString();
}
}
public static class EnumerableExtensions
{
public static Grouping<TKey, TElement> ToGroup<TKey, TElement>(this IEnumerable<TElement> list, TKey key)
=> new Grouping<TKey, TElement>(key, list);
public static IEnumerable<Grouping<TKey, TElement>> ToGroup<TKey, TElement>(this IEnumerable<TElement> list, Func<TElement, TKey> keySelector)
=> list.GroupBy(keySelector, (key, items) => new Grouping<TKey, TElement>(key, items));
}
public static class GroupingExtensions
{
// Converts a System Group to this Group.
public static Grouping<TKey, TElement> ToGroup<TKey, TElement>(this IGrouping<TKey, TElement> group)
=> new Grouping<TKey, TElement>(group.Key, group.AsEnumerable());
}I think your PR covers most of this though (or will by the end), eh?
FYI @Sergio0694
|
This PR has been marked as "needs attention 👋" and awaiting a response from the team. |
|
@michael-hawker Our grouping implementations are similar (except the extensions methods which are not in this PR 😀) |
|
@vgromfeld don't forget about the docs PR for this as well. :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of small tweaks for the sample, and needs the doc PR started, but otherwise looking great! Going to test this out more via copy+paste in my stream today.
Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ObservableGroup/ObservableGroup.bind
Show resolved
Hide resolved
Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ObservableGroup/ObservableGroupPage.xaml
Show resolved
Hide resolved
|
This PR has been marked as "needs attention 👋" and awaiting a response from the team. |
|
|
I was thinking, while it's absolutely fine to add this to the main package now in All these new types are primarily meant to be used with the MVVM pattern (they're all collections using the Just a note, thought I'd mention this for future reference 😄 |
|
@Sergio0694 I think this is probably the right place for them, the main use-case for these is to help with grouping in a ListView which doesn't need to be using an MVVM pattern. So I think |
|
@michael-hawker Nope, as of right now the I was just thinking that, especially in the context of a more "general .NET"-oriented toolkit architecture, these new collection types would've been a good fit for the As in, my rationale was to leave the base toolkit for basically 100% platform/framework agnostic things, such as general extensions for base types (eg. value types, This way eg. someone writing a .NET Core console app, which doesn't have any UI framework at all, would just see the types relevent to him when referencing the base package from the toolkit. Of course, just thinking out loud here! 😊 |
|
Thanks @Sergio0694, I think we just want to be careful about putting things in a separate package where they can also be used in non-MVVM scenarios, as then that adds extra dependencies the MVVM package has. Let's leave this here for now, and see how feedback goes on all these new APIs in upcoming releases. |
PR Type
What kind of change does this PR introduce?
What is the new behavior?
When using grouping in
ListVieworGridView, we often have to create our own group classes and re-implement the same kind of logic.In this PR, I'm proposing a set of classes to simplify this:
ObservableGroup<TKey, TValue>: an observable list ofTValuewith a group name (TKey)ObservableGroupedCollection<TKey, TValue>: an observable list of groups. It can be created from a LINQ query result.ReadOnlyObservableGroup<TKey, TValue>: a read-onlyObservableGroup<TKey, TValue>ReadOnlyObservableGroupedCollection<TKey, TValue>: a read-onlyObservableGroupedCollection<TKey, TValue>.PR Checklist
Please check if your PR fulfills the following requirements: