Git Product home page Git Product logo

communitytoolkit / maui Goto Github PK

View Code? Open in Web Editor NEW
2.1K 2.1K 329.0 4.27 MB

The .NET MAUI Community Toolkit is a community-created library that contains .NET MAUI Extensions, Advanced UI/UX Controls, and Behaviors to help make your life as a .NET MAUI developer easier

Home Page: https://learn.microsoft.com/dotnet/communitytoolkit/maui

License: MIT License

C# 99.94% PowerShell 0.06%
android desktop dotnet dotnet-maui hacktoberfest ios maccatalyst macos maui mobile windows winui

maui's Introduction

Community Toolkit Docs

This repo contains the guidance documentation for various Community Toolkits that are part of the .NET Foundation.

This includes the following projects:

  • .NET Community Toolkit (and MVVM Toolkit)
  • .NET MAUI Community Toolkit
  • Windows Community Toolkit (though our docs are still migrating from the old repo here)

More information to come...

Microsoft Open Source Code of Conduct

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

maui's People

Contributors

allend-msft avatar anpin avatar axemasta avatar bijington avatar bretjohnson avatar brminnick avatar cat0363 avatar cliffagius avatar cucumber-sp avatar dependabot[bot] avatar georgeleithead avatar github-actions[bot] avatar jfversluis avatar jsuarezruiz avatar juansturla avatar kphillpotts avatar marsscotia avatar maxkoshevoi avatar ne0rrmatrix avatar nicjay avatar niksedk avatar pictos avatar pieeatingninjas avatar pureween avatar russnash avatar stefanbogaard86 avatar steven62f avatar trybina132 avatar vhugogarcia avatar vladislavantonyuk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

maui's Issues

[Proposal] `Color`-To-Component Converters

ColorToComponentConverters

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Documentation
  • Sample

Summary

Converts the incoming value from Color and returns the object of a type byte.

Detailed Design

public class ColorToByteAlphaConverter : BaseConverterOneWay<Color, byte>;
public class ColorToByteRedConverter : BaseConverterOneWay<Color, byte>;
public class ColorToByteGreenConverter : BaseConverterOneWay<Color, byte>;
public class ColorToByteBlueConverter : BaseConverterOneWay<Color, byte>;
public class ColorToPercentCyanConverter : BaseConverterOneWay<Color, double>;
public class ColorToPercentMagentaConverter : BaseConverterOneWay<Color, double>;
public class ColorToPercentYellowConverter : BaseConverterOneWay<Color, double>;
public class ColorToBlackKeyConverter : BaseConverterOneWay<Color, double>;
public class ColorToDegreeHueConverter : BaseConverterOneWay<Color, double>;

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">
    <ContentPage.Resources>
         <ResourceDictionary>
             <toolkit: ColorToByteAlphaConverter x:Key="ColorToByteAlphaConverter" />
         </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label TextColor="{Binding MyTextColor, Converter={StaticResource ColorToByteAlphaConverter}}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new Label().Bind(Image.TextColorProperty, named(ViewModel.LabelTextColor), converter: new ColorToByteAlphaConverter());
  }
}

[Proposal] Maximum Length Reached Behavior

MaxLengthReachedBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Documentation: Not Started
  • Sample

Summary

The MaxLengthReachedBehavior is a behavior that allows the user to trigger an action when a user has reached the maximum length allowed on an Entry. It can either trigger an ICommand or an event depending on the user's preferred scenario.

Detailed Design

MaxLengthReachedBehavior.shared.cs

public class MaxLengthReachedBehavior : BaseBehavior<InputView>
{
    public static readonly BindableProperty CommandProperty;
    public static readonly BindableProperty ShouldDismissKeyboardAutomaticallyProperty;

    public event EventHandler<MaxLengthReachedEventArgs> MaxLengthReached;

    public ICommand? Command { get; set; }
    public bool ShouldDismissKeyboardAutomatically { get; set; }
}

Usage Syntax

XAML Usage

<Entry 
    Placeholder="Start typing until MaxLength is reached..."
    MaxLength="10">
    <Entry.Behaviors>
        <toolkit:MaxLengthReachedBehavior 
            Command="{Binding MaxLengthReachedCommand}"
            ShouldDismissKeyboardAutomatically="False" 
        />
    </Entry.Behaviors>
</Entry>

C# Usage

var behavior = new MaxLengthReachedBehavior
{
    ShouldDismissKeyboardAutomatically = false
});
behavior.SetBinding(MaxLengthReachedBehavior.CommandProperty, nameof(ViewModel. MaxLengthReachedCommand);

var entry = new Entry();
entry.Behaviors.Add(behavior);

[Proposal] `enum`-To-`bool` Converter

EnumToBoolConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation: Not Started

Summary

The EnumToBoolConverter is a converter that allows users to convert a enum value binding to a bool value. The Enum value can be compared against the TrueList or against the ConverterParameter.

Detailed Design

EnumToBoolConverter.shared.cs

public class EnumToBoolConverter : ValueConverterExtension, IValueConverter
{
  public IList<Enum> TrueValues { get; } = new List<Enum>();

  public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
  {
	  if (value is not Enum enumValue)
		  throw new ArgumentException("The value should be of type Enum", nameof(value));
  
	  return TrueValues.Count == 0
		  ? CompareTwoEnums(enumValue, parameter as Enum)
		  : TrueValues.Any(item => CompareTwoEnums(enumValue, item));
  
	  static bool CompareTwoEnums(Enum valueToCheck, object? referenceValue)
	  {
		  if (referenceValue is not Enum referenceEnumValue)
			  return false;
  
		  var valueToCheckType = valueToCheck.GetType();
		  if (valueToCheckType != referenceEnumValue.GetType())
			  return false;
  
		  if (valueToCheckType.GetTypeInfo().GetCustomAttribute<FlagsAttribute>() != null)
			  return referenceEnumValue.HasFlag(valueToCheck);
  
		  return Equals(valueToCheck, referenceEnumValue);
	  }
  }

  public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             xmlns:myEnum="MyLittleApp.Models"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <!-- Converter with TRUE list -->
            <xct:EnumToBoolConverter x:Key="OpenIssueConverter">
                <xct:EnumToBoolConverter.TrueValues>
                    <myEnum:MyStateEnum>New</myEnum:MyStateEnum>
                    <myEnum:MyStateEnum>InReview</myEnum:MyStateEnum>
                    <myEnum:MyStateEnum>Developing</myEnum:MyStateEnum>
                </xct:EnumToBoolConverter.TrueValues>
            </xct:EnumToBoolConverter>
            <xct:EnumToBoolConverter x:Key="ClosedIssueConverter">
                <xct:EnumToBoolConverter.TrueValues>
                    <myEnum:MyStateEnum>WantFix</myEnum:MyStateEnum>
                    <myEnum:MyStateEnum>Resolved</myEnum:MyStateEnum>
                </xct:EnumToBoolConverter.TrueValues>
            </xct:EnumToBoolConverter>
            <!-- Converter, that uses parameter -->
            <xct:EnumToBoolConverter x:Key="IssueStateConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <!-- Converter with TRUE list -->
        <Label IsVisible="{Binding IssueState, Converter={StaticResource OpenIssueConverter}}" />
        <Label IsVisible="{Binding IssueState, Converter={StaticResource ClosedIssueConverter}}" />
        <!-- Converter, that uses parameter -->
        <Label IsVisible="{Binding IssueState, Converter={StaticResource IssueStateConverter}, ConverterParameter={x:Static myEnum:MyStateEnum.WaitingForCustomer}}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    var converter = new EnumToBoolConverter();
    converter.TrueValues.Add(MyStateEnum.WantFix);
    converter.TrueValues.Add(MyStateEnum.Resolve);

    Content = new StackLayout
    {
      Children = 
      {
          new Label().Bind(Label.IsVisibleProperty, nameof(ViewModel. IssueState), converter: converter),
      };
    }
  }
}

[Proposal] NotEqual Converter

NotEqualConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The NotEqualConverter is a converter that allows users to convert any value binding to a boolean depending on whether or not it is equal to a different value. The initial binding contains the object that will be compared and the ConverterParameter contains the object to compare it to.

Detailed Design

NotEqualConverter.shared.cs

public class NotEqualConverter : ValueConverterExtension, IValueConverter
{
  public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) => !EqualConverter.ConvertInternal(value, parameter);
  public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:NotEqualConverter x:Key="NotEqualConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label IsVisible="{Binding MyFirstObject, Converter={StaticResource NotEqualConverter}, ConverterParameter=100}" />

    </StackLayout>
