I have a custom slider and have tried to put it inside a listview with grids so the listview displays the following: Top left: items from a list as strings. Bottom left: (under each item) the custom slider. (To the right/the second column: an int/number value for each item).
The custom slider is made with the NuGet package SkiaSharp and is called "x:Name="balloon_slider"". My problem: When I put the custom slider inside my listview in xaml - it can no longer be refered to in the C# behind (does not exist in the current context). (It works outside the listview.) Here is the xaml code for the listview:
<ListView x:Name="displaylist100" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!--Items from list as strings-->
<Grid Grid.Column="0">
<Label x:Name="listlabel"
Text="{Binding Text}"
HorizontalTextAlignment="Center"
FontSize="Medium"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand"
Padding="10, 10"
Margin="0,5,5,0"/>
<!--SkiaSharp Balloon_slider-->
<Grid>
<skia:SKCanvasView x:Name="balloon_slider"
PaintSurface="Handle_Slider_PaintSurface"/>
<local:BalloonView x:Name="balloonSvg"/>
<Grid.Effects>
<local:TouchEffect
TouchAction="Handle_TouchAction"/>
</Grid.Effects>
</Grid>
<!--Value displayed to the right-->
<Label Text="{Binding DisplayValue, Mode=TwoWay}"
FontSize="Medium"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Padding="10, 10"
Grid.Column="1"/>
</Grid>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The slider that I'm using is a customized version of this: "https://github.com/memsranga/Balloony".
I have tried to use this: "var balloon_slider = (SKCanvasView)listView.FindByName("balloon_slider");" in the "InitializeComponent ();" section - but it doesn't seem to make it connect the two. I have also tried to have a standard slider in the listview - which works. So my question is: how can I refer to the slider from my C# behind when it is in the listview?
Update: Here is the code behind from the balloon slider project - I'm a bit confused about what I need to change in order to do the binding. Currently it is each "balloon_slider" and "balloonSvg" which has the error that it can't be found:
using System;
using System.Diagnostics;
using System.Xml.Linq;
using SkiaSharp;
using SkiaSharp.Views.Forms;
using Xamarin.Forms;
using static Balloony.TouchEffect;
namespace Balloony
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
SliderSelectedPaint = new SKPaint
{
Color = Color.FromHex("#eb0000").ToSKColor(),
IsAntialias = true,
Style = SKPaintStyle.Fill,
StrokeWidth = SliderHeight
};
SliderUnSelectedPaint = new SKPaint
{
Color = Color.FromHex("#f8f8f8").ToSKColor(),
IsAntialias = true,
Style = SKPaintStyle.Stroke,
StrokeWidth = SliderHeight
};
ThumbPaint = new SKPaint
{
Color = Color.FromHex("#eb0000").ToSKColor(),
IsAntialias = true,
Style = SKPaintStyle.Fill
};
ThumbSelectedPaint = new SKPaint
{
Color = Color.FromHex("#eb0000").ToSKColor(),
IsAntialias = true,
Style = SKPaintStyle.Stroke,
StrokeWidth = SelectedThumbThickness
};
ThumbSelectedSubtractPaint = new SKPaint
{
Color = Color.Transparent.ToSKColor(),
IsAntialias = true,
Style = SKPaintStyle.Fill,
StrokeWidth = 0,
BlendMode = SKBlendMode.Src
};
ThumbSubtractPaint = new SKPaint
{
Color = Color.Transparent.ToSKColor(),
IsAntialias = true,
Style = SKPaintStyle.Fill,
StrokeWidth = 0,
BlendMode = SKBlendMode.Src
};
Percent = 50;
}
float SliderHeight = 5;
float SelectedThumbThickness = 10;
float ThumbSize = 50;
SKPaint SliderSelectedPaint { get; set; }
SKPaint SliderUnSelectedPaint { get; set; }
SKPaint ThumbPaint { get; set; }
SKPaint ThumbSubtractPaint { get; set; }
SKPaint ThumbSelectedPaint { get; set; }
SKPaint ThumbSelectedSubtractPaint { get; set; }
private TouchActionType _touchType;
private double _width;
private float _percent;
public float Percent
{
get => _percent;
private set
{
_percent = value;
balloon_slider.InvalidateSurface();
TranslateBalloon(Percent);
}
}
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
// only calling when orientation changes
if (Math.Abs(width - _width) > 0.01)
{
_width = width;
balloonSvg.AnchorY = 1;
balloonSvg.TranslationX = (balloon_slider.Width * Percent / 100) - balloonSvg.Width / 2;
balloonSvg.Scale = 0;
balloonSvg.TranslationY = balloon_slider.Height;
}
}
void Handle_Slider_PaintSurface(object sender, SkiaSharp.Views.Forms.SKPaintSurfaceEventArgs e)
{
var info = e.Info;
var canvas = e.Surface.Canvas;
canvas.Clear();
DrawSlider(canvas, info, Percent);
DrawThumb(canvas, info, Percent, _touchType);
}
private void TranslateBalloon(float percent)
{
if (this.AnimationIsRunning("TranslationAnimation"))
{
return;
}
var oldX = balloonSvg.TranslationX;
var newX = balloon_slider.Width * percent / 100 - balloonSvg.Width / 2;
balloonSvg.Text = Math.Floor(Percent).ToString();
var translation = new Animation();
translation.Add(0, 1, new Animation((s) =>
{
if (oldX > newX)
{
var delta = oldX - s * Math.Abs(oldX - newX);
balloonSvg.TranslationX = delta;
}
else
{
var delta = oldX + s * Math.Abs(oldX - newX);
balloonSvg.TranslationX = delta;
}
}, 0, 1));
translation.Add(0, 1, new Animation(s =>
{
if (oldX > newX)
{
var delta = oldX - s * Math.Abs(oldX - newX);
var angle = Math.Abs(oldX - newX) > 0.001 ? Math.Tanh((oldX - newX) / balloon_slider.Width) : 0;
balloonSvg.Rotation = angle * 180;
}
else
{
var delta = oldX + s * Math.Abs(oldX - newX);
var angle = Math.Abs(oldX - newX) > 0.001 ? Math.Tanh((oldX - newX) / balloon_slider.Width) : 0;
balloonSvg.Rotation = angle * 180;
}
}, 0, 1, finished: () =>
{
balloonSvg.RelRotateTo(-balloonSvg.Rotation, 500);
}));
translation.Commit(balloonSvg, "TranslationAnimation", length: 100);
}
private void DrawThumb(SKCanvas canvas, SKImageInfo info, float percent, TouchActionType touchActionType)
{
var y = info.Height - ThumbSize - SelectedThumbThickness;
var center = info.Width * percent / 100;
if (touchActionType == TouchActionType.Pressed || touchActionType == TouchActionType.Moved)
{
// selected thumb
var radius = ThumbSize * 0.5f; // 50% of size
canvas.DrawCircle(center, y, radius, ThumbSelectedPaint);
canvas.DrawCircle(center, y, radius, ThumbSelectedSubtractPaint);
return;
}
//default thumb
var startX = center - ThumbSize / 2;
var startY = y - ThumbSize / 2;
var cornerRadius = ThumbSize * 0.4f; // 40% of size
var innerRadius = ThumbSize / 2 * .5f; // 50 % of side
canvas.DrawRoundRect(startX, startY, ThumbSize, ThumbSize, cornerRadius, cornerRadius, ThumbPaint);
canvas.DrawCircle(center, y, innerRadius, ThumbSubtractPaint);
}
private void Handle_TouchAction(object sender, Balloony.TouchEffect.TouchActionEventArgs args)
{
_touchType = args.Type;
if (this.AnimationIsRunning("FloatAnimation") || this.AnimationIsRunning("DropAnimation"))
{
return;
}
if (_touchType == TouchActionType.Pressed || _touchType == TouchActionType.Entered)
{
Debug.WriteLine("entered");
var floatAnimation = new Animation();
floatAnimation.Add(0, 1, new Animation((s) =>
{
balloonSvg.Scale = s;
balloonSvg.TranslationY = (balloon_slider.Height - balloonSvg.Height - 88) - s * -45;
}, 0, 1));
floatAnimation.Commit(balloonSvg, "FloatAnimation");
}
else if (_touchType == TouchActionType.Released || _touchType == TouchActionType.Exited)
{
var dropAnimation = new Animation();
dropAnimation.Add(0, 1, new Animation((s) =>
{
balloonSvg.Scale = s;
balloonSvg.TranslationY = (balloon_slider.Height - balloonSvg.Height - 88) - s * -45;
}, 1, 0));
dropAnimation.Commit(balloonSvg, "DropAnimation");
}
Percent = (float)((args.Location.X / balloon_slider.Width) * 100 - 15);
if (Percent > 100)
{
Percent = 100;
}
if (Percent < 0)
{
Percent = 0;
}
if (Percent < 20)
{
}
}
private void DrawSlider(SKCanvas canvas, SKImageInfo info, float percent)
{
var y = info.Height - ThumbSize - SelectedThumbThickness; // minus the thumb radius, minus thumb thickness
percent = Math.Min(percent, 100);
var selectX = info.Width * percent / 100;
canvas.DrawLine(0, y, selectX, y, SliderSelectedPaint);
canvas.DrawLine(selectX, y, info.Width, y, SliderUnSelectedPaint);
}
}
}
Just as Jason said, Elements inside templated controls cannot be referenced by Name.
If you add event to the
SKCanvasViewinside of the ListView and implement this function inYourPage.xaml.cs, you can get the View's instance by the paramtersender.Please refer to the following code:
Update:
In general, we recommend using
MVVManddata bindingto achieve this. For comparison, I added two buttons, one that responds to the click event using theCommandmethod, and one that adds theClickedevent.You can refer to the following code:
MyViewModel.cs
Item.cs
MainPage.xaml
MainPage.xaml.cs