xamarin / SignaturePad

MIT License
245 stars 150 forks source link

Signature Pad Crash: Cannot access a disposed object. Object name: 'Xamarin.Controls.InkPresenter'. #178

Open jthun opened 4 years ago

jthun commented 4 years ago

Description

A Xamarin Forms app on Android sometimes (not frequently) crashes with System.ObjectDisposedException: Cannot access a disposed object. Object name: 'Xamarin.Controls.InkPresenter'. There are two Signature Pads on the same XAML page.

Full Stack Trace

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
  at System.Reflection.RuntimeMethodInfo.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00086] in <725bd52bc9f5458daebe089c6739adae>:0
  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <725bd52bc9f5458daebe089c6739adae>:0
  at Xamarin.Forms.BindingExpression.ApplyCore (System.Object sourceObject, Xamarin.Forms.BindableObject target, Xamarin.Forms.BindableProperty property, System.Boolean fromTarget) [0x0030a] in <ac5841ee7b264022a4b31735a5b36ce1>:0
  at Xamarin.Forms.BindingExpression.Apply (System.Boolean fromTarget) [0x0003e] in <ac5841ee7b264022a4b31735a5b36ce1>:0
  at Xamarin.Forms.Binding.Apply (System.Boolean fromTarget) [0x00020] in <ac5841ee7b264022a4b31735a5b36ce1>:0
  at Xamarin.Forms.BindableObject.SetValueActual (Xamarin.Forms.BindableProperty property, Xamarin.Forms.BindableObject+BindablePropertyContext context, System.Object value, System.Boolean currentlyApplying, Xamarin.Forms.Internals.SetValueFlags attributes, System.Boolean silent) [0x00105] in <ac5841ee7b264022a4b31735a5b36ce1>:0
  at Xamarin.Forms.BindableObject.SetValueCore (Xamarin.Forms.BindableProperty property, System.Object value, Xamarin.Forms.Internals.SetValueFlags attributes, Xamarin.Forms.BindableObject+SetValuePrivateFlags privateAttributes) [0x00173] in <ac5841ee7b264022a4b31735a5b36ce1>:0
  at Xamarin.Forms.BindableObject.SetValue (Xamarin.Forms.BindableProperty property, System.Object value, System.Boolean fromStyle, System.Boolean checkAccess) [0x00042] in <ac5841ee7b264022a4b31735a5b36ce1>:0
  at Xamarin.Forms.BindableObject.SetValue (Xamarin.Forms.BindableProperty property, System.Object value) [0x00000] in <ac5841ee7b264022a4b31735a5b36ce1>:0
  at ECM.Behaviors.CaptureSignaturePointsBehavior.set_Points (System.Collections.Generic.IEnumerable`1[T] value) [0x00000] in <33345a6ea9824868b32a2796cfe89dc6>:0
  at ECM.Behaviors.CaptureSignaturePointsBehavior.UpdateBehavior (SignaturePad.Forms.SignaturePadView signaturePad) [0x00008] in <33345a6ea9824868b32a2796cfe89dc6>:0
  at ECM.Behaviors.CaptureSignatureBehaviorBase.OnSignatureChanged (System.Object sender, System.EventArgs e) [0x0001d] in <33345a6ea9824868b32a2796cfe89dc6>:0
  at SignaturePad.Forms.SignaturePadView.OnSignatureStrokeCompleted () [0x00016] in <a04d8d0ed5714418a40610b67138958a>:0
  at SignaturePad.Forms.SignaturePadView.<.ctor>b__32_0 (System.Object <p0>, System.EventArgs <p1>) [0x00000] in <a04d8d0ed5714418a40610b67138958a>:0
  at SignaturePad.Forms.SignaturePadCanvasView.OnStrokeCompleted () [0x00010] in <a04d8d0ed5714418a40610b67138958a>:0
  at SignaturePad.Forms.SignaturePadCanvasRenderer.OnStrokeCompleted (System.Object sender, System.EventArgs e) [0x0000a] in <a04d8d0ed5714418a40610b67138958a>:0
  at Xamarin.Controls.SignaturePadCanvasView.OnStrokeCompleted (System.Object sender, System.EventArgs e) [0x0000a] in <0c0873655a504a1796935fa2cfa62e06>:0
  at Xamarin.Controls.InkPresenter.OnStrokeCompleted () [0x0000a] in <0c0873655a504a1796935fa2cfa62e06>:0
  at Xamarin.Controls.InkPresenter.TouchesEnded (Android.Views.MotionEvent e) [0x0003c] in <0c0873655a504a1796935fa2cfa62e06>:0
  at Xamarin.Controls.InkPresenter.OnTouchEvent (Android.Views.MotionEvent e) [0x0002e] in <0c0873655a504a1796935fa2cfa62e06>:0
  at Android.Views.View.n_OnTouchEvent_Landroid_view_MotionEvent_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_e) [0x00011] in <f782784eea9c43caa2d3105424824e07>:0
  at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.35(intptr,intptr,intptr)
--- End of inner exception stack trace ---
  System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Xamarin.Controls.InkPresenter'.
    at Java.Interop.JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self) [0x00029] in <344d0ba1c0a44407a1566fada0513d51>:0
    at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeVirtualVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x00000] in <344d0ba1c0a44407a1566fada0513d51>:0
    at Android.Views.View.Invalidate () [0x0000a] in <f782784eea9c43caa2d3105424824e07>:0
    at Xamarin.Controls.InkPresenter.Clear () [0x00012] in <0c0873655a504a1796935fa2cfa62e06>:0
    at Xamarin.Controls.SignaturePadCanvasView.Clear () [0x00000] in <0c0873655a504a1796935fa2cfa62e06>:0
    at Xamarin.Controls.SignaturePadCanvasView.LoadPoints (System.Drawing.PointF[] loadedPoints) [0x00000] in <0c0873655a504a1796935fa2cfa62e06>:0
    at SignaturePad.Forms.SignaturePadCanvasRenderer.OnPointsSpecified (System.Object sender, SignaturePad.Forms.SignaturePadCanvasView+PointsEventArgs e) [0x0003a] in <a04d8d0ed5714418a40610b67138958a>:0
    at SignaturePad.Forms.SignaturePadCanvasView.SetSignaturePoints (System.Collections.Generic.IEnumerable`1[T] points) [0x00018] in <a04d8d0ed5714418a40610b67138958a>:0
    at SignaturePad.Forms.SignaturePadCanvasView.set_Points (System.Collections.Generic.IEnumerable`1[T] value) [0x00000] in <a04d8d0ed5714418a40610b67138958a>:0
    at SignaturePad.Forms.SignaturePadView.set_Points (System.Collections.Generic.IEnumerable`1[T] value) [0x00006] in <a04d8d0ed5714418a40610b67138958a>:0
    at ECM.Behaviors.CaptureSignaturePointsBehavior.UpdateSignaturePad (SignaturePad.Forms.SignaturePadView bindable, Xamarin.Forms.BindableProperty property, System.Object oldValue, System.Object newValue) [0x00001] in <33345a6ea9824868b32a2796cfe89dc6>:0
    at ECM.Behaviors.CaptureSignatureBehaviorBase.OnPropertyChanged (Xamarin.Forms.BindableObject bindable, Xamarin.Forms.BindableProperty property, System.Object oldValue, System.Object newValue) [0x0001d] in <33345a6ea9824868b32a2796cfe89dc6>:0
    at ECM.Behaviors.CaptureSignatureBehaviorBase+<>c__DisplayClass2_0.<CreatePropertyChanged>g__OnPropertyChanged|0 (Xamarin.Forms.BindableObject bindable, System.Object oldValue, System.Object newValue) [0x00008] in <33345a6ea9824868b32a2796cfe89dc6>:0
    at Xamarin.Forms.BindableObject.SetValueActual (Xamarin.Forms.BindableProperty property, Xamarin.Forms.BindableObject+BindablePropertyContext context, System.Object value, System.Boolean currentlyApplying, Xamarin.Forms.Internals.SetValueFlags attributes, System.Boolean silent) [0x0012a] in <ac5841ee7b264022a4b31735a5b36ce1>:0
    at Xamarin.Forms.BindableObject.SetValueCore (Xamarin.Forms.BindableProperty property, System.Object value, Xamarin.Forms.Internals.SetValueFlags attributes, Xamarin.Forms.BindableObject+SetValuePrivateFlags privateAttributes) [0x00173] in <ac5841ee7b264022a4b31735a5b36ce1>:0
    at Xamarin.Forms.BindingExpression.ApplyCore (System.Object sourceObject, Xamarin.Forms.BindableObject target, Xamarin.Forms.BindableProperty property, System.Boolean fromTarget) [0x00220] in <ac5841ee7b264022a4b31735a5b36ce1>:0
    at Xamarin.Forms.BindingExpression.Apply (System.Boolean fromTarget) [0x0003e] in <ac5841ee7b264022a4b31735a5b36ce1>:0
    at Xamarin.Forms.BindingExpression+BindingExpressionPart.<PropertyChanged>b__49_0 () [0x00000] in <ac5841ee7b264022a4b31735a5b36ce1>:0
    at Xamarin.Forms.BindingExpression+BindingExpressionPart.PropertyChanged (System.Object sender, System.ComponentModel.PropertyChangedEventArgs args) [0x000be] in <ac5841ee7b264022a4b31735a5b36ce1>:0
    at Xamarin.Forms.BindingExpression+WeakPropertyChangedProxy.OnPropertyChanged (System.Object sender, System.ComponentModel.PropertyChangedEventArgs e) [0x00012] in <ac5841ee7b264022a4b31735a5b36ce1>:0
    at (wrapper delegate-invoke) <Module>.invoke_void_object_PropertyChangedEventArgs(object,System.ComponentModel.PropertyChangedEventArgs)
    at ECM.ViewModels.BaseViewModel.OnPropertyChanged (System.String propertyName) [0x0001b] in <33345a6ea9824868b32a2796cfe89dc6>:0
    at ECM.ViewModels.BaseViewModel.SetProperty[T] (T& backingStore, T value, System.String propertyName, System.Action onChanged) [0x00030] in <33345a6ea9824868b32a2796cfe89dc6>:0
    at ECM.ViewModels.UCRActivityViewModel.set_ReceivingSignature (System.Collections.Generic.IEnumerable`1[T] value) [0x00001] in <33345a6ea9824868b32a2796cfe89dc6>:0
    at (wrapper managed-to-native) System.Reflection.RuntimeMethodInfo.InternalInvoke(System.Reflection.RuntimeMethodInfo,object,object[],System.Exception&)
    at System.Reflection.RuntimeMethodInfo.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x0006a] in <725bd52bc9f5458daebe089c6739adae>:0

Code

CaptureSignaturePointsBehavior.cs

namespace ECM.Behaviors
{
    using System.Collections.Generic;
    using SignaturePad.Forms;
    using Xamarin.Forms;

    /// <summary>
    /// Defines the <see cref="CaptureSignaturePointsBehavior" />.
    /// </summary>
    public class CaptureSignaturePointsBehavior : CaptureSignatureBehaviorBase
    {
        /// <summary>
        /// Defines the PointsProperty.
        /// </summary>
        public static readonly BindableProperty PointsProperty = BindableProperty.Create(
            nameof(Points),
            typeof(IEnumerable<Point>),
            typeof(CaptureSignaturePointsBehavior),
            default(IEnumerable<Point>),
            BindingMode.TwoWay,
            propertyChanged: CreatePropertyChanged(PointsProperty));

        /// <summary>
        /// Gets or sets the Points.
        /// </summary>
        /// <value>
        /// The Points.
        /// </value>
        public IEnumerable<Point> Points { get => (IEnumerable<Point>)GetValue(PointsProperty); set => SetValue(PointsProperty, value); }

        /// <summary>
        /// Updates the SignaturePad.
        /// </summary>
        /// <param name="bindable">The bindable<see cref="SignaturePadView"/>.</param>
        /// <param name="property">The property<see cref="BindableProperty"/>.</param>
        /// <param name="oldValue">The oldValue<see cref="object"/>.</param>
        /// <param name="newValue">The newValue<see cref="object"/>.</param>
        protected override void UpdateSignaturePad(SignaturePadView bindable, BindableProperty property, object oldValue, object newValue)
        {
            bindable.Points = newValue as IEnumerable<Point>;
        }

        /// <summary>
        /// Update Behavior.
        /// </summary>
        /// <param name="signaturePad">The signaturePad<see cref="SignaturePadView"/>.</param>
        protected override void UpdateBehavior(SignaturePadView signaturePad)
        {
            Points = signaturePad.Points;
        }
    }
}

CaptureSignatureBehaviorBase.cs

namespace ECM.Behaviors
{
    using System;
    using SignaturePad.Forms;
    using Xamarin.Forms;

    /// <summary>
    /// Defines the <see cref="CaptureSignatureBehaviorBase" />.
    /// </summary>
    public class CaptureSignatureBehaviorBase : Behavior<SignaturePadView>
    {
        /// <summary>
        /// Defines if the signature pad is updating.
        /// </summary>
        private bool updating;

        /// <summary>
        /// Defines the associated signature pad view.
        /// </summary>
        private SignaturePadView associated;

        /// <summary>
        /// The CreatePropertyChanged.
        /// </summary>
        /// <param name="property">The property<see cref="BindableProperty"/>´.</param>
        /// <returns>The <see cref="BindableProperty.BindingPropertyChangedDelegate"/>.</returns>
        public static BindableProperty.BindingPropertyChangedDelegate CreatePropertyChanged(BindableProperty property)
        {
            return OnPropertyChanged;

            void OnPropertyChanged(BindableObject bindable, object oldValue, object newValue)
            {
                var behavior = bindable as CaptureSignatureBehaviorBase;
                behavior.OnPropertyChanged(bindable, property, oldValue, newValue);
            }
        }

        /// <summary>
        /// The OnAttachedTo event.
        /// </summary>
        /// <param name="bindable">The view<see cref="SignaturePadView"/>.</param>
        protected override void OnAttachedTo(SignaturePadView bindable)
        {
            base.OnAttachedTo(bindable);

            associated = bindable;
            UpdateBindingContext(bindable, EventArgs.Empty);

            bindable.Cleared += OnSignatureChanged;
            bindable.StrokeCompleted += OnSignatureChanged;
            bindable.BindingContextChanged += UpdateBindingContext;
        }

        /// <summary>
        /// The OnDetachingFrom event.
        /// </summary>
        /// <param name="bindable">The view<see cref="SignaturePadView"/>.</param>
        protected override void OnDetachingFrom(SignaturePadView bindable)
        {
            bindable.Cleared -= OnSignatureChanged;
            bindable.StrokeCompleted -= OnSignatureChanged;
            bindable.BindingContextChanged -= UpdateBindingContext;

            BindingContext = null;
            associated = null;

            base.OnDetachingFrom(bindable);
        }

        /// <summary>
        /// Updates the signature pad.
        /// </summary>
        /// <param name="bindable">The view<see cref="SignaturePadView"/>.</param>
        /// <param name="property">The property<see cref="BindableProperty"/>.</param>
        /// <param name="oldValue">The oldValue<see cref="object"/>.</param>
        /// <param name="newValue">The newValue<see cref="object"/>.</param>
        protected virtual void UpdateSignaturePad(SignaturePadView bindable, BindableProperty property, object oldValue, object newValue)
        {
        }

        /// <summary>
        /// Update behavior.
        /// </summary>
        /// <param name="signaturePad">The signature pad<see cref="SignaturePadView"/>.</param>
        protected virtual void UpdateBehavior(SignaturePadView signaturePad)
        {
        }

        /// <summary>
        /// The OnPropertyChanged.
        /// </summary>
        /// <param name="bindable">The object<see cref="BindableObject"/>.</param>
        /// <param name="property">The property<see cref="BindableProperty"/>.</param>
        /// <param name="oldValue">The oldValue<see cref="object"/>.</param>
        /// <param name="newValue">The newValue<see cref="object"/>.</param>
        protected void OnPropertyChanged(BindableObject bindable, BindableProperty property, object oldValue, object newValue)
        {
            var behavior = bindable as CaptureSignatureBehaviorBase;

            if (!behavior.updating)
            {
                behavior.updating = true;
                behavior.UpdateSignaturePad(behavior.associated, property, oldValue, newValue);
                behavior.updating = false;
            }
        }

        /// <summary>
        /// The UpdateBindingContext.
        /// </summary>
        /// <param name="sender">The sender<see cref="object"/>.</param>
        /// <param name="e">The e<see cref="EventArgs"/>.</param>
        private void UpdateBindingContext(object sender, EventArgs e)
        {
            var signaturePad = sender as SignaturePadView;

            BindingContext = signaturePad.BindingContext;
        }

        /// <summary>
        /// The OnSignatureChanged.
        /// </summary>
        /// <param name="sender">The sender<see cref="object"/>.</param>
        /// <param name="e">The e<see cref="EventArgs"/>.</param>
        private void OnSignatureChanged(object sender, EventArgs e)
        {
            var signaturePad = sender as SignaturePadView;

            if (!updating)
            {
                updating = true;
                UpdateBehavior(signaturePad);
                updating = false;
            }
        }
    }
}

XAML

<controls:SignaturePadView
    x:Name="receivingSignaturePadView"
    Margin="0"
    BackgroundColor="#e3e3e3"
    CaptionText="Receiving Party Signature"
    HeightRequest="150"
    StrokeColor="#000F55"
    StrokeWidth="3"
    WidthRequest="400">

    <controls:SignaturePadView.Behaviors>
        <behaviors:CaptureSignaturePointsBehavior Points="{Binding ReceivingSignature}" />
    </controls:SignaturePadView.Behaviors>

</controls:SignaturePadView>

In the ViewModel

public IEnumerable<Point> ReceivingSignature
{
    get { return receivingSignature; }
    set { SetProperty(ref receivingSignature, value); }
}

Basic Information