Hey there, everyone. I made a simple behavior which provides number from/to animation (e.g. counter). I'm sharing the code below for whoever needs it and I'm looking to some suggestions on how to improve this code .
https://youtube.com/watch?v=3ciQWIaARN0
using System;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace MyApp.Behaviors
{
public class NumericLabelFromToBehavior : Behavior<Label>
{
public static readonly BindableProperty TextProperty =
BindableProperty.Create(
nameof(Text),
typeof(double),
typeof(LabelHighlightAndStepOnChangeBehavior),
null,
propertyChanged: OnTextPropertyChanged);
public double Text
{
get { return (double)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public Label AssociatedObject { get; private set; }
private static CancellationTokenSource cancellationTokenSource;
private static long currentValue = 0;
private static void OnTextPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var behavior = (NumericLabelFromToBehavior)bindable;
if (behavior.AssociatedObject == null)
{
return;
}
cancellationTokenSource?.Cancel();
cancellationTokenSource = new CancellationTokenSource();
Task.Run(() => AnimateText(behavior.AssociatedObject, currentValue, newValue, cancellationTokenSource.Token), cancellationTokenSource.Token);
}
private static async void AnimateText(BindableObject bindable, object fromValue, object toValue, CancellationToken cancellationToken)
{
long.TryParse(fromValue.ToString(), out long initialValue);
long.TryParse(toValue.ToString(), out long finalValue);
if (initialValue < finalValue)
{
var step = Math.Max(1, Convert.ToInt32((finalValue - initialValue) / 50.0) - 1);
while (initialValue < finalValue)
{
if (cancellationToken.IsCancellationRequested)
break;
if ((initialValue + step) <= finalValue)
initialValue += step;
else
initialValue = finalValue;
currentValue = initialValue;
Device.BeginInvokeOnMainThread(() => (bindable as Label).Text = initialValue.ToString("C"));
await Task.Delay(50);
}
}
else if (initialValue > finalValue)
{
var step = -Math.Max(1, Convert.ToInt32((initialValue - finalValue) / 50.0) - 1);
while (initialValue > finalValue)
{
if (cancellationToken.IsCancellationRequested)
break;
if ((initialValue + step) >= finalValue)
initialValue += step;
else
initialValue = finalValue;
currentValue = initialValue;
Device.BeginInvokeOnMainThread(() => (bindable as Label).Text = initialValue.ToString("C"));
await Task.Delay(50);
}
}
}
protected override void OnAttachedTo(Label bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject = bindable;
if (bindable.BindingContext != null)
{
BindingContext = bindable.BindingContext;
}
bindable.BindingContextChanged += OnBindingContextChanged;
}
protected override void OnDetachingFrom(Label bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= OnBindingContextChanged;
AssociatedObject = null;
cancellationTokenSource.Dispose();
}
private void OnBindingContextChanged(object sender, EventArgs e)
{
OnBindingContextChanged();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
BindingContext = AssociatedObject.BindingContext;
}
}
}