In a Xamarin.Forms project, I'm trying to repeatedly translate an image from a position A(x,y) to a position B(x,y) and back, from B to A. To achieve this, I read that is possible to customize behaviors.
I extend Behavior class, overriding OnAttachedTo and OnDetachingFrom. And in the OnAttachedTo method I start a Task which repeatedly does the two translations.
This is my Behavior class:
public class MoveImageBehavior : Behavior<Image>
{
private Image _Image = null;
public static readonly BindableProperty AnimatedProperty = BindableProperty.Create("Animated", typeof(bool), typeof(ImageAnimatedBehavior), defaultValue: false);
public bool Animated
{
get { return (bool)GetValue(AnimatedProperty); }
set { SetValue(AnimatedProperty, value); }
}
protected override void OnAttachedTo(Image image)
{
base.OnAttachedTo(image);
_Image = image;
Animated = true;
Task.Run(AnimateImage);
}
protected override void OnDetachingFrom(Image image)
{
base.OnDetachingFrom(image);
_Image = null;
}
private async void AnimateImage()
{
while (_Image != null && Animated)
{
await _Image.TranslateTo(100, 100, 1000);
await _Image.TranslateTo(0, 0, 1000);
}
}
}
The image in the xaml file:
<ContentView>
<Grid>
<Image x:Name="image_translating" Source="my_icon" Aspect="AspectFit">
<Image.Behaviors>
<behaviors:MoveImageBehavior Animated="{Binding ImageTranslating}" BindingContext="{Binding BindingContext, Source={x:Reference image_translating}}"/>
</Image.Behaviors>
</Image>
</Grid>
</ContentView>
The Image repeatedly translates correctly as I want, but I'm not able to stop the while routine. The property binding doesn't work when Animated is set to false in the ViewModel and OnDetachingFrom is never called. What am I doing wrong? Any suggestions?
Through the document, we can see that:
The OnDetachingFrom method is fired when the behavior is removed from the control. This method receives a reference to the control to which it is attached, and is used to perform any required cleanup. For example, you could unsubscribe from an event on a control to prevent memory leaks.
It will only fired when you remove the behavior from the image. I would give you an example about how to stop the animation:
I defined an bool property in the code behind to control stop or not stop:
public bool showA = true;
And I add a button as an example to stop the animation:
private void Button_Clicked(object sender, EventArgs e)
{
showA = !showA;
if (showA)
{
image_translating.Behaviors.Add(new MoveImageBehavior());
}
else
{
var toRemove = image_translating.Behaviors.FirstOrDefault(b => b is MoveImageBehavior);
if (toRemove != null)
{
image_translating.Behaviors.Remove(toRemove);
}
}
}
Also in your OnDetachingFrom
method, do not set the image to null, it will cause a null expection, just set the Animated to false :
protected override void OnDetachingFrom(Image image)
{
base.OnDetachingFrom(image);
Animated = false;
}
You can convert my click event to some binding in your project and make it work.