</ContentPage>

C# Usage

new Label().Bind(Label.IsVisibleProperty, nameof(ViewModel.MyFirstObject), converter: new NotEqualConverter(), converterParameter: 100);

[Proposal] Is In Range Converter

IsInRangeConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The IsInRangeConverter is a converter that allows users to convert a object value into a boolean value, checking if the value is between MinValue and MaxValue, returning true if the value is within the range and false if the value is out of the range

Detailed Design

IsInRangeConverter.shared.cs

public class IsInRangeConverter : ValueConverterExtension, IValueConverter
{
  public static readonly BindableProperty MinValueProperty = BindableProperty.Create(nameof(MinValue), typeof(object), typeof(IsInRangeConverter));

  public static readonly BindableProperty MaxValueProperty = BindableProperty.Create(nameof(MaxValue), typeof(object), typeof(IsInRangeConverter));

  public object? MinValue
  {
	get => GetValue(MinValueProperty);
	set => SetValue(MinValueProperty, value);
  }

  public object? MaxValue
  {
	get => GetValue(MaxValueProperty);
	set => SetValue(MaxValueProperty, value);
  }

  public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
  {
	if (value is not IComparable comparable)
		throw new ArgumentException("is expected to implement IComparable interface.", nameof(value));

	if (MinValue is not IComparable)
		throw new ArgumentException("is expected to implement IComparable interface.", nameof(MinValue));

	if (MaxValue is not IComparable)
		throw new ArgumentException("is expected to implement IComparable interface.", nameof(MaxValue));

	return comparable.CompareTo(MinValue) >= 0 && comparable.CompareTo(MaxValue) <= 0;
  }

  public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             xmlns:myEnum="MyLittleApp.Models"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
          <xct:IsInRangeConverter
                          x:Key="IsInRangeConverter"
                          MaxValue= "1"
                          MinValue= "3" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>
        <Label IsVisible="{Binding Number, Converter={StaticResource IssueStateConverter } }" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new StackLayout
    {
      Children = 
      {
          new Label().Bind(Label.IsVisibleProperty, nameof(ViewModel.Number), converter: new IsInRangeConverter { MinValue = 1, MaxValue = 3 }),
      };
    }
  }
}

[Proposal] `List`-To-`string` Converter

ListToStringConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The ListToStringConverter is a converter that allows users to convert an incoming binding that implements IEnumerable to a single string value. The Separator property is used to join the items in the IEnumerable.

Motivation

Detailed Design

ListToStringConverter.shared.cs

public class ListToStringConverter : ValueConverterExtension, IValueConverter
{
  public string Separator { get; set; } = string.Empty;

  public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
  {
	if (value == null)
		return string.Empty;

	if (value is not IEnumerable enumerable)
		throw new ArgumentException("Value cannot be casted to IEnumerable", nameof(value));

	if ((parameter ?? Separator ?? string.Empty) is not string separator)
		throw new ArgumentException("Parameter cannot be casted to string", nameof(parameter));

	var collection = enumerable
		.OfType<object>()
		.Select(x => x.ToString())
		.Where(x => !string.IsNullOrWhiteSpace(x));

	return string.Join(separator, collection);
  }

  public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="clr-namespace:Xamarin.CommunityToolkit.Converters;assembly=Xamarin.CommunityToolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:ListToStringConverter x:Key="ListToStringConverter" Separator=", " />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label IsVisible="{Binding MyListValue, Converter={StaticResource ListToStringConverter}}" />

    </StackLayout>
</ContentPage>

C# Usage

new Label().Bind(Label.TextPropoerty, nameof(ViewModel.MyListValue), converter: = new ListToStringConverter());

[Proposal] `bool`-to-`object` Converter

BoolToObjectConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Documentation
  • Sample

Summary

The BoolToObjectConverter is a converter that allows users to convert a bool value binding to a specific object. By providing both a TrueObject and a FalseObject in the converter the appropriate object will be used depending on the value of the binding

Detailed Design

public class BoolToObjectConverter : BoolToObjectConverter<object>
{
}

public class BoolToObjectConverter<TObject> : ValueConverterExtension, IValueConverter
{
  public TObject? TrueObject { get; set; }
  public TObject? FalseObject { get; set; }

  public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
  {
	  if (value is bool result)
		  return result ? TrueObject : FalseObject;
  
	  throw new ArgumentException("Value is not a valid boolean", nameof(value));
  }

  public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
  {
	  if (value is TObject result)
		  return result.Equals(TrueObject);
  
	  if (default(TObject) == null && value == null && TrueObject == null)
		  return true;
  
	  throw new ArgumentException($"Value is not a valid {typeof(TObject).Name}", nameof(value));
  }
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
         <ResourceDictionary>
             <xct:BoolToObjectConverter x:Key="BoolToObjectConverter" TrueObject="16" FalseObject="10" />
         </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label Text="Hi there from the Docs!" FontSize="{Binding MyBoolean, Converter={StaticResource BoolToObjectConverter}}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
    public MyPage()
    {
        Content = new Label { Text = "Hi there from the Docs!"}
                          .Bind(Label.FontSizeProperty, nameof(ViewModel.MyBoolean), converter: new BoolToObjectConverter<double>{ FalseObject = 10, TrueObject = 16 })
    }
}

[Proposal] DateTimeOffsetConverter

DateTimeOffsetConverter

  • Proposed
  • Prototype
  • Implementation: Not Started
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Documentation: Not Started
  • Sample

Summary

The DateTimeOffsetConverter is a converter that allows users to convert a DateTimeOffset to a DateTime. Sometimes a DateTime value is stored with the offset on a backend to allow for storing the timezone in which a DateTime originated from. Controls like the Microsoft.Maui.Controls.DatePicker only work with DateTime. This converter can be used in those scenarios.

Detailed Design

DateTimeOffsetConverter.shared.cs

public class DateTimeOffsetConverter : ValueConverterExtension, IValueConverter
{
  public object Convert(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo culture)=> value is DateTimeOffset dateTimeOffset
    ? dateTimeOffset.DateTime
    : throw new ArgumentException("Value is not a valid DateTimeOffset", nameof(value));

  public object ConvertBack(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo culture) => value is DateTime dateTime
    ? dateTime.Kind switch
    {
	    DateTimeKind.Local => new DateTimeOffset(dateTime, DateTimeOffset.Now.Offset),
	    DateTimeKind.Utc => new DateTimeOffset(dateTime, DateTimeOffset.UtcNow.Offset),
	    _ => new DateTimeOffset(dateTime, TimeSpan.Zero),
    }
    : throw new ArgumentException("Value is not a valid DateTime", nameof(value));
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">
    <ContentPage.Resources>
         <ResourceDictionary>
             <xct:DateTimeOffsetConverter x:Key="DateTimeOffsetConverter" />
         </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label Text="{Binding MyDateTimeOffset, Converter={StaticResource DateTimeOffsetConverter}}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new StackLayout
    {
      Children = 
      {
          new Label().Bind(Label.TextProperty, nameof(ViewModel. MyDateTimeOffset), converter: new DateTimeOffsetConverter()),
      };
    }
  }
}

[Proposal] IndexToArrayItemConverter

IndexToArrayItemConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The IndexToArrayItemConverter is a converter that allows users to convert a int value binding to an item in an array. The int value being data bound represents the indexer used to access the array. The array is passed in through the ConverterParameter.

Detailed Design

IndexToArrayItemConverter.shared.cs

public class IndexToArrayItemConverter : ValueConverterExtension, IValueConverter
{
  public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
  {
	if (value is not int index)
		throw new ArgumentException("Value is not a valid integer", nameof(value));

	if (parameter is not Array array)
		throw new ArgumentException("Parameter is not a valid array", nameof(parameter));

	if (index < 0 || index >= array.Length)
		throw new ArgumentOutOfRangeException(nameof(value), "Index was out of range");

	return array.GetValue(index);
  }
  
