Bindable Property not working with Croppign Rectangle

Oliver 21 Reputation points
2022-09-07T16:36:24.51+00:00

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:
239904-correct.gif

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:
239864-incorrect.gif

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,326 questions
{count} votes