You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI中MVVM模式下图形元素动态行为实现问题

问题分析与解决方案

你的方案存在三个核心问题,修正后完全可以通过MVVM实现进度弧的动画控制:


问题1:类职责混淆

你让ProgressArcDrawable同时继承GraphicsView并实现IDrawable,这混淆了控件容器和绘制逻辑的职责。GraphicsView是用来展示IDrawable的UI控件,而IDrawable仅负责绘制逻辑,两者应该分离。

问题2:未触发UI重绘

你的BindableProperty没有设置propertyChanged回调,当ArcProgress或ArcColor值变化时,不会通知UI重新绘制,导致界面无更新。

问题3:资源绑定上下文缺失

放在ContentPage.Resources中的对象默认不会继承页面的BindingContext,所以{Binding ArcProgress}无法找到ViewModel中的属性,绑定直接失效;而硬编码的Stroke="20"是直接赋值,不受绑定上下文影响,因此能生效。


修正后的实现代码

步骤1:分离绘制逻辑与控件

创建独立的绘制类ArcDrawable,仅负责绘制逻辑:

public class ArcDrawable : IDrawable
{
    public double ArcProgress { get; set; }
    public float Stroke { get; set; }
    public Color ArcColor { get; set; }

    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);
    }
}

步骤2:创建自定义控件ArcProgressView

继承GraphicsView,定义BindableProperty并在属性变化时触发重绘:

public class ArcProgressView : GraphicsView
{
    private readonly ArcDrawable _drawable;

    public ArcProgressView()
    {
        _drawable = new ArcDrawable();
        Drawable = _drawable;
    }

    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(ArcProgressView),
            0.0,
            propertyChanged: (bindable, oldVal, newVal) =>
            {
                if (bindable is ArcProgressView view)
                {
                    view._drawable.ArcProgress = (double)newVal;
                    view.Invalidate(); // 触发UI重绘
                }
            });

    public static readonly BindableProperty StrokeProperty =
        BindableProperty.Create(
            nameof(Stroke),
            typeof(float),
            typeof(ArcProgressView),
            0f,
            propertyChanged: (bindable, oldVal, newVal) =>
            {
                if (bindable is ArcProgressView view)
                {
                    view._drawable.Stroke = (float)newVal;
                    view.Invalidate();
                }
            });

    public static readonly BindableProperty ArcColorProperty =
        BindableProperty.Create(
            nameof(ArcColor),
            typeof(Color),
            typeof(ArcProgressView),
            Colors.Black,
            propertyChanged: (bindable, oldVal, newVal) =>
            {
                if (bindable is ArcProgressView view)
                {
                    view._drawable.ArcColor = (Color)newVal;
                    view.Invalidate();
                }
            });
}

步骤3:修改XAML布局

直接使用自定义控件,无需放在资源中:

<Grid>
    <controls:ArcProgressView
        HeightRequest="350"
        WidthRequest="350"
        ArcProgress="{Binding ArcProgress}"
        Stroke="20"
        ArcColor="{Binding Scrum.ScrumTheme.AccentColor}" />
</Grid>

步骤4:ViewModel代码保持不变

[ObservableProperty]
double arcProgress;

后续动画实现

在ViewModel中可以通过定时器或异步方法更新ArcProgress值,比如每秒递增:

private async Task UpdateProgressAsync()
{
    while (ArcProgress < 1.0)
    {
        ArcProgress += 0.1;
        await Task.Delay(1000);
    }
}

内容的提问来源于stack exchange,提问作者Lilly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 05:30:45