CommunityToolkit/Maui.Markup

[Proposal] Add ItemsView Extensions

brminnick opened this issue · 5 comments

Add ItemsView Extensions

Link to Discussion

#50

Summary

This PR adds fluent extension methods for ItemsView.

These extensions will work for both CarouselView and CollectionView, both of which inherit from ItemsView.

Motivation

To improve the developer workflow around initializing CollectionView and CarouselView

Detailed Design

using System.Collections;
using System.Windows.Input;
using Microsoft.Maui;
using Microsoft.Maui.Controls;

namespace CommunityToolkit.Maui.Markup;

/// <summary>
/// Fluent extension methods for <see cref="ItemsView"/>
/// </summary>
public static class ItemsViewExtensions
{
	/// <summary>
	/// Assigns the <see cref="ItemsView.EmptyView"/> property
	/// </summary>
	/// <typeparam name="TItemsView"></typeparam>
	/// <param name="itemsView"></param>
	/// <param name="view"></param>
	/// <returns>ItemsView with Empty View</returns>
	public static TItemsView EmptyView<TItemsView>(this TItemsView itemsView, object view) where TItemsView : ItemsView
	{
		itemsView.EmptyView = view;
		return itemsView;
	}

	/// <summary>
	/// Assigns the <see cref="ItemsView.EmptyViewTemplate"/> property
	/// </summary>
	/// <typeparam name="TItemsView"></typeparam>
	/// <param name="itemsView"></param>
	/// <param name="view"></param>
	/// <returns>ItemsView with Empty View Template</returns>
	public static TItemsView EmptyViewTemplate<TItemsView>(this TItemsView itemsView, DataTemplate view) where TItemsView : ItemsView
	{
		itemsView.EmptyViewTemplate = view;
		return itemsView;
	}

	/// <summary>
	/// Assigns the <see cref="ItemsView.ItemsSource"/> property
	/// </summary>
	/// <typeparam name="TItemsView"></typeparam>
	/// <param name="itemsView"></param>
	/// <param name="itemsSource"></param>
	/// <returns>ItemsView with ItemSource</returns>
	public static TItemsView ItemsSource<TItemsView>(this TItemsView itemsView, IEnumerable itemsSource) where TItemsView : ItemsView
	{
		itemsView.ItemsSource = itemsSource;
		return itemsView;
	}

	/// <summary>
	/// Assigns the <see cref="ItemsView.HorizontalScrollBarVisibility"/> property
	/// </summary>
	/// <typeparam name="TItemsView"></typeparam>
	/// <param name="itemsView"></param>
	/// <param name="visibility"></param>
	/// <returns>ItemsView with updated Horiztonal Scroll Bar Visibility</returns>
	public static TItemsView HorizontalScrollBarVisibility<TItemsView>(this TItemsView itemsView, ScrollBarVisibility visibility) where TItemsView : ItemsView
	{
		itemsView.HorizontalScrollBarVisibility = visibility;
		return itemsView;
	}

	/// <summary>
	/// Assigns the <see cref="ItemsView.VerticalScrollBarVisibility"/> property
	/// </summary>
	/// <typeparam name="TItemsView"></typeparam>
	/// <param name="itemsView"></param>
	/// <param name="visibility"></param>
	/// <returns>ItemsView with updated Vertical Scroll Bar Visibility</returns>
	public static TItemsView VerticalScrollBarVisibility<TItemsView>(this TItemsView itemsView, ScrollBarVisibility visibility) where TItemsView : ItemsView
	{
		itemsView.VerticalScrollBarVisibility = visibility;
		return itemsView;
	}

	/// <summary>
	/// Assigns the <see cref="ItemsView.VerticalScrollBarVisibility"/> and <see cref="ItemsView.HorizontalScrollBarVisibility"/> properties
	/// </summary>
	/// <typeparam name="TItemsView"></typeparam>
	/// <param name="itemsView"></param>
	/// <param name="visibility"></param>
	/// <returns>ItemsView with updated Horiztonal + Vertical Scroll Bar Visibility</returns>
	public static TItemsView ScrollBarVisibility<TItemsView>(this TItemsView itemsView, ScrollBarVisibility visibility) where TItemsView : ItemsView
	{
		return itemsView.HorizontalScrollBarVisibility(visibility).VerticalScrollBarVisibility(visibility);
	}

	/// <summary>
	/// Assigns the <see cref="ItemsView.RemainingItemsThreshold"/> property
	/// </summary>
	/// <typeparam name="TItemsView"></typeparam>
	/// <param name="itemsView"></param>
	/// <param name="threshold"></param>
	/// <returns>ItemsView with updated Remaining Items Threshold</returns>
	public static TItemsView RemainingItemsThreshold<TItemsView>(this TItemsView itemsView, int threshold) where TItemsView : ItemsView
	{
		itemsView.RemainingItemsThreshold = threshold;
		return itemsView;
	}

