microsoft / microsoft-ui-xaml-specs

API spec repository for the Windows UI Library (WinUI)
https://github.com/Microsoft/microsoft-ui-xaml
Creative Commons Attribution 4.0 International
119 stars 40 forks source link

ItemsRepeater: Spec for adding First/LastVisibleIndex and RealizedItems properties to API #87

Open anawishnoff opened 4 years ago

anawishnoff commented 4 years ago

Background

ItemsRepeater is a flexible layout control that allows you to easily display collections of items complete with custom layouts, animations, styling, and more. Currently, there is no built-in way to access which items are visible or realized in the viewport of the ItemsRepeater. This inhibits lots of interaction-based UIs and puts lots of extra work on the developer to manually determine which items are visible.

ItemsRepeater does independent scrolling, and requires a buffer area that holds non-visible items and runs layout processes on them before they become visible/scrolled onto. This buffer area size is configurable via the HorizontalCacheLength and VerticalCacheLength properties.

The ListView and GridView controls have properties to access the indices of their first and last visible items in the form of the FirstVisibleIndex and LastVisibleIndex properties on their respective base panels, ItemsStackPanel and ItemsWrapGrid. These properties allow you to access all currently visible items.

Description

RealizedItems and VisibleItems are properties that give you access to all realized or visible items within an ItemsRepeater. These two properties both return lists of UIElement objects that make up the ItemsRepeater.

Realized items includes all items that are currently visible and all items that are in the cache/buffer area. The size of these buffer areas are configurable via the HorizontalCacheLength and VerticalCacheLength properties

Visible items refers to all items that are visible in the ItemsRepeater, whether they are fully or partially visible.

Examples

/* Example 1: Check if a specific item is visible by the user. If visible, inform the user that they've scrolled to the bottom of the list. */

int repeater_length = repeater.ItemsSource.Count;

foreach (Grid item in repeater.VisibleItems){
  if (repeater.GetElementIndex(item) == repeater_length - 1){
    myTextBlock.Text = "No more elements to display.";
  }
}

/* Example 2: Change the background color of any items that have been seen by the user (i.e. they're visible on the screen). This example applies well to messaging scenarios where the styling is changed for messages that have been read/seen.

By using the RealizedItems property, any item that becomes visible while scrolling will have this changed background color.  */ 

foreach(StackPanel message in repeater.RealizedItems){
  // each item has a DataTemplate with a StackPanel root element 
  message.Background = "Red";
}

API Notes


public IEnumerable<UIElement> RealizedItems { get; }
public IEnumerable<UIElement> VisibleItems { get; }

// RealizedItems and VisibleItems are *unsorted* IEnumerable objects.

API Details

unsealed runtimeclass ItemsRepeater : Windows.UI.Xaml.FrameworkElement
{
    ItemsRepeater();

    // ...
    Windows.Foundation.Collections.IEnumerable<Windows.UI.Xaml.UIElement> RealizedItems { get; }

    Windows.Foundation.Collections.IEnumerable<Windows.UI.Xaml.UIElement> VisibleItems { get; }

    // ...
}
marcelwgn commented 4 years ago

Should the RealizedItems property return an IEnumerable<object> or List<object>? Or should it simply return an object to match the ItemsSource property?

Similiar to what SelectionModel does, I think exposing an IEnumerable<UIElement> would be the best scenario. After all, we we know that the realized items must be UIElements and, we know that it should be a list.

I think the reason why ItemsSource is object is because of the fact that it can accept a broad range of object types. In theory IIterable could be sufficient, but I am not 100% if that would have worked with the projections.

PS: Not entirely sure why but MD seems broken for that comment, so I've added the initial question as code block to differentiate it from my response.

ranjeshj commented 4 years ago

Should the RealizedItems property return an IEnumerable<object> or List<object>? Or should it simply return an object to match the ItemsSource property?

Similiar to what SelectionModel does, I think exposing an IEnumerable<UIElement> would be the best scenario. After all, we we know that the realized items must be UIElements and, we know that it should be a list.

I think the reason why ItemsSource is object is because of the fact that it can accept a broad range of object types. In theory IIterable could be sufficient, but I am not 100% if that would have worked with the projections.

PS: Not entirely sure why but MD seems broken for that comment, so I've added the initial question as code block to differentiate it from my response.

Yes. Definitely more type safety where possible. ItemsSource is a bit special because it is trying to do the same thing that ItemsControl did for compat reasons and there was not one underlying type we could use there other than object.

One thing to note/question. Should the returned list of realized elements have an order (index order) ? Not guaranteeing an order would be preferred because sorting by index can become a performance issue. I think in most cases the app developer would only care about the set and not the order. If order matters, it can be sorted in the app code. I think it is worth calling that out.

marcelwgn commented 4 years ago

One thing to note/question. Should the returned list of realized elements have an order (index order) ? Not guaranteeing an order would be preferred because sorting by index can become a performance issue. I think in most cases the app developer would only care about the set and not the order. If order matters, it can be sorted in the app code. I think it is worth calling that out.

How would the sorting look like from an App's perspective? Get the index of each realized item and sort via those?

Depending on the layout, the items probably are already sorted, but in general, we would need to sort it to be sure. Since ItemsRepeater is focused on performance, I wouldn't say it's necessary to add the overhead of sorting those. After all in most cases, you are interested in the realized items to update them based on a certain criteria which most likely won't rely on the position in the collection. So, I agree with @ranjeshj, we should call that out that it is not guaranteed the items are in a specific order.

anawishnoff commented 4 years ago

You got here so fast, @chingucoding! :)

Thanks Marcel and @ranjeshj - I agree that type safety should be prioritized. I'll make the change to have it return an IEnumerable. This also makes it much easier to interact with the items inside the list without having to go through the trouble of casting it.

I agree on leaving RealizedItems unsorted. I think that if you wanted to access the first or last index of the RealizedItems (i.e. needed it to be sorted), you could just use FirstVisibleIndex or LastVisibleIndex to receive the same value, even in non-contiguous scenarios. Can't think of any other common use case where you'd need to know the order of the realized items. I'll explicitly mention this in the spec.

anawishnoff commented 4 years ago

@ranjeshj Bringing this back to life. Any more questions/concerns?

ranjeshj commented 4 years ago

@ranjeshj Bringing this back to life. Any more questions/concerns?

Looks good to me.

anawishnoff commented 4 years ago

@MikeHillberg - would you be able to review this spec? Looks like Ranjesh has approved it, so not totally sure of the next step here. Thanks!