xamarinxamarin.formsmvvmcross

How to stop a Xamarin Forms behavior used to asynchronously translate an image?


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?


Solution

  • 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.

    Refer: creating-a-xamarinforms-behaviorer