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

.NET MAUI中如何将分数传递给GraphicsView的IDrawable

.NET MAUI Drawable绑定属性不生效的解决方案

问题核心原因

直接让IDrawable继承BindableObject并添加绑定属性的方式不可行,因为:

  1. Drawable并非VisualElement,无法自动继承父级的绑定上下文,绑定关系无法正确解析;
  2. 即使绑定值发生变化,Drawable也没有内置机制通知Canvas触发重绘,导致绘制内容无法更新。

最优解决方案:以自定义View作为绑定载体

将绑定逻辑放在自定义ScoreGaugeView(继承自View)中,由View接收绑定值后传递给Drawable,并触发重绘。

1. 实现自定义ScoreGaugeView

public class ScoreGaugeView : View
{
    private readonly ScoreGaugeDrawable _drawable;

    // 定义View的绑定属性,用于接收外部传入的分数
    public static readonly BindableProperty ScoreProperty =
        BindableProperty.Create(nameof(Score), typeof(double), typeof(ScoreGaugeView), 0.0, 
            propertyChanged: OnScoreUpdated);

    public double Score
    {
        get => (double)GetValue(ScoreProperty);
        set => SetValue(ScoreProperty, value);
    }

    public ScoreGaugeView()
    {
        _drawable = new ScoreGaugeDrawable();
        BackgroundColor = Colors.Transparent;
    }

    // 绑定值变化时,更新Drawable的分数并触发重绘
    private static void OnScoreUpdated(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is ScoreGaugeView view && newValue is double score)
        {
            view._drawable.Score = score;
            view.Invalidate(); // 通知Canvas重新绘制
        }
    }

    protected override void OnDraw(ICanvas canvas, RectF dirtyRect)
    {
        base.OnDraw(canvas, dirtyRect);
        _drawable.Draw(canvas, dirtyRect);
    }
}

2. 实现ScoreGaugeDrawable(无需继承BindableObject)

public class ScoreGaugeDrawable : IDrawable
{
    // 普通属性接收分数,负责绘制逻辑
    public double Score { get; set; }

    public void Draw(ICanvas canvas, RectF dirtyRect)
    {
        // 示例绘制逻辑:根据Score绘制仪表指针
        canvas.SaveState();

        // 绘制表盘背景
        canvas.StrokeColor = Colors.Gray;
        canvas.StrokeSize = 2;
        canvas.DrawCircle(dirtyRect.Center.X, dirtyRect.Center.Y, dirtyRect.Width / 2 - 10);

        // 根据分数计算指针角度(假设分数范围0-100,对应0-360度)
        var angle = (Score / 100) * 360;
        var radians = angle * Math.PI / 180;
        var pointerLength = dirtyRect.Width / 2 - 20;
        var endX = dirtyRect.Center.X + (float)(Math.Cos(radians - Math.PI / 2) * pointerLength);
        var endY = dirtyRect.Center.Y + (float)(Math.Sin(radians - Math.PI / 2) * pointerLength);

        // 绘制指针
        canvas.StrokeColor = Colors.Red;
        canvas.StrokeSize = 3;
        canvas.DrawLine(dirtyRect.Center.X, dirtyRect.Center.Y, endX, endY);

        canvas.RestoreState();
    }
}

3. XAML中使用自定义View

在CollectionView的ItemTemplate中直接绑定Procedure.AvgScore到ScoreGaugeView.Score:

<CollectionView ItemsSource="{Binding Procedures}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <VerticalStackLayout Padding="10">
                <!-- Label正常显示验证绑定上下文 -->
                <Label Text="{Binding AvgScore, StringFormat='平均分: {0:F1}'}" />
                <!-- 自定义仪表View绑定分数 -->
                <local:ScoreGaugeView Score="{Binding AvgScore}" 
                                      WidthRequest="200" 
                                      HeightRequest="200" 
                                      Margin="0,10,0,10"/>
            </VerticalStackLayout>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

备选方案:手动为Drawable设置绑定上下文

如果坚持让Drawable使用绑定属性,可在ScoreGaugeView中手动设置绑定上下文并监听变化,但这种方式不如上述方案简洁:

// ScoreGaugeDrawable继承BindableObject
public class ScoreGaugeDrawable : BindableObject, IDrawable
{
    public static readonly BindableProperty ScoreProperty =
        BindableProperty.Create(nameof(Score), typeof(double), typeof(ScoreGaugeDrawable), 0.0);

    public double Score
    {
        get => (double)GetValue(ScoreProperty);
        set => SetValue(ScoreProperty, value);
    }

    // 绘制逻辑同上
}

// ScoreGaugeView中手动绑定
public ScoreGaugeView()
{
    _drawable = new ScoreGaugeDrawable();
    _drawable.BindingContext = this;
    _drawable.SetBinding(ScoreGaugeDrawable.ScoreProperty, new Binding(nameof(Score)));
    BackgroundColor = Colors.Transparent;
    
    // 监听View的Score变化触发重绘
    this.PropertyChanged += (s, e) =>
    {
        if (e.PropertyName == nameof(Score))
            Invalidate();
    };
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 05:42:38