Reputation: 109
I am porting an application from Xamarin to MAUI. I am trying to create a SkiaSharp SKCanvasView that is square. It is inside a Grid as I am oerlaying 2 SVG images and a Label. This is how I did it in Xamarin:
<Grid x:Name="compassGrid" Margin="50,10">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<skia:SKCanvasView x:Name="compassCanvasView" PaintSurface="compassCanvasView_PaintSurface"
HeightRequest="{Binding Width, Source={x:Reference compassGrid}}"
MinimumHeightRequest="{Binding Height, Source={x:Reference compassGrid}}"
WidthRequest="{Binding Height, Source={x:Reference compassGrid}}"
MinimumWidthRequest="{Binding Width, Source={x:Reference compassGrid}}"/>
<skia:SKCanvasView x:Name="arrowCanvasView" PaintSurface="arrowCanvasView_PaintSurface" />
<Label Text="{Binding CompassReading}"
TextColor="{StaticResource Gray-300}"
FontSize="40"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"/>
</Grid>
And this worked fine, Resulting in this:
Now, with MAUI, the same does not work and results in this:
The Image is basically oval instead of round. Then when I rotate the compass, obviously the longer sides goes outside the canvas with is rectangular instead of square.
How can I get it to look like before?
Edit:
As per request from @FreakyAli here are sample apps for both Xamarin and MAUI (https://github.com/NemesisXB/CompassXamMaui). The maui version initially shows nothing until you rotate the screen once, then it exhibits the same problem.
Upvotes: 2
Views: 2158
Reputation: 6472
You can drop those tons of bindings and just claim your own size within available constraints. Subclass your SKCanvasView
then:
protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
{
if (check you need your own size)
{
// return your own size within constrains
// like take var side = Math.Min(widthConstraint, heightConstraint)
// as square rect side and return Size(side,side).
// use this to adapt maui view size to your needs
return AdaptSizeToContentIfNeeded(widthConstraint, heightConstraint, NeedMeasure);
}
//this will be called several times, so make sure that
// (check you need your own size) returns FALSE if new
// constrains are finally ok to you (square) so you'll fall here:
Update(); // invalidate canvas (on main thread)
return base.MeasureOverride(widthConstraint, heightConstraint);
}
protected override Size ArrangeOverride(Rect bounds)
{
if (we have asked for our own size)
{
// you return nothing just consume
// internally what they finally offer
ReactToOfferedBounds(bounds.Width, bounds.Height);
}
return base.ArrangeOverride(bounds);
}
MAUI provides some methods to have your own view size to create custom controls that adapt to internally needed size. Like you have a maui view with no height and width requests but you want to load an image inside with dynamic size that is unknown yet. when the image is loaded you'll issue a hacky invalidation using :
//will trigger parent calling our MeasureOverride
//this can be called from main thread only !!!
MainThread.BeginInvokeOnMainThread(() =>
{
InvalidateMeasureNonVirtual(InvalidationTrigger.HorizontalOptionsChanged);
});
Upvotes: 1
Reputation: 8883
Not sure how or why that worked with Xamarin.Forms before, because you're effectively binding to the size of the Grid while you're also telling the Grid at the same time to size itself based on the available width and auto-size the height of the row based on the content. In any case, your approach most likely results in excessive layout cycles.
To resolve this, you could either give the SKCanvasView
elements of the Grid a fixed width and height or you need to actually set the HeightRequest
based on the Width
property of the Grid:
XAML
<Grid x:Name="compassGrid"
Margin="50,10"
HorizontalOptions="Fill"
HeightRequest="{Binding Width, Source={RelativeSource Self}}">
<skia:SKCanvasView x:Name="compassCanvasView" PaintSurface="compassCanvasView_PaintSurface"
HeightRequest="{Binding Height, Source={x:Reference compassGrid}}"
WidthRequest="{Binding Width, Source={x:Reference compassGrid}}" />
<skia:SKCanvasView x:Name="arrowCanvasView" PaintSurface="arrowCanvasView_PaintSurface" />
<Label Text="{Binding CompassReading}"
TextColor="{StaticResource Gray-300}"
FontSize="40"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"/>
</Grid>
Note that this will only look good in Portrait mode. If you need to support Landscape mode as well, you'll need to either set the WidthRequest
and HeightRequest
from the code-behind or you can use OrientationStateTriggers to react to orientation changes.
Upvotes: 2