  public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
  {
	if (parameter is not Array array)
		throw new ArgumentException("Parameter is not a valid array", nameof(parameter));

	for (var i = 0; i < array.Length; i++)
	{
		var item = array.GetValue(i);
		if ((item != null && item.Equals(value)) || (item == null && value == null))
			return i;
	}

	throw new ArgumentException("Value does not exist in the array", nameof(value));
  }
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:IndexToArrayItemConverter x:Key="IndexToArrayItemConverter" />
            <x:Array x:Key="MyArray" Type="x:String">
                <x:String>Value 1</x:String>
                <x:String>Value 2</x:String>
                <x:String>Value 3</x:String>
                <x:String>Value 4</x:String>
                <x:String>Value 5</x:String>
            </x:Array>
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label IsVisible="{Binding MyIntegerValue, Converter={StaticResource IndexToArrayItemConverter}, ConverterParameter={StaticResource MyArray}}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    var array = new string[] { "Value 1", "Value 2", "Value 3", "Value 4", "Value 5" };

    Content = new StackLayout
    {
      Children = 
      {
          new Label().Bind(Label.IsVisibleProperty, nameof(ViewModel.MyIntegerValue), converter: new IndexToArrayItemConverter { ConverterParameter = array }),
      };
    }
  }
}

[Proposal] Math Expression Converters

MathExpressionConverter

Summary

The MathExpressionConverter is a converter that allows users to calculate an expression at runtime from supplied arguments.

Detailed Design

MathExpression.shared.cs

sealed class MathExpression
{
  internal MathExpression(string expression, IEnumerable<double>? arguments = null);

  public double Calculate();
}

MathExpressionConverter.shared.cs

public class MathExpressionConverter : ValueConverterExtension, IValueConverter
{
  public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo culture)
  {
	if (parameter is not string expression)
		throw new ArgumentException("The parameter should be of type String.");

	if (value == null || !double.TryParse(value.ToString(), out var xValue))
		return null;

	var math = new MathExpression(expression, new[] { xValue });

	var result = math.Calculate();
	return result;
  }

  public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}

MathOperator.shared.cs

sealed class MathOperator
{
  public string Name { get; }
  
  public int NumericCount { get; }
  
  public MathOperatorPrecedence Precedence { get; }
  
  public Func<double[], double> CalculateFunc { get; }
  
  public MathOperator(
	  string name,
	  int numericCount,
	  MathOperatorPrecedence precedence,
	  Func<double[], double> calculateFunc)
  {
	  Name = name;
	  CalculateFunc = calculateFunc;
	  Precedence = precedence;
	  NumericCount = numericCount;
  }
}

MultiMathExpressionConverter.shared.cs

public class MultiMathExpressionConverter : MultiValueConverterExtension, IMultiValueConverter
{
  public object? Convert(object[]? values, Type? targetType, object? parameter, CultureInfo culture)
  {
	if (parameter is not string expression)
		throw new ArgumentException("The parameter should be of type String.");

	if (values == null)
		return null;

	var args = new List<double>();
	foreach (var value in values)
	{
		if (value == null)
			return null;

		if (double.TryParse(value.ToString(), out var xValue))
		{
			args.Add(xValue);
		}
	}

	var math = new MathExpression(expression, args);

	var result = math.Calculate();
	return result;
  }

  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException();
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">
    <ContentPage.Resources>
         <ResourceDictionary>
             <xct:MathExpressionConverter x:Key="MathExpressionConverter" />
             <xct:MultiMathExpressionConverter x:Key="MultiMathExpressionConverter" />
         </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Frame
           x:Name="CalculatedFrame"
           HeightRequest="120"
           CornerRadius="{Binding Source={x:Reference CalculatedFrame}, Path=HeightRequest, Converter={StaticResource MathExpressionConverter}, ConverterParameter='x/2'}">

        <Label TextColor="Black">
            <Label.Text>
                <MultiBinding Converter="{StaticResource MultiMathExpressionConverter}"
                              ConverterParameter="x0 * x1"
                              StringFormat="The area of Frame = {0}">
                    <Binding Path="{Binding Source={x:Reference CalculatedFrame}, Path=HeightRequest}" />
                    <Binding Path="{Binding Source={x:Reference CalculatedFrame}, Path=WidthRequest}" />
                </MultiBinding>
            </Label.Text>
        </Label>

    </StackLayout>
</ContentPage>

C# Usage

new Frame().Size(120, -1).Assign(out var frame)
  .Bind(Frame.CornerRadiusProperty, nameof(Frame.HeightRequest), converter = new MathExpressionConverter(), converterParameter: "x/2", source: frame);

[Proposal] `byte[]`-to-`ImageSource` Converter

ByteArrayToImageSourceConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The ByteArrayToImageSourceConverter is a Converter that allows the user to convert an incoming value from byte[] and returns an ImageSource. This object can then be used as the Source of an Image control.

Detailed Design

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">
    <ContentPage.Resources>
         <ResourceDictionary>
             <xct:ByteArrayToImageSourceConverter x:Key="ByteArrayToImageSourceConverter" />
         </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Image Source="{Binding MyByteArray, Converter={StaticResource ByteArrayToImageSourceConverter}}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new Image().Bind(Image.SourceProperty, named(ViewModel.MyByteArray), converter: new ByteArrayToImageSourceConverter());
  }
}

[Proposal] Numeric Validation Behavior

NumericValidationBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The NumericValidationBehavior is a Behavior that allows the user to determine if text input is a valid numeric value. For example, an Entry control can be styled differently depending on whether a valid or an invalid numeric input is provided. Additional properties handling validation are inherited from ValidationBehavior.

Detailed Design

NumericValidationBehavior.shared.cs

public class NumericValidationBehavior : ValidationBehavior
{
    public static readonly BindableProperty MinimumValueProperty;
    public static readonly BindableProperty MaximumValueProperty;
    public static readonly BindableProperty MinimumDecimalPlacesProperty;
    public static readonly BindableProperty MaximumDecimalPlacesProperty;

    public double MinimumValue { get; set; }
    public double MaximumValue { get; set; }
    public int MinimumDecimalPlaces { get; set; }
    public int MaximumDecimalPlaces { get; set; }
}

Usage Syntax

XAML Usage

<Entry>
    <Entry.Behaviors>
        <toolkit:NumericValidationBehavior
            MinimumValue="1.0"
            MaximumValue="100.0"
        />
    </Entry.Behaviors>
</Entry>

C# Usage

var numericEntry = new Entry();
numericEntry.Behaviors.Add(new CharactersValidationBehavior
{
    MinimumValue = 1
    MaximumValue = 100
});

[Proposal] EqualConverter

EqualConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The EqualConverter is a converter that allows users to convert any value binding to a bool depending on whether or not it is equal to a different value. The initial binding contains the object that will be compared and the ConverterParameter contains the object to compare it to

Detailed Design

EqualConverter.shared.cs

public class EqualConverter : ValueConverterExtension, IValueConverter
{
  public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) => ConvertInternal(value, parameter);

  internal static bool ConvertInternal(object? value, object? parameter) =>
	(value != null && value.Equals(parameter)) || (value == null && parameter == null);

  public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
	=> throw new NotImplementedException();
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:EqualConverter x:Key="EqualConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label IsVisible="{Binding MyFirstObject, Converter={StaticResource EqualConverter}, ConverterParameter=100}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new StackLayout
    {
      Children = 
      {
          new Label().Bind(Label.IsVisibleProperty, nameof(ViewModel.MyFirstObject), converter: new EqualConverter { ConverterParameter = 100 }),
      };
    }
  }
}

[Proposal] Email Validation Behavior

EmailValidationBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The EmailValidationBehavior is a behavior that allows users to determine whether or not text input is a valid e-mail address. For example, an Entry control can be styled differently depending on whether a valid or an invalid e-mail address is provided. The validation is achieved through a regular expression that is used to verify whether or not the text input is a valid e-mail address. It can be overridden to customize the validation through the properties it inherits from ValidationBehavior.

Motivation

Many forms applications require email input, e.g. Sign up, Sign in. The EmailValidationBehavior ensures that users enter a valid email address before submitting the information.

Detailed Design

EmailValidationBehavior.shared.cs

public class EmailValidationBehavior : TextValidationBehavior
{
    readonly Regex normalizerRegex = new Regex(@"(@)(.+)$");
   
    protected override string DefaultRegexPattern
			=> @"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
				@"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
    protected override RegexOptions DefaultRegexOptions => RegexOptions.IgnoreCase;

    protected override object? Decorate(object? value)
    {
	    var stringValue = base.Decorate(value)?.ToString();
	    if (string.IsNullOrWhiteSpace(stringValue))
		    return stringValue;
    
	    try
	    {
		    static string DomainMapper(Match match)
		    {
			    // Use IdnMapping class to convert Unicode domain names.
			    var idn = new IdnMapping();
    
			    // Pull out and process domain name (throws ArgumentException on invalid)
			    var domainName = idn.GetAscii(match.Groups[2].Value);
			    return match.Groups[1].Value + domainName;
		    }
    
		    // Normalize the domain
		    return normalizerRegex.Replace(stringValue, DomainMapper);
	    }
	    catch (ArgumentException)
	    {
		    return stringValue;
	    }
    }
}

