2

For the animation of UI elements like a progress bar, even in Microsofts documentation, there are always suggestions that (only) work from code behind, like this:

await progressBar.ProgressTo(0.75, 500, Easing.Linear);

I could not find a way to do this from a ViewModel, as it is not possible (and not intended) to access XAML elements from there. I tried to this with data binding, using BindableProperty. Could not get it to work. So, am I holding it wrong, or is it not possible?

I created a Drawable class:

public class ProgressArcDrawable : GraphicsView, IDrawable
{

    public double ArcProgress
    {
        get => (double)GetValue(ArcProgressProperty);
        set => SetValue(ArcProgressProperty, value);
    }

    public float Stroke
    {
        get => (float)GetValue(StrokeProperty);
        set => SetValue(StrokeProperty, value);
    }

    public Color ArcColor
    {
        get => (Color)GetValue(ArcColorProperty);
        set => SetValue(ArcColorProperty, value);
    }

    public static readonly BindableProperty ArcProgressProperty =
        BindableProperty.Create(nameof(ArcProgress), typeof(double), typeof(ProgressArcDrawable));

    public static readonly BindableProperty StrokeProperty =
        BindableProperty.Create(nameof(Stroke), typeof(float), typeof(ProgressArcDrawable));

    public static readonly BindableProperty ArcColorProperty =
        BindableProperty.Create(nameof(ArcColor), typeof(Color), typeof(ProgressArcDrawable));

    public void Draw(ICanvas canvas, RectF dirtyRect)
    {
        var endAngle = 90 - (int)Math.Round(ArcProgress * 360, MidpointRounding.AwayFromZero);
        canvas.StrokeColor = ArcColor;
        canvas.StrokeSize = Stroke;
        canvas.DrawArc(Stroke / 2, Stroke / 2, (dirtyRect.Width - Stroke), (dirtyRect.Height - Stroke), 90, endAngle, false, false);
    }
}

As I am using the MVVM toolkit, I created ObservableProperties in my viewmodel like so:

[ObservableProperty]
double arcProgress;

In my view I integrate the drawable with the data bindings:

<ContentPage.Resources>
    <drawables:ProgressArcDrawable
        x:Key="progressArcDrawable"
        ArcProgress="{Binding ArcProgress}"
        Stroke="20"
        ArcColor="{Binding Scrum.ScrumTheme.AccentColor}" />
</ContentPage.Resources>
...
<Grid>
       <GraphicsView
                Drawable="{StaticResource progressArcDrawable}"
                HeightRequest="350"
                WidthRequest="350" />
</Grid>

The only binding that works, is the one that sets the stroke as a discrete value. The AccentColor binding works on other elements on the page, so there is data in there, but it does not work for the drawable. And thats the same for the ArcProgress, which should change the progress arc once a second.

10
  • 1
    Have you already searched for third party components on nuget.org that might do what you need? Commented Dec 23, 2022 at 15:26
  • 2
    For Perform Animations in MVVM you usually just create methods in your View and then in your VM you create Actions that trigger those methods... Commented Dec 23, 2022 at 16:28
  • @ewerspej Thanks for that hint. I see, that you offer a progress bar there as well. But all stuff that can be found do not answer my primary question: How to control that from the viewmodel? If I control it from code behind, I can use the .Invalidate method on the xaml element to redraw it when it changes. Can't do that from the viewmodel either ... Commented Dec 23, 2022 at 19:29
  • 1
    Like Freaky Ali correctly pointed out, you can call View methods from the ViewModel without breaking MVVM by applying inversion of control (IoC). You could define a delegate in your ViewModel which is View-agnostic and set the delegate in your View's code behind. Now, usually using a property binding should actually suffice, I don't know why your binding doesn't work. It could be related to the fact that the Drawable instance comes from a ResourceDictionary. Commented Dec 23, 2022 at 19:51
  • 1
    Calling a Method in View's CodeBehind from ViewModel? / Registering an action in viewmodel from view is one way to have code in viewmodel able to trigger code in view. Commented Dec 23, 2022 at 23:08

1 Answer 1

0

If you are still looking for this, here is how I did it.

public class ProgressArcDrawable : GraphicsView, IDrawable
{
    public ProgressArcDrawable()
    {
        Drawable = this;
    }

    //...
}

in your XAML file

<local:ProgressArcDrawable ArcProgress ="{Binding ProgressValue}"
       HeightRequest="50" WidthRequest="50"/>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.