	/// <summary>
	/// Assigns the <see cref="ItemsView.RemainingItemsThresholdReachedCommand"/>  ans <see cref="ItemsView.RemainingItemsThresholdReachedCommandParameter"/>properties
	/// </summary>
	/// <typeparam name="TItemsView"></typeparam>
	/// <param name="itemsView"></param>
	/// <param name="command"></param>
	/// <param name="parameter"></param>
	/// <returns>ItemsView with updated Remaining Items Threshold Reached Command + CommandParameter</returns>
	public static TItemsView RemainingItemsThresholdReachedCommand<TItemsView>(this TItemsView itemsView, ICommand command, object? parameter) where TItemsView : ItemsView
	{
		return itemsView.RemainingItemsThresholdReachedCommand(command).RemainingItemsThresholdReachedCommandParameter(parameter);
	}

	/// <summary>
	/// Assigns the <see cref="ItemsView.RemainingItemsThresholdReachedCommand"/> property
	/// </summary>
	/// <typeparam name="TItemsView"></typeparam>
	/// <param name="itemsView"></param>
	/// <param name="command"></param>
	/// <returns>ItemsView with updated Remaining Items Threshold Reached Command</returns>
	public static TItemsView RemainingItemsThresholdReachedCommand<TItemsView>(this TItemsView itemsView, ICommand command) where TItemsView : ItemsView
	{
		itemsView.RemainingItemsThresholdReachedCommand = command;
		return itemsView;
	}

	/// <summary>
	/// Assigns the <see cref="ItemsView.RemainingItemsThresholdReachedCommandParameter"/> property
	/// </summary>
	/// <typeparam name="TItemsView"></typeparam>
	/// <param name="itemsView"></param>
	/// <param name="parameter"></param>
	/// <returns>ItemsView with updated Remaining Items Threshold Reached Command Parameter</returns>
	public static TItemsView RemainingItemsThresholdReachedCommandParameter<TItemsView>(this TItemsView itemsView, object? parameter) where TItemsView : ItemsView
	{
		itemsView.RemainingItemsThresholdReachedCommandParameter = parameter;
		return itemsView;
	}

	/// <summary>
	/// Assigns the <see cref="ItemsView.ItemTemplate"/> property
	/// </summary>
	/// <typeparam name="TItemsView"></typeparam>
	/// <param name="itemsView"></param>
	/// <param name="template"></param>
	/// <returns>ItemsView with updated Item Template</returns>
	public static TItemsView ItemTemplate<TItemsView>(this TItemsView itemsView, DataTemplate template) where TItemsView : ItemsView
	{
		itemsView.ItemTemplate = template;
		return itemsView;
	}

	/// <summary>
	/// Assigns the <see cref="ItemsView.ItemsUpdatingScrollMode"/> property
	/// </summary>
	/// <typeparam name="TItemsView"></typeparam>
	/// <param name="itemsView"></param>
	/// <param name="mode"></param>
	/// <returns>ItemsView with updated ItemsUpdatingScrollMode</returns>
	public static TItemsView ItemsUpdatingScrollMode<TItemsView>(this TItemsView itemsView, ItemsUpdatingScrollMode mode) where TItemsView : ItemsView
	{
		itemsView.ItemsUpdatingScrollMode = mode;
		return itemsView;
	}
}

Usage Syntax

C# Usage

Content = new CollectionView()
                  .ItemSource(new [] { "Hello", "World" })
                  .ItemTemplate(new DataTemplate(() => new Label().Bind(Label.TextProperty, "."));

Drawbacks

No known drawbacks

Alternatives

Currently, the properties need to be set using initializers, leading to a combination of properties + extension methods:

Content = new CollectionView
{
  ItemTemplate = new DataTemplate(() => new Label().Bind(Label.TextProperty, "."))
}.Bind(CollectionView.ItemSourceProperty, nameof(ViewModel.ItemSource));

Unresolved Questions

No known questions

Approve.
Just a small question. Does EmptyView type is Object? can we set a more specific type like String or IView?

public static TItemsView EmptyView<TItemsView>(this TItemsView itemsView, string emptyViewContent) where TItemsView : ItemsView
	
public static TItemsView EmptyView<TItemsView>(this TItemsView itemsView, IView view) where TItemsView : ItemsView

Thanks! Yea, I thought the same thing about EmptyView's type. But ItemsView.EmptyView is in fact object in .NET MAUI. I assumed it would be IView too.

I approve this feature ✅

I also approve ✅

Reopening Proposal.

Only Proposals moved to the Closed Project Column and Completed Project Column can be closed.