.NET MAUI中如何将分数传递给GraphicsView的IDrawable
.NET MAUI Drawable绑定属性不生效的解决方案
问题核心原因
直接让IDrawable继承BindableObject并添加绑定属性的方式不可行,因为:
Drawable并非VisualElement,无法自动继承父级的绑定上下文,绑定关系无法正确解析;- 即使绑定值发生变化,
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
相关产品推荐
相关产品推荐

