Bindable Property not working with Croppign Rectangle

Dear Community!
I have a very strange issue. In the following code at 261 in the TouchActioNTypeCancelled i want to call a Method which should set the CroppingRect.Rect to my bindable Property testImage. The Strange thing: When testIamge is a normal Property, not bindable, everything works fine. I can move the croppign rectangle with all corners in the view. If i now change the Proeprty to a bindable property when i start movign the rectangle on one corner in the app i can move the rectangle only from the opposite corners again and if i press the opposite corner, the rectangle buggs back into its original position. I don't understand how this can happen.
public static readonly BindableProperty testImageProperty =
BindableProperty.Create(nameof(testImage), typeof(SKRect), typeof(SKRect));
public SKRect testImage
{
get
{
return (SKRect)GetValue(testImageProperty);
}
set
{
SetValue(testImageProperty, value);
}
}
//public SKRect testImage { get; set; }
void OnTouchEffectTouchAction(object sender, TouchActionEventArgs args)
{
int i = 0;
SKPoint pixelLocation = ConvertToPixel(args.Location);
SKPoint bitmapLocation = inverseBitmapMatrix.MapPoint(pixelLocation);
switch (args.Type)
{
case TouchActionType.Pressed:
// Convert radius to bitmap/cropping scale
float radius = inverseBitmapMatrix.ScaleX * RADIUS;
// Find corner that the finger is touching
int cornerIndex = croppingRect.HitTest(bitmapLocation, radius);
if (cornerIndex != -1 && !touchPoints.ContainsKey(args.Id))
{
TouchPoint touchPoint = new TouchPoint
{
CornerIndex = cornerIndex,
Offset = bitmapLocation - croppingRect.Corners[cornerIndex]
};
touchPoints.Add(args.Id, touchPoint);
}
break;
case TouchActionType.Moved:
if (touchPoints.ContainsKey(args.Id))
{
TouchPoint touchPoint = touchPoints[args.Id];
croppingRect.MoveCorner(touchPoint.CornerIndex,
bitmapLocation - touchPoint.Offset);
InvalidateSurface();
}
break;
case TouchActionType.Released:
case TouchActionType.Cancelled:
if (touchPoints.ContainsKey(args.Id))
{
SetMap();
touchPoints.Remove(args.Id);
}
break;
}
}
private void SetMap()
{
testImage = croppingRect.Rect;
Console.WriteLine("test");
}
Thw complete view:
public class PhotoCropperCanvasView : SKCanvasView, INotifyPropertyChanged
{
const int CORNER = 50; // pixel length of cropper corner
const int RADIUS = 100; // pixel radius of touch hit-test
SKMatrix inverseBitmapMatrix;
public SKBitmap testmap;
public SKBitmap testImage { get; set; }
public CroppingRectangle croppingRect { get; set; }
//public SKMatrix inverseBitmapMatrix { get; set; }
public static readonly BindableProperty bitmapProperty =
BindableProperty.Create(nameof(bitmap), typeof(SKBitmap), typeof(Image),null,propertyChanged: OnbitmapChanged);
static void OnbitmapChanged(BindableObject bindable, object oldValue, object newValue)
{
Console.WriteLine("test");
}
//public static readonly BindableProperty testImageProperty =
// BindableProperty.Create(nameof(testImage), typeof(SKBitmap), typeof(Element),null);
//
//public SKBitmap testImage
//{
// get
// {
// return (SKBitmap)GetValue(testImageProperty);
// }
// set
// {
// SetValue(testImageProperty, value);
// }
public SKBitmap bitmap
{
get
{
return (SKBitmap)GetValue(bitmapProperty);
}
set
{
SetValue(bitmapProperty, value);
}
}
public SKBitmap CroppedBitmap
{
get
{
SKRect cropRect = croppingRect.Rect;
SKBitmap croppedBitmap = new SKBitmap((int)cropRect.Width,
(int)cropRect.Height);
SKRect dest = new SKRect(0, 0, cropRect.Width, cropRect.Height);
SKRect source = new SKRect(cropRect.Left, cropRect.Top,
cropRect.Right, cropRect.Bottom);
using (SKCanvas canvas = new SKCanvas(croppedBitmap))
{
canvas.DrawBitmap(bitmap, source, dest);
}
return croppedBitmap;
}
}
// Touch tracking
TouchEffect touchEffect = new TouchEffect();
struct TouchPoint
{
public int CornerIndex { set; get; }
public SKPoint Offset { set; get; }
}
Dictionary<long, TouchPoint> touchPoints = new Dictionary<long, TouchPoint>();
// Drawing objects
SKPaint cornerStroke = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.White,
StrokeWidth = 10
};
SKPaint edgeStroke = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.White,
StrokeWidth = 2
};
// this constructor for profile image
public PhotoCropperCanvasView(SKBitmap bitmap, float? aspectRatio = null)
{
this.bitmap = bitmap;
SKRect bitmapRect = new SKRect(0, 0, bitmap.Width, bitmap.Height);
croppingRect = new CroppingRectangle(bitmapRect, aspectRatio);
touchEffect.TouchAction += OnTouchEffectTouchAction;
}
// this constructor for post images
public PhotoCropperCanvasView()
{
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = nameof(bitmap))
{
base.OnPropertyChanged(propertyName);
if (bitmap != null)
{
SKRect bitmapRect = new SKRect(0, 0, bitmap.Width, bitmap.Width);
croppingRect = new CroppingRectangle(bitmapRect, 1);
touchEffect.TouchAction += OnTouchEffectTouchAction;
}
}
protected override void OnParentSet()
{
base.OnParentSet();
// Attach TouchEffect to parent view
Parent.Effects.Add(touchEffect);
}
protected override void OnPaintSurface(SKPaintSurfaceEventArgs args)
{
base.OnPaintSurface(args);
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear(SKColors.Gray);
// Calculate rectangle for displaying bitmap
float scale = Math.Min((float)info.Width / bitmap.Width, (float)info.Height / bitmap.Height);
float x = (info.Width - scale * bitmap.Width) / 2;
float y = (info.Height - scale * bitmap.Height) / 2;
SKRect bitmapRect = new SKRect(x, y, x + scale * bitmap.Width, y + scale * bitmap.Height);
canvas.DrawBitmap(bitmap, bitmapRect);
// Calculate a matrix transform for displaying the cropping rectangle
SKMatrix bitmapScaleMatrix = SKMatrix.MakeIdentity();
bitmapScaleMatrix.SetScaleTranslate(scale, scale, x, y);
// Display rectangle
SKRect scaledCropRect = bitmapScaleMatrix.MapRect(croppingRect.Rect);
canvas.DrawRect(scaledCropRect, edgeStroke);
// Display heavier corners
using (SKPath path = new SKPath())
{
path.MoveTo(scaledCropRect.Left, scaledCropRect.Top + CORNER);
path.LineTo(scaledCropRect.Left, scaledCropRect.Top);
path.LineTo(scaledCropRect.Left + CORNER, scaledCropRect.Top);
path.MoveTo(scaledCropRect.Right - CORNER, scaledCropRect.Top);
path.LineTo(scaledCropRect.Right, scaledCropRect.Top);
path.LineTo(scaledCropRect.Right, scaledCropRect.Top + CORNER);
path.MoveTo(scaledCropRect.Right, scaledCropRect.Bottom - CORNER);
path.LineTo(scaledCropRect.Right, scaledCropRect.Bottom);
path.LineTo(scaledCropRect.Right - CORNER, scaledCropRect.Bottom);
path.MoveTo(scaledCropRect.Left + CORNER, scaledCropRect.Bottom);
path.LineTo(scaledCropRect.Left, scaledCropRect.Bottom);
path.LineTo(scaledCropRect.Left, scaledCropRect.Bottom - CORNER);
canvas.DrawPath(path, cornerStroke);
}
// Invert the transform for touch tracking
bitmapScaleMatrix.TryInvert(out inverseBitmapMatrix);
}
void OnTouchEffectTouchAction(object sender, TouchActionEventArgs args)
{
int i = 0;
SKPoint pixelLocation = ConvertToPixel(args.Location);
SKPoint bitmapLocation = inverseBitmapMatrix.MapPoint(pixelLocation);
switch (args.Type)
{
case TouchActionType.Pressed:
// Convert radius to bitmap/cropping scale
float radius = inverseBitmapMatrix.ScaleX * RADIUS;
// Find corner that the finger is touching
int cornerIndex = croppingRect.HitTest(bitmapLocation, radius);
if (cornerIndex != -1 && !touchPoints.ContainsKey(args.Id))
{
TouchPoint touchPoint = new TouchPoint
{
CornerIndex = cornerIndex,
Offset = bitmapLocation - croppingRect.Corners[cornerIndex]
};
touchPoints.Add(args.Id, touchPoint);
}
break;
case TouchActionType.Moved:
if (touchPoints.ContainsKey(args.Id))
{
TouchPoint touchPoint = touchPoints[args.Id];
croppingRect.MoveCorner(touchPoint.CornerIndex,
bitmapLocation - touchPoint.Offset);
InvalidateSurface();
}
break;
case TouchActionType.Released:
case TouchActionType.Cancelled:
if (touchPoints.ContainsKey(args.Id))
{
SetMap();
touchPoints.Remove(args.Id);
}
break;
}
}
private void SetMap()
{
testImage = CroppedBitmap.Copy();
Console.WriteLine("test");
}
SKPoint ConvertToPixel(Xamarin.Forms.Point pt)
{
return new SKPoint((float)(CanvasSize.Width * pt.X / Width),
(float)(CanvasSize.Height * pt.Y / Height));
}
}
Bindingcontext is set like this:
public partial class CutImagesPage : ContentPage
{
private readonly CutImagesViewModel viewModel;
public CutImagesPage(Collection<SKBitmapImageSource> images)
{
InitializeComponent();
BindingContext = viewModel = new CutImagesViewModel(images);
}
This is the way i want it to work. In Fact itworks like that when i use a normal property in the custom view class:
This, however, is the way it works when i change the normal property to the Binding property which is commentet out in the View code example asbove:
I am very sorry. I added the code examples and to gifs showing what my problem is.
I tried to add the
CutImagesPage
andPhotoCropperCanvasView
to my sample, but it's confused that how you display the view and how you set thetestImageProperty
inCutImagesPage
. You create a newPhotoCropperCanvasView
then add it like the official sample, right? And how you set thetestImageProperty
inCutImagesViewModel
?