Usage Syntax

XAML Usage

<Entry Placeholder="Email">
    <Entry.Behaviors>
        <toolkit:EmailValidationBehavior
            DecorationFlags="Trim"
        />
    </Entry.Behaviors>
</Entry>

C# Usage

var emailEntry = new Entry();
emailEntry(new EmailValidationBehavior
{
    DecorationFlags = TextDecorationFlags.Trim,
});

[Proposal] `Color-To-Color` Converters

ColorToBlackOrWhiteConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Sample
  • Unit Tests
  • Documentation

Summary

A one-way converter that converts a Color to a monochrome value of black or white. If the incoming Color is light, it will be converted to Colors.White. If the incoming Color is dark, it will be converted to Colors.Black.

Detailed Design

ColorExtension.shared.cs

public static Color ToBlackOrWhite(this Color baseColor) => baseColor.IsDark() ? Colors.Black : Colors.White;

public static bool IsDark(this Color c) => c.GetByteRed() + c.GetByteGreen() + c.GetByteBlue() <= 127 * 3;

public static Color ToBlackOrWhiteForText(this Color baseColor) => baseColor.IsDarkForTheEye() ? Colors.White : Colors.Black;

public static Color ToGrayScale(this Color baseColor)
{
    var avg = (baseColor.Red + baseColor.Blue + baseColor.Green) / 3;
    return Color.FromRgb(avg, avg, avg);
}

public static bool IsDarkForTheEye(this Color c) => (c.GetByteRed() * 0.299) + (c.GetByteGreen() * 0.587) + (c.GetByteBlue() * 0.114) <= 186;

public static Color ToInverseColor(this Color baseColor) => Color.FromRgb(1 - baseColor.Red, 1 - baseColor.Green, 1 - baseColor.Blue);

ColorConverters.shared.cs

public class ColorToBlackOrWhiteConverter : BaseConverterOneWay<Color, Color>
{
    public override Color ConvertFrom(Color value) => value.ToBlackOrWhite();
}

public class ColorToColorForTextConverter : BaseConverterOneWay<Color, Color>
{
    public override Color ConvertFrom(Color value) => value.ToBlackOrWhiteForText();
}

public class ColorToGrayScaleColorConverter : BaseConverterOneWay<Color, Color>
{
    public override Color ConvertFrom(Color value) => value.ToGrayScale();
}

public class ColorToInverseColorConverter : BaseConverterOneWay<Color, Color>
{
    public override Color ConvertFrom(Color value) => value.ToInverseColor();
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">
    <ContentPage.Resources>
         <ResourceDictionary>
             <toolkit: ColorToBlackOrWhiteConverter x:Key="ColorToBlackOrWhiteConverter" />
         </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label TextColor="{Binding MyTextColor, Converter={StaticResource ColorToBlackOrWhiteConverter}}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new Label().Bind(Image.TextColorProperty, named(ViewModel.LabelTextColor), converter: new ColorToBlackOrWhiteConverter());
  }
}

[Proposal] `Color`-To-`string` Converters

ColorToStringConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Documentation
  • Sample

Summary

Converts a Color to its associated string, e.g. RGB, RGBA, HEX, CMYK, etc.

Detailed Design

ColorToStringConverter.shared.cs

public class ColorToRgbStringConverter : BaseConverterOneWay<Color, string>;
public class ColorToRgbaStringConverter : BaseConverterOneWay<Color, string>;
public class ColorToHexRgbStringConverter : BaseConverter<Color, string>;
public class ColorToHexRgbaStringConverter : BaseConverter<Color, string>;
public class ColorToCmykStringConverter : BaseConverterOneWay<Color, string>;
public class ColorToCmykaStringConverter : BaseConverterOneWay<Color, string>;
public class ColorToHslStringConverter : BaseConverterOneWay<Color, string>;
public class ColorToHslaStringConverter : BaseConverterOneWay<Color, string>;

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">
    <ContentPage.Resources>
         <ResourceDictionary>
             <toolkit: ColorToRgbStringConverter x:Key="ColorToRgbStringConverter" />
         </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label Text="{Binding LabelTextColor, Converter={StaticResource ColorToRgbStringConverter}}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new Label().Bind(Image.TextProperty, named(ViewModel.LabelTextColor), converter: new ColorToRgbStringConverter());
  }
}

[Proposal] `double`-To-`int` Converter

DoubleToIntConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Documentation
  • Sample

Summary

The DoubleToIntConverter is a Converter that allows users to convert an incoming double value to an int. Optionally the user can provide a multiplier to the conversion through the Ratio property.

Detailed Design

[ContentProperty(nameof(Ratio))]
public class DoubleToIntConverter : ValueConverterExtension, IValueConverter
{
  public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
	=> value is double result
		? (int)Math.Round(result * GetParameter(parameter))
		: throw new ArgumentException("Value is not a valid double", nameof(value));

  public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
	=> value is int result
		? result / GetParameter(parameter)
		: throw new ArgumentException("Value is not a valid integer", nameof(value));

  double GetParameter(object? parameter)
	=> parameter == null
	? Ratio
	: parameter switch
	{
		double d => d,
		int i => i,
		string s => double.TryParse(s, out var result)
			? result
			: throw new ArgumentException("Cannot parse number from the string", nameof(parameter)),
		_ => 1,
	};
}

Usage Syntax

XAML Usage

 <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:DoubleToIntConverter x:Key="DoubleToIntConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label Text="{Binding MyDouble, Converter={StaticResource DoubleToIntConverter}, ConverterParameter=2}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new StackLayout
    {
      Children = 
      {
          new Label().Bind(Label.TextProperty, nameof(ViewModel. MyDouble), converter: new DoubleToIntConverter { ConverterParameter = 2 } ),
      };
    }
  }
}

Add `.net6` target framework

Currently it's not possible to move UI into a separate project (that targets just .net6) due to MauiToolkint not targeting that framework. In XCT it was possible. Microsoft.Maui.Dependencies has .net6, so it's also possible in vanilla Maui.

Could you please add .net6 and another target framework to Maui toolkit (and Xamarin.CommunityToolkit.MauiCompat)?

Workardond:
Replace .net6 with net6.0-android30 (and all other frameworks that your app builds for) in all of app's projects.
Downsides: increases build time, and enables platform specific code in project where there shouldn't be any.

Localization classes migration

Migrate XCT localization classes to Maui CT.

Classes list:

  • LocalizationResourceManager
  • TranslateExtension
  • LocalizedString

I am willing to take this on myself

[Proposal] Inverted `bool` Converter

InvertedBoolConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The InvertedBoolConverter is a converter that allows users to convert a bool value binding to its inverted value

Detailed Design

InvertedBoolConverter.shared.cs

public class InvertedBoolConverter : ValueConverterExtension, IValueConverter
{
  public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) => InverseBool(value);

  public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => InverseBool(value);

  bool InverseBool(object? value)
  {
	  if (value is bool result)
		  return !result;
  
	  throw new ArgumentException("Value is not a valid boolean", nameof(value));
  }
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:InvertedBoolConverter x:Key="InvertedBoolConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label IsVisible="{Binding IsBusy, Converter={StaticResource InvertedBoolConverter}}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new StackLayout
    {
      Children = 
      {
          new Label().Bind(Label.IsVisibleProperty, nameof(ViewModel.IsBusy), converter: new InvertedBoolConverter()),
      };
    }
  }
}

[Proposal] Required String Validation Behavior

RequiredStringValidationBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The RequiredStringValidationBehavior is a Behavior that allows the user to determine if text input is equal to specific text. For example, an Entry control can be styled differently depending on whether a valid or an invalid text input is provided. Additional properties handling validation are inherited from ValidationBehavior.

Detailed Design

RequiredStringValidationBehavior.shared.cs

public class RequiredStringValidationBehavior : ValidationBehavior
{
    public static readonly BindableProperty RequiredStringProperty;
   
    public string? RequiredString { get; set; }

    protected override ValueTask<bool> ValidateAsync(object? value, CancellationToken token) => new ValueTask<bool>(value?.ToString() == RequiredString);
}

Usage Syntax

XAML Usage

