Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.

Implement a generic version of the EventToCommandBehavior #1010

Merged
merged 17 commits into from
Mar 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
using System;
using System.Reflection;
using Xamarin.CommunityToolkit.Behaviors;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.CommunityToolkit.UnitTests.Mocks;
using Xamarin.Forms;
using NUnit.Framework;

namespace Xamarin.CommunityToolkit.UnitTests.Behaviors
{
public class EventToCommandBehaviorGeneric_Tests
{
[SetUp]
public void Setup() => Device.PlatformServices = new MockPlatformServices();

[Test]
public void ArgumentExceptionIfSpecifiedEventDoesNotExist()
{
var listView = new ListView();
var behavior = new EventToCommandBehavior<Coffee>
{
EventName = "Wrong Event Name"
};
Assert.Throws<ArgumentException>(() => listView.Behaviors.Add(behavior));
}

[Test]
public void NoExceptionIfSpecifiedEventExists()
{
var listView = new ListView();
var behavior = new EventToCommandBehavior<Coffee>
{
EventName = nameof(ListView.ItemTapped)
};
listView.Behaviors.Add(behavior);
}

[Test]
public void NoExceptionIfAttachedToPage()
{
var page = new ContentPage();
var behavior = new EventToCommandBehavior<Coffee>
{
EventName = nameof(Page.Appearing)
};
page.Behaviors.Add(behavior);
}

[Test]
public void NoExceptionWhenTheEventArgsAreNotNull()
{
var vm = new ViewModelCoffe();
var behavior = new EventToCommandBehavior<Coffee>
{
EventName = nameof(ListView.ItemTapped),
EventArgsConverter = new ItemSelectedEventArgsConverter(),
Command = vm.SelectedCommand
};

Assert.Null(vm.CoffeeName);
var coffe = new Coffee { Id = 1, Name = "Café" };
var eventArgs = new SelectedItemChangedEventArgs(coffe, 1);

var notNullArgs = new object?[] { null, eventArgs };

TriggerEventToCommandBehavior(behavior, notNullArgs);

Assert.AreEqual(coffe.Name, vm.CoffeeName);
}

[Test]
public void NoExceptionWhenTheEventArgsAreNotNull_InheritedType()
{
var vm = new ViewModelCoffe();
var behavior = new EventToCommandBehavior<Coffee>
{
EventName = nameof(ListView.ItemTapped),
EventArgsConverter = new ItemSelectedEventArgsConverter(),
Command = vm.SelectedCommand
};

Assert.Null(vm.CoffeeName);
var coffe = new Starbucks { Id = 1, Name = "Latte" };
var eventArgs = new SelectedItemChangedEventArgs(coffe, 1);

var notNullArgs = new object?[] { null, eventArgs };

TriggerEventToCommandBehavior(behavior, notNullArgs);

Assert.AreEqual(coffe.Name, vm.CoffeeName);
}

[Test]
public void ParameterOfTypeInt()
{
var vm = new ViewModelCoffe();
var behavior = new EventToCommandBehavior<int>
{
EventName = nameof(ListView.ItemTapped),
Command = vm.SelectedCommand,
CommandParameter = 2
};

var nullArgs = new object?[] { null, null };

TriggerEventToCommandBehavior(behavior, nullArgs);
}

[Test]
public void NoExceptionWhenTheSelectedItemIsNull()
{
var vm = new ViewModelCoffe();
var behavior = new EventToCommandBehavior<Coffee>
{
EventName = nameof(ListView.ItemTapped),
EventArgsConverter = new ItemSelectedEventArgsConverter(),
Command = vm.SelectedCommand
};

Assert.Null(vm.CoffeeName);
var coffeNull = default(Coffee);
var notNullArgs = new object?[] { null, new SelectedItemChangedEventArgs(coffeNull, -1) };

TriggerEventToCommandBehavior(behavior, notNullArgs);

Assert.Null(vm.CoffeeName);
}

static void TriggerEventToCommandBehavior<T>(EventToCommandBehavior<T> eventToCommand, object?[] args)
{
var method = eventToCommand.GetType().GetMethod("OnTriggerHandled", BindingFlags.Instance | BindingFlags.NonPublic);
method?.Invoke(eventToCommand, args);
}

class Starbucks : Coffee
{
}

class Coffee
{
public int Id { get; set; }

public string Roaster { get; set; } = string.Empty;

public string? Name { get; set; }

public string Image { get; set; } = string.Empty;
}

class ViewModelCoffe
{
public Command<Coffee> SelectedCommand { get; set; }

public string? CoffeeName { get; set; }

public ViewModelCoffe()
{
SelectedCommand = new Command<Coffee>(Selected);
}

void Selected(Coffee coffee)
{
if (coffee == null)
return;

CoffeeName = coffee?.Name;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;

namespace Xamarin.CommunityToolkit.Behaviors
{
/// <summary>
/// This <see cref="EventToCommandBehavior"/> cast the sender object to a specific type defined by the user.
/// </summary>
/// <typeparam name="TType">The type that you want to receive in your <see cref="Xamarin.Forms.Command"/> </typeparam>
public sealed class EventToCommandBehavior<TType> : EventToCommandBehavior
{
protected override void OnTriggerHandled(object? sender = null, object? eventArgs = null)
{
var parameter = CommandParameter
?? EventArgsConverter?.Convert(eventArgs, typeof(object), null, null)
?? eventArgs;

if (parameter is not TType)
{
// changing it to the default value to avoid a cast exception
parameter = default(TType);
}

var command = Command;
if (command?.CanExecute(parameter) ?? false)
command.Execute(parameter);
}
}
}