I have the following collectionView in ProceduresPage.xaml (my questions is related to the view:ScoreGaugeView part and how to pass the score to my IDrawable in order to draw a needle gauge).
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MediSkillApp.View.ProceduresPage"
Title="Alle mine indgreb"
xmlns:viewmodel="clr-namespace:MediSkillApp.ViewModel"
x:DataType="viewmodel:ProceduresViewModel"
xmlns:model="clr-namespace:MediSkillApp.Model"
xmlns:view="clr-namespace:MediSkillApp.View">
<Grid ColumnDefinitions="*"
ColumnSpacing="5"
RowSpacing="0">
<CollectionView
BackgroundColor="Transparent"
ItemsSource="{Binding Procedures}"
RemainingItemsThresholdReachedCommand="{Binding GetProceduresCommand}"
RemainingItemsThreshold="5">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Procedure">
<VerticalStackLayout>
<Frame Margin="5">
<Grid ColumnDefinitions="64,*, 64">
<Image Source="{Binding Icon}"
HeightRequest="48"
WidthRequest="48"
HorizontalOptions="Start"/>
<VerticalStackLayout
Grid.Column="1">
<Label Text="{Binding ProcedureTypeString}"
Style="{StaticResource Heading}"/>
<Label Text="{Binding OpRoleString}"
Style="{StaticResource NormalLabel}"/>
<Label Text="{Binding Date, StringFormat='{0:dd/MM-yyyy}'}"
Style="{StaticResource NormalLabel}"/>
</VerticalStackLayout>
<view:ScoreGaugeView
Grid.Column="2"
ScoreValue="{Binding GetAvgScore}"
IsVisible="{Binding IsScored}"/>
</Grid>
</Frame>
</VerticalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<ActivityIndicator IsVisible="{Binding IsBusy}"
IsRunning="{Binding IsBusy}"
HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand"/>
</Grid>
</ContentPage>
It produces frames like these (an icon, some text, and a score if it has one):
Now, I want to add a scoregauge needle to show the score.
I also have the ScoreGaugeView (ScoreGaugeView.xaml)
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:drawables="clr-namespace:MediSkillApp.Drawables"
x:Class="MediSkillApp.View.ScoreGaugeView">
<ContentView.Resources>
<drawables:ScoreGaugeDrawable x:Key="scoreGaugeDrawable"/>
</ContentView.Resources>
<ContentView.ControlTemplate>
<ControlTemplate>
<VerticalStackLayout>
<Label Text="{TemplateBinding ScoreValue, StringFormat='{0:F2}'}"
HorizontalOptions="Center"/>
<Image Source="scoremeter.png"/>
<GraphicsView>
<GraphicsView.Drawable>
<drawables:ScoreGaugeDrawable/>
</GraphicsView.Drawable>
</GraphicsView>
</VerticalStackLayout>
</ControlTemplate>
</ContentView.ControlTemplate>
</ContentView>
And my ScoreGaugeDrawable (ScoreGaugeDrawable.cs)
namespace MediSkillApp.Drawables;
public class ScoreGaugeDrawable : IDrawable
{ public void Draw(ICanvas canvas, RectF dirtyRect) {
var centerPoint = new PointF(32, 0);
var circleRadius = 5;
canvas.FillColor = Colors.Black;
canvas.FillCircle(centerPoint, circleRadius);
}
}
So my question is how to pass the score to my ScoreGaugeDrawable
, so I know where to point the needle?
EDIT: I updated my ScoreGaugeView.xaml to the following
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:drawables="clr-namespace:MediSkillApp.Drawables"
x:Class="MediSkillApp.View.ScoreGaugeView">
<VerticalStackLayout>
<Label Text="{Binding GetAvgScore, StringFormat='{0:F2}'}"
HorizontalOptions="Center"/>
<Image Source="scoremeter.png"/>
<GraphicsView>
<GraphicsView.Drawable>
<drawables:ScoreGaugeDrawable
Score="{Binding AvgScore}"/>
</GraphicsView.Drawable>
</GraphicsView>
</VerticalStackLayout>
</ContentView>
And the ScoreGaugeDrawable.cs to:
namespace MediSkillApp.Drawables;
public class ScoreGaugeDrawable : BindableObject, IDrawable
{
private string score = "";
public static readonly BindableProperty ScoreProperty = BindableProperty.Create(nameof(Score),
typeof(double),
typeof(ScoreGaugeDrawable));
public double Score
{
get => (double)GetValue(ScoreProperty);
set => SetValue(ScoreProperty, value);
}
public void Draw(ICanvas canvas, RectF dirtyRect)
{
var centerPoint = new PointF(32, 0);
var circleRadius = 5;
canvas.FillColor = Colors.Black;
canvas.FillCircle(centerPoint, circleRadius);
canvas.StrokeColor = Colors.Black;
canvas.DrawLine(centerPoint, new Point(0, Score));
}
}
This will print the number in the label but not pass the value to the drawable. In my Procedure class I have:
public double AvgScore { get; set; }
public string GetAvgScore {
get {
if (AvgScore == 0) return "";
return AvgScore.ToString();
}
}
You should be able to do this with a BindableProperty. You can add that to your ScoreGaugeDrawable
class and then pass the value to it from the XAML, e.g. using a Binding expression.
Update your ScoreGaugeDrawable
like this:
using Microsoft.Maui.Controls;
public class ScoreGaugeDrawable : BindableObject, IDrawable
{
public static readonly BindableProperty ScoreProperty = BindableProperty.Create(nameof(Score), typeof(double), typeof(ScoreGaugeDrawable));
public double Score
{
get => (double)GetValue(ScoreProperty);
set => SetValue(ScoreProperty, value);
}
public void Draw(ICanvas canvas, RectF dirtyRect)
{
var centerPoint = new PointF(32, 0);
var circleRadius = 5;
canvas.FillColor = Colors.Black;
canvas.FillCircle(centerPoint, circleRadius);
//Add your drawing logic that uses the Score property
//...
}
}
Then you should be able to pass a value to the ScoreGaugeDrawable
like this, e.g. by Binding to another property that holds the score value:
<GraphicsView>
<GraphicsView.Drawable>
<drawables:ScoreGaugeDrawable Score="{Binding SomeScore}"/>
</GraphicsView.Drawable>
</GraphicsView>
Let me know if this works, because I've seen issues with Bindings and Drawables before.
By the way, you can safely remove this from your XAML, since that's not used anywhere:
<ContentView.Resources>
<drawables:ScoreGaugeDrawable x:Key="scoreGaugeDrawable"/>
</ContentView.Resources>
Something that I also noticed is that you're using a ControlTemplate
, but for no apparent reason. It should be possible to remove that and place the VerticalStackLayout
directly into the ContentView
.