<Entry>
    <Entry.Behaviors>
        <xct:RequiredStringValidationBehavior
            RequiredString="OK"
        />
    </Entry.Behaviors>
</Entry>

C# Usage

var entry = new Entry();
entry.Behaviors.Add(new RequiredStringValidationBehavior
{
    RequiredString = "OK"
});

[Proposal] Event-To-Command Behavior

EventToCommandBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Documentation
  • Sample

Summary

The EventToCommandBehavior is a Behavior that allows the user to invoke an ICommand through an Event. It is designed to associate ICommand to events exposed by controls that were not designed to support Commands. It allows you to map any arbitrary event on a control to an ICommand.

Detailed Design

public class EventToCommandBehavior : BaseBehavior<VisualElement>
{
    public static readonly BindableProperty EventNameProperty;
    public static readonly BindableProperty CommandProperty;
    public static readonly BindableProperty CommandParameterProperty;
    public static readonly BindableProperty EventArgsConverterProperty;

    public string? EventName { get; set; }
    public ICommand? Command { get; set; }
    public object? CommandParameter { get; set; }
    public IValueConverter EventArgsConverter { get; set; }
}

Usage Syntax

XAML Usage

<Button.Behaviors>
    <toolkit:EventToCommandBehavior
        EventName="Clicked"
        Command="{Binding MyCustomCommand}" />
</Button.Behaviors>

C# Usage

var button = new Button();
button.Behaviors.Add(new EventToCommandBehavior
{
    EventName = nameof(Button.Clicked),
    Command = new MyCustomCommand()
});

Drawbacks

Alternatives

Unresolved Questions

Add from Xamarin.Essentials: `Contacts`, `FilePicker`, `MediaPicker` and `WebAuthenticator`

The following APIs are included in Xamarin.Essentials, but will not be included in the first release of .NET MAUI Essentials:

To avoid breaking changes to developers using these Xamarin.Essentials APIs, let's bring them into the .NET MAUI Toolkit. This will also allow us to improve upon the existing APIs, making them more robust, to one day add them into .NET MAUI Essentials.

[Proposal] Character Validation Behavior

CharactersValidationBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The CharactersValidationBehavior is a Behavior that allows the user to validate text input depending on specified parameters. For example, an Entry control can be styled differently depending on whether a valid or an invalid text value is provided. This behavior includes built-in checks such as checking for a certain number of digits or alphanumeric characters. Additional properties handling validation are inherited from ValidationBehavior.

Motivation

Validating user text entry is common in most apps where user-input is required. This Behavior allows developers to easily implement validation based on the following attributes:

  • MinimumCharacterCount
  • MaximumCharacterCount
  • CharacterType
    • LowercaseLetter,
    • UppercaseLetter,
    • Letter
    • Digit,
    • Alphanumeric,
    • Whitespace,
    • NonAlphanumericSymbol
    • LowercaseLatinLetter
    • UppercaseLatinLetter
    • LatinLetter,
    • Any

Detailed Design

CharacterType.shared.cs

[Flags]
public enum CharacterType
{
  LowercaseLetter = 1,
  UppercaseLetter = 2,
  Letter = LowercaseLetter | UppercaseLetter,
  Digit = 4,
  Alphanumeric = Letter | Digit,
  Whitespace = 8,
  NonAlphanumericSymbol = 16,
  LowercaseLatinLetter = 32,
  UppercaseLatinLetter = 64,
  LatinLetter = LowercaseLatinLetter | UppercaseLatinLetter,
  Any = Alphanumeric | NonAlphanumericSymbol | Whitespace
}

CharactersValidationBehavior.shared.cs

public class CharactersValidationBehavior : TextValidationBehavior
{
    public static readonly BindableProperty CharacterTypeProperty;
    public static readonly BindableProperty MinimumCharacterCountProperty;
    public static readonly BindableProperty MaximumCharacterCountProperty;

    public CharacterType CharacterType { get; set; }
    public int MinimumCharacterCount { get; set; }
    public int MaximumCharacterCount { get; set; }
}

Usage Syntax

XAML Usage

<Entry>
    <Entry.Behaviors>
        <toolkit:CharactersValidationBehavior
            CharacterType="Digit"
            MaximumCharacterCount="10"
        />
    </Entry.Behaviors>
</Entry>

C# Usage

var phoneEntry = new Entry();
phoneEntry.Behaviors.Add(new CharactersValidationBehavior
{
    CharacterType = CharacterType.Digit,
    MaximumCharacterCount = 10
});

Alternatives

There are no known alternatives to this feature

Unresolved Questions

Is this scalable for validating additional types in the future?

[Proposal] `int`-To-`bool` Converter

IntToBoolConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The IntToBoolConverter is a converter that allows users to convert an incoming int value to a bool. If the incoming int value is 0, it will be converted to false. Any other incoming value will be converted to true.

Detailed Design

IntToBoolConverter.shared.cs

public class IntToBoolConverter : ValueConverterExtension, IValueConverter
{
  public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
	  => value is int result
		  ? result != 0
		  : throw new ArgumentException("Value is not a valid integer", nameof(value));

  public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
  {
	  if (value is bool result)
		  return result ? 1 : 0;
  
	  throw new ArgumentException("Value is not a valid boolean", nameof(value));
  }
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:IntToBoolConverter x:Key="IntToBoolConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label IsVisible="{Binding MyIntegerValue, Converter={StaticResource IntToBoolConverter}}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new StackLayout
    {
      Children = 
      {
          new Label().Bind(Label.IsVisibleProperty, nameof(ViewModel.MyIntegerValue), converter: new IntToBoolConverter()),
      };
    }
  }
}

[Proposal] IsStringNullOrEmpty Converters

IsStringNullOrEmptyConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The IsStringNullOrEmptyConverter is a converter that allows users to convert an incoming string binding to a bool value. This value represents if the incoming binding value is null or empty using string.IsNullOrEmpty.

The IsStringNotNullOrEmptyConverter is a converter that allows users to convert an incoming string binding to a bool value. This value represents if the incoming binding value is null or empty using string.IsNullOrEmpty.

Detailed Design

IsStringNullOrEmptyConverter.shared.cs

public class IsStringNullOrEmptyConverter : ValueConverterExtension, ICommunityToolkitValueConverter
{
  public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) => value is string stringValue ? string.IsNullOrEmpty(stringValue) : throw new InvalidArgumentException("Binding must be of type string")

  public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}

IsStringNotNullOrEmptyConverter.shared.cs

public class IsStringNullOrEmptyConverter : ValueConverterExtension, ICommunityToolkitValueConverter
{
  public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) => value is string stringValue ? !string.IsNullOrEmpty(stringValue) : throw new InvalidArgumentException("Binding must be of type string")

  public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct: IsStringNullOrEmptyConverter x:Key="IsStringNullOrEmptyConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label IsVisible="{Binding MyStringValue, Converter={StaticResource IsStringNullOrEmptyConverter}}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new StackLayout
    {
      Children = 
      {
          new Label().Bind(Label.IsVisibleProperty, nameof(ViewModel.MyString), converter: new IsStringNullOrEmptyConverter()),
      };
    }
  }
}

[Proposal] `ItemSelectedEventArgs` Converter

ItemSelectedEventArgs Converter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The ItemSelectedEventArgsConverter is a converter that allows users to extract the SelectedItem value from an SelectedItemChangedEventArgs object. It can subsequently be used in combination with EventToCommandBehavior

Detailed Design

ItemSelectedEventArgsConverter.shared.cs

public class ItemSelectedEventArgsConverter : ValueConverterExtension, IValueConverter
{
  public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
  {
    if (value == null)
      return null;
    
    return value is SelectedItemChangedEventArgs selectedItemChangedEventArgs
		? selectedItemChangedEventArgs.SelectedItem
		: throw new ArgumentException("Expected value to be of type SelectedItemChangedEventArgs", nameof(value));
  }

  public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:ItemSelectedEventArgsConverter x:Key="ItemSelectedEventArgsConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <ListView ItemsSource="{Binding Items}" HasUnevenRows="True">

        <ListView.Behaviors>
            <xct:EventToCommandBehavior EventName="ItemSelected"
                                              Command="{Binding ItemSelectedCommand}"
                                              EventArgsConverter="{StaticResource ItemSelectedEventArgsConverter}" />
        </ListView.Behaviors>

    </ListView>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    var behavior = new EventToCommandBehavior
    {
      EventName = nameof(ListView.ItemSelected),
      EventArgsConverter = new ItemSelectedEventArgsConverter()
    };
    behavior.SetBinding(EventToCommandBehavior.CommandProperty, nameof(ViewModel.ItemSelectedCommand);

    var listView = new ListView { HasUnevenRows = true }.Bind(ListView.ItemsSource, nameof(ViewModel.Items));
    listView.Behaviors.Add(behavior);

    Content = listView;
  }
}

[Proposal] Animation Behavior

Animation Behavior

Summary

AnimationBehavor is a Behavior that can be attached to any VisualElement, allowing developers to trigger the following animations via ICommand AnimateCommand:

  • #519
  • FlipHorizontalAnimation
  • FlipVerticalAnimation
  • RotateAnimation
  • ScaleAnimation
  • ShakeAnimation

Motivation

AnimationBehavior solves the following challenges:

  • Animations are not easily accomplished in XAML
  • Animations are not easily accomplished using ICommand

Following the implementation of AnimationBehavior, developers will easily be able to create bindable animations for any VisualElement

Detailed Design

AnimationBehavior.shared.cs

AnimationBehavior is a Behavior can be attached to any VisualElement.

AnimationBehavior implements EventToCommandBehavior.

public class AnimationBehavior : EventToCommandBehavior
{
    public static readonly BindableProperty AnimationTypeProperty;
    public static readonly BindableProperty AnimateCommandProperty;

    public AnimationBase? AnimationType { get; set; }
    public ICommand AnimateCommand { get; }
}

AnimationBase

AnimationBase is an abstract class implementing BindableObject that allows for creating animations.

public abstract class AnimationBase<TView> : BindableObject where TView : View
{
    public static readonly BindableProperty DurationProperty;
    public static readonly BindableProperty EasingTypeProperty

    public uint Duration { get; set; }
    public Easing Easing { get; set; }

    public abstract Task Animate(TView? view);
}

[Proposal] ProgressBar Animation Behavior

ProgressBarAnimationBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Documentation
  • Sample

Summary

A behavior that animates a ProgressBar based on double AnimateProgress.

Detailed Design

ProgressBarAnimationBehavior.shared.cs

public class ProgressBarAnimationBehavior : BaseBehavior<ProgressBar>
{
    public static readonly BindableProperty ProgressProperty;
    public static readonly BindableProperty LengthProperty;
    public static readonly BindableProperty EasingProperty;

    public double Progress { get; set; }
    public uint Length { get; set; }
    public Easing Easing { get; set; }
}

Usage Syntax

XAML Usage

<ProgressBar>
    <ProgressBarBehaviors>
        <toolkit: ProgressBarAnimationBehavior
            AnimateProgress = { Binding ProgressValue}
        />
    </ProgressBar.Behaviors>
</ProgressBar>

C# Usage

var behavior = new ProgressBarAnimationBehavior();
behavior.SetBinding(ProgressBarAnimationBehavior.AnimateProgressProperty, nameof(ViewModel.ProgressValue);

var progressBar = new ProgressBar();
phoneEntry.Behaviors.Add(behavior);

[Proposal] `ItemTappedEventArgs` Converter

ItemTappedEventArgsConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The ItemTappedEventArgsConverter is a converter that allows users to extract the Item value from an ItemTappedEventArgs object. It can subsequently be used in combination with EventToCommandBehavior.

Detailed Design

ItemTappedEventArgsConverter.shared.cs

public class ItemTappedEventArgsConverter : ValueConverterExtension, IValueConverter
{
  public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
  {
	if (value == null)
		return null;

	return value is ItemTappedEventArgs itemTappedEventArgs
		? itemTappedEventArgs.Item
		: throw new ArgumentException("Expected value to be of type ItemTappedEventArgs", nameof(value));
  }

  public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:ItemTappedEventArgsConverter x:Key="ItemTappedEventArgsConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <ListView ItemsSource="{Binding Items}" HasUnevenRows="True">

        <ListView.Behaviors>
            <xct:EventToCommandBehavior EventName="ItemTapped"
                                              Command="{Binding ItemTappedCommand}"
                                              EventArgsConverter="{StaticResource ItemTappedEventArgsConverter}" />
        </ListView.Behaviors>

    </ListView>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    var behavior = new EventToCommandBehavior
    {
      EventName = nameof(ListView.ItemTapped),
      EventArgsConverter = new ItemTappedEventArgsConverter()
    };
    behavior.SetBinding(EventToCommandBehavior.CommandProperty, nameof(ViewModel.ItemTappedCommand);

    var listView = new ListView { HasUnevenRows = true }.Bind(ListView.ItemsSource, nameof(ViewModel.Items));
    listView.Behaviors.Add(behavior);

    Content = listView;
  }
}

[Proposal] ImageResourceConverter

ImageResourceConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

Converts embedded image resource ID to it ImageSource

Detailed Design

ImageResourceConverter.shared.cs

public class ImageResourceConverter : IValueConverter
{
  public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
  {
	if (value == null)
		return null;

	if (value is not string imageId)
		throw new ArgumentException("Value is not a string", nameof(value));

	return ImageSource.FromResource(imageId, Application.Current.GetType().GetTypeInfo().Assembly);
  }

  public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             xmlns:myEnum="MyLittleApp.Models"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct: ImageResourceConverter x:Key="ImageResourceConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Image Source="{Binding ImageResource, Converter={StaticResource ImageResourceConverter}}" />

    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new StackLayout
    {
      Children = 
      {
          new Image().Bind(Image.SourceProperty, nameof(ViewModel.ImageResource), converter: new ImageResourceConverter()),
      };
    }
  }
}

Context menu contribution

Hi! I haven't really tried MAUI yet, but I was wondering if it makes sense to port my existing context menu plugin for XF to MAUI as a part of this community toolkit or if something would be implemented natively in the MAUI itself and porting it would be just a waste of time?

[Proposal] Multi Converter

MultiConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The MultiConverter is a converter that allows users to chain multiple converters together. The initial binding value is passed through to the first converter and, depending on what these converters return, that value is subsequently passed through to the next converter

Detailed Design

MultiConverter.shared.cs

public class MultiConverter : List<IValueConverter>, IValueConverter
{
  public object? Convert(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo culture)
	=> parameter is IList<MultiConverterParameter> parameters
	? this.Aggregate(value, (current, converter) => converter.Convert(current, targetType,
				parameters.FirstOrDefault(x => x.ConverterType == converter.GetType())?.Value, culture))
	: this.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));

  public object? ConvertBack(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo culture) => throw new NotImplementedException();
}

MultiConverterParameter.shared.cs

public class MultiConverterParameter : BindableObject
{
  public Type? ConverterType { get; set; }
  public object? Value { get; set; }
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:MultiConverter x:Key="MultiConverter">
                <xct:TextCaseConverter />
                <xct:NotEqualConverter />
            </xct:MultiConverter>
            <x:Array x:Key="MultiConverterParams" Type="{x:Type xct:MultiConverterParameter}">
                <xct:MultiConverterParameter ConverterType="{x:Type xct:TextCaseConverter}" Value="{x:Static xct:TextCaseType.Upper}" />
                <xct:MultiConverterParameter ConverterType="{x:Type xct:NotEqualConverter}" Value="ANDREI ROCKS ๐ŸŽธ" />
            </x:Array>
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label IsVisible="{Binding MyStringValue, Converter={StaticResource MultiConverter}, ConverterParameter={StaticResource MultiConverterParams}}" />

    </StackLayout>
</ContentPage>

C# Usage

var multiConverter = new MultiConverter 
{
  new TextCaseConverter(),
  new NotEqualConverter()
};

var converterParameters = new List<MultiConverterParameter>
{
  new MultiConverterParameter { ConverterType = typeof(TextCaseConverter), Value = TextCaseType.Upper }
  new MultiConverterParameter { ConverterType = typeof(NotEqualConverter), Value = "ANDREI ROCKS ๐ŸŽธ" }
    
};

new Label().Bind(Label.IsVisibleProperty, nameof(ViewModel.MyStringValue), converter: multiConverter, converterParameter = converterParameters );

[Proposal] Masked Behavior

MaskedBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Documentation
  • Sample

Summary

The MaskedBehavior is a Behavior that allows the user to define an input mask for data entry. Adding this behavior to an Entry control will force the user to only input values matching a given mask. Examples of its usage include input of a credit card number or a phone number

Detailed Design

MaskedBehavior.shared.cs

public class MaskedBehavior : BaseBehavior<InputView>
{
    public static readonly BindableProperty MaskProperty;
    public static readonly BindableProperty UnMaskedCharacterProperty;

    public string? Mask { get; set; }
    public char UnMaskedCharacter { get; set; }
}

Usage Syntax

XAML Usage

<Entry>
    <Entry.Behaviors>
        <xct:MaskedBehavior 
            Mask="AA-AA-AA" 
            UnMaskedCharacter="A"
        />
    </Entry.Behaviors>
</Entry>

C# Usage

var entry = new Entry();
entry.Behaviors.Add(new MaskedBehavior
{
    Mask="AA-AA-AA" 
    UnMaskedCharacter="A"
});

[Proposal] Implied Order Grid Behavior

ImpliedOrderGridBehavior

  • Proposed
  • Prototype: Not Started
  • Implementation: Not Started
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests: Not Started
  • Documentation: Not Started
  • Sample: Not Started

Summary

The ImpliedOrderGridBehavior enables you to automatically assign a Grid row and column to a view based on the order the view is added to the Grid. You only need to setup the row and column definitions and then add children to the Grid. You may still assign RowSpan and ColumnSpan to views and their values will be taken into account when assigning a row and column to a view. If a view has a user defined row or column value it will be honored.

Detailed Design

Usage Syntax

XAML Usage

<Grid x:Name="TestGrid" Margin="30" BackgroundColor="Gray">
    <Grid.Behaviors>
    <autoLayoutGrid:ImpliedOrderGridBehavior />
    </Grid.Behaviors>

    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <Grid .ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <!-- Row 0 -->
    <Label Grid.RowSpan="2"/>
    <Label />
    <Label />
    <Label Grid.ColumnSpan="2"/>

    <!-- Row 1 -->
    <Label/>
    <Label />
    <Label Grid.ColumnSpan="2" Grid.RowSpan="2" />

</Grid >

C# Usage

var grid = new Grid
{
    RowDefinitions = 
    {
         new RowDefinition { Height = GridLength.Auto },
         new RowDefinition { Height = GridLength.Auto },
    },

    ColumnDefinitions =
    {
         new ColumnDefinition { Width = GridLength.Auto },
         new ColumnDefinition { Width = GridLength.Auto },
    },
};
grid.Behaviors.Add(new ImpliedOrderGridBehavior());

grid.Children.Add(new Label());
grid.Children.Add(new Label());
grid.Children.Add(new Label());
grid.Children.Add(new Label());

Drawbacks

This is a very complicated feature that may or may not add much value.

Alternatives

The alternative is to manually assign each VisualElement to a row + column.

Unresolved Questions

I'm not sure if this feature has enough value to merit its complexity and maintenance.

[Proposal] User Stopped Typing Behavior

UserStoppedTypingBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Documentation: In Progress (MicrosoftDocs/CommunityToolkit#60)
  • Sample

Summary

The UserStoppedTypingBehavior is a Behavior that allows the user to trigger an action when a user has stopped data input an Entry. Examples of its usage include triggering a search when a user has stopped entering their search query.

Detailed Design

UserStoppedTypingBehavior.shared.cs

public class UserStoppedTypingBehavior : BaseBehavior<InputView>
{
    public static readonly BindableProperty CommandProperty;
    public static readonly BindableProperty CommandParameterProperty;
    public static readonly BindableProperty StoppedTypingTimeThresholdProperty;
    public static readonly BindableProperty MinimumLengthThresholdProperty;
    public static readonly BindableProperty ShouldDismissKeyboardAutomaticallyProperty;

    public ICommand? Command { get; set; }
    public object? CommandParameter { get; set; }
    public int StoppedTypingTimeThreshold { get; set; }
    public int MinimumLengthThreshold { get; set; }
    public bool ShouldDismissKeyboardAutomatically { get; set; )
}

## Usage Syntax
[usage]: #usage-syntax

### XAML Usage

```xml
<Entry>
    <Entry.Behaviors>
        <xct:UserStoppedTypingBehavior 
            Command="{Binding SearchCommand}"
            StoppedTypingTimeThreshold="1000"
            MinimumLengthThreshold="3"
            ShouldDismissKeyboardAutomatically="True" />
    </Entry.Behaviors>
</Entry>

C# Usage

var behavior = new UserStoppedTypingBehavior()
{
     StoppedTypingTimeThreshold = 1000,
     MinimumLengthThreshold = 3,
     ShouldDismissKeyboardAutomatically = true
};
behavior.SetBinding(UserStoppedTypingBehavior.CommandProperty, nameof(ViewModel. SearchCommand);

var entry = new Entry();
entry.Behaviors.Add(behavior);

[Proposal] Text Validation Behavior

TextValidationBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The TextValidationBehavior is a Behavior that allows the user to validate a given text depending on specified parameters. By adding this behavior to an Entry control it can be styled differently depending on whether a valid or an invalid text value is provided. It offers various built-in checks such as checking for a certain length or whether or not the input value matches a specific regular expression. Additional properties handling validation are inherited from ValidationBehavior.

Detailed Design

TextValidationBehavior.shared.cs

public class TextValidationBehavior : ValidationBehavior
{
    public static readonly BindableProperty MinimumLengthProperty;
    public static readonly BindableProperty MaximumLengthProperty;
    public static readonly BindableProperty DecorationFlagsProperty;
    public static readonly BindableProperty RegexPatternProperty;
    public static readonly BindableProperty RegexOptionsProperty;

    public int MinimumLength { get; set; }
    public int MaximumLength { get; set; }
    public TextDecorationFlags DecorationFlags { get; set; }
    public string? RegexPattern { get; set; }
    public RegexOptions RegexOptions { get; set; }
}

Usage Syntax

XAML Usage

<Entry>
    <Entry.Behaviors>
        <toolkit:TextValidationBehavior
            MinimumLength="1"
            MaximumLength="10"
        />
    </Entry.Behaviors>
</Entry>

C# Usage

var entry = new Entry();
entry.Behaviors.Add(new TextValidationBehavior
{
    MinimumLength = 1
    MaximumLength = 10
});

[Proposal] Multi-Validation Behavior

MultiValidationBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The MultiValidationBehavior is a Behavior that allows the user to combine multiple validators to validate text input depending on specified parameters. For example, an Entry control can be styled differently depending on whether a valid or an invalid text input is provided. By allowing the user to chain multiple existing validators together, it offers a high degree of customizability when it comes to validation. Additional properties handling validation are inherited from ValidationBehavior.

Motivation

Allows users to easily validate Entry.

User input is very common in apps today. This feature gives .NET MAUI developers an easy way to validate user inputs.

Detailed Design

MultiValidationBehavior.shared.cs

public class MultiValidationBehavior : ValidationBehavior
{
    public static readonly BindableProperty ErrorsProperty;
    public static readonly BindableProperty ErrorProperty;
    
    public IList<ValidationBehavior> { get; }
    public List<object?>? Errors { get; set; }

    public static object? GetError(BindableObject bindable);
    public static void SetError(BindableObject bindable, object value);
    protected override async ValueTask<bool> ValidateAsync(object? value, CancellationToken token); 
}

Usage Syntax

XAML Usage

<Entry>
    <Entry.Behaviors>
        <xct:MultiValidationBehavior
            x:Name="MultiValidation"
            InvalidStyle="{StaticResource InvalidEntryStyle}">

            <xct:NumericValidationBehavior
                xct:MultiValidationBehavior.Error="NaN"
            />
            <xct:NumericValidationBehavior
                MinimumValue="-10"
                xct:MultiValidationBehavior.Error="Min: -10"
            />
            <xct:NumericValidationBehavior
                MaximumValue="5"
                xct:MultiValidationBehavior.Error="Max: 5"
            />

        </xct:MultiValidationBehavior>
    </Entry.Behaviors>
</Entry>

C# Usage

var multiValidationBehavior = new MultiValidationBehavior();
multiValidationBehavior.Children.Add(new NumericValidationBehavior());

var entry = new Entry();
entry.Behaviors.Add(multiValidationBehavior);

[Proposal] CompareConverter

CompareConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Documentation
  • Sample

Summary

The CompareConverter is a Converter that converts an object of a type implementing IComparable, and returns the comparison result as a bool if no objects were specified through the TrueObject and/or FalseObject properties. If values are assigned to the TrueObject and/or FalseObject properties, the CompareConverter returns the respective object assigned.

Detailed Design

CompareConverter.shared.cs

public sealed class CompareConverter : CompareConverter<object>
{
}

public abstract class CompareConverter<TObject> : ValueConverterExtension, IValueConverter
{
  public IComparable? ComparingValue { get; set; }
  public OperatorType ComparisonOperator { get; set; }
  public TObject? TrueObject { get; set; }
  public TObject? FalseObject { get; set; }
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>    
            <x:Double x:Key="ComparingValue">2</x:Double>
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>
        <Slider x:Name="slider" HorizontalOptions="FillAndExpand" Maximum="4"/>
        <Label Text="{Binding Source={x:Reference slider}, Path=Value}"/>
        <Label BackgroundColor="{Binding Source={x:Reference slider}, Path=Value,
                                Converter={xct:CompareConverter ComparisonOperator=Greater,
                                          ComparingValue={StaticResource ComparingValue},
                                          FalseObject=Red, TrueObject=Green}}"
                   Text="{Binding Source={x:Reference slider}, Path=Value,
                            Converter={xct:CompareConverter ComparisonOperator=Greater,
                                    ComparingValue={StaticResource ComparingValue}}}"/>
    </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new StackLayout
    {
      Children = 
      {
          new Slider { Maximum = 4}.FillExpandHorizontal().Assign(out var slider),
          new Label().Bind(Label.TextProperty, nameof(Slider.Value), source: slider),
          new Label()
            .Bind(Label.BackgroundColorProperty, nameof(Slider.Value), source: slider, converter = new  CompareConverter { ComparingValue = 2, ComparisonOperator = OperatorType.Greater, FalseObject = Colors.Red, TrueObject = Colors.Green }),
            .Bind(Label.TextProperty, named(Slider.Value), source: slider, converter: new CompareConverter { ComparingValue = 2, ComparisonOperator = OperatorType.Greater})
    };
  }
}

[Proposal] Set Focus When Entry Completed Behavior

SetFocusOnEntryCompletedBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Documentation
  • Sample

Summary

The SetFocusOnEntryCompletedBehavior is a Behavior that gives focus to a specified VisualElement when an Entry is completed. For example, a page might have several Entrys in sequence, and this makes it convenient to the user if completing an Entry automatically switched focus to the next Entry.

Motivation

Many apps contain input forms, e.g. Log In page, Sign Up page, Profile page, etc.

It is frustrating for users to enter text, dismiss the keyboard, then manually tap the next text box. SetFocusOnEntryCompletedBehavior allows us to Shepard our users to the next logical text box.

Detailed Design

SetFocusedOnEntryCompletedBehavior.shared.cs

public class SetFocusOnEntryCompletedBehavior : BaseBehavior<VisualElement>
{
    public static readonly BindableProperty NextElementProperty;
    
    public static VisualElement GetNextElement(BindableObject view);
    public static void SetNextElement(BindableObject view, VisualElement value) 
}

Alternatives

.NET MAUI already implements a Entry.ReturnCommand that has a similar functionality.

When Entry.ReturnType is ReturnType.Next, .NET MAUI will auto-focus on the next focusable view: xamarin/Xamarin.Forms#6706

Unresolved Questions

Does implementing SetFocusedOnEntryCompletedBehavior provide enough value over Entry.ReturnType = ReturnType.Next?

[Proposal] IsListNullOrEmptyConverter Converters

IsListNullOrEmpty Converters

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The IsListNullOrEmpty is a converter that allows users to convert an incoming binding that implements IEnumerable to a bool value. This value represents if the incoming binding value is either null or an empty list.

The IsListNotNullOrEmpty is a converter that allows users to convert an incoming binding that implements IEnumerable to a bool value. This value represents if the incoming binding value is not null or an empty list.

Detailed Design

IsListNullOrEmptyConverter.shared.cs

public class IsListNullOrEmptyConverter : ValueConverterExtension, IValueConverter
{
  public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) => ConvertInternal(value);

  internal static bool ConvertInternal(object? value)
  {
	if (value == null)
		return true;

	if (value is IEnumerable list)
		return !list.GetEnumerator().MoveNext();

	throw new ArgumentException("Value is not a valid IEnumerable or null", nameof(value));
  }

  public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}

IsListNotNullOrEmptyConverter.shared.cs

public class IsListNotNullOrEmptyConverter : ValueConverterExtension, IValueConverter
{
  public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) => !ListIsNullOrEmptyConverter.ConvertInternal(value);

  public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}

Usage Syntax

XAML Usage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct: IsListNullOrEmptyConverter x:Key="IsListNullOrEmptyConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label IsVisible="{Binding MyListValue, Converter={StaticResource IsListNullOrEmptyConverter}}" />

    </StackLayout>
</ContentPage>

C# Usage

 class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new Label().Bind(Label.IsVisible, nameof(ViewMode.MyList), converter: new IsListNullOrEmptyConverter();
  }
}

[Proposal] `enum`-To-`int` Converter

EnumToIntConverter

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation: Started at MicrosoftDocs/CommunityToolkit#57

Summary

The EnumToIntConverter is a converter that allows you to convert a standard Enum (extending int) to its underlying primitive int type. It is useful when binding a collection of values representing an enumeration type with default numbering to a control such as a Picker.

For localization purposes or due to other requirements, the enum values often need to be converted to a human-readable string. In this case, when the user selects a value, the resulting SelectedIndex can easily be converted to the underlying enum value without requiring additional work in the associated ViewModel.

Detailed Design

EnumToIntConverter.shared.cs

public class EnumToIntConverter : ValueConverterExtension, IValueConverter
{
  public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) =>value is Enum ? System.Convert.ToInt32(value) : throw new ArgumentException($"{value?.GetType().Name} is not a valid enumeration type");

  public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo? culture) => value is int enumIntVal && Enum.IsDefined(targetType, enumIntVal) ? Enum.ToObject(targetType, enumIntVal) : throw new ArgumentException($"{value} is not valid for {targetType.Name}");
}

Usage Syntax

XAML Usage

<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage
  x:Class="Xamarin.CommunityToolkit.Sample.Pages.Converters.EnumToIntConverterPage"
  xmlns="http://xamarin.com/schemas/2014/forms"
  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
  xmlns:vm="clr-namespace:Xamarin.CommunityToolkit.Sample.ViewModels.Converters"
  xmlns:xct="http://xamarin.com/schemas/2020/toolkit">
  <ContentPage.BindingContext>
    <vm:EnumToIntConverterViewModel />
  </ContentPage.BindingContext>
  
  <StackLayout Padding="10,10" Spacing="10">
    <Picker ItemsSource="{Binding AllStates}" SelectedIndex="{Binding SelectedState, Converter={xct:EnumToIntConverter}}" />
    <Label Text="{Binding Path=SelectedState, Converter={xct:EnumToIntConverter}}" />
  </StackLayout>
</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
    Content = new StackLayout
    {
      Children = 
      {
        new Picker()
          .Bind(Picker.ItemSource, nameof(ViewModel.AllStates)
          .Bind(Picker.SelectedIndex, nameof(ViewModel.SelectedState),

          new Label()
            .Bind(Label.TextPropoerty, nameof(ViewModel.SelectedState), converter: new EnumToIntConverter()),
      };
    }
  }
}

[Proposal] Uri Validation Behavior

UriValidationBehavior

  • Proposed
  • Prototype
  • Implementation
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests
  • Sample
  • Documentation

Summary

The UriValidationBehavior is a behavior that allows users to determine whether or not text input is a valid URI. For example, an Entry control can be styled differently depending on whether a valid or an invalid URI is provided. Additional properties handling validation are inherited from ValidationBehavior.

Detailed Design

UriValidationBehavior.shared.cs

public class UriValidationBehavior : TextValidationBehavior
{
    public static readonly BindableProperty UriKindProperty;

    public UriKind UriKind { get; set; }

    protected override async ValueTask<bool> ValidateAsync(object? value, CancellationToken token) => await base.ValidateAsync(value, token).ConfigureAwait(false) && Uri.IsWellFormedUriString(value?.ToString(), UriKind);
}

Usage Syntax

XAML Usage

<Entry>
    <Entry.Behaviors>
        <toolkit:UriValidationBehavior
            UriKind="Absolute"
            InvalidStyle="{StaticResource InvalidEntryStyle}"
        />
    </Entry.Behaviors>
</Entry>

C# Usage

var entry = new Entry();
entry.Behaviors.Add(new UriValidationBehavior
{
    UriKind = UriKind.Absolute,
    InvalidStyle = new InvalidEntryStyle()
});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.