locked
Installed 5.9 (Build 431) -- "Event registration is overwriting existing delegate..." error RRS feed

  • Question

  • User363 posted

    Code is unchanged in weeks but is now failing with the following error:

    System.InvalidOperationException: Event registration is overwriting existing delegate. Either just use events or your own delegate.

    It's at a line of code where I'm defining the "Scrolled" delegate for my UITableView:

    MyTables[CurrentTableIndex].Scrolled += (object sender, EventArgs e) => { ... ... }

    I found a couple of posts about this error but assumed that the latest stable update fixed it. Any help appreciated...

    Thursday, April 30, 2015 1:00 AM

All replies

  • User181 posted

    You can blame me. I asked for that exception (though it may have already been planned).

    The good news is that this shows a likely bug in your code. I explain why in detail in this video. The Scrolled event is mutually exclusive with the Delegate property (and the Source property). If you are using Source now and haven't noticed anything not working then you may be able to just switch to using DataSource instead of Source (and inheriting from UITableViewDataSource instead of UITableViewSource). If that compiles then awesome. If you were using methods that aren't in UITableViewSource then congrats! You had a bug you didn't know about.

    Thursday, April 30, 2015 1:23 AM
  • User363 posted

    Well, if there is a bug, it hasn't reared it's ugly head in over a year of being in production. Approximately where in the video do you talk about this (the video is over an hour)? I'm in a crunch here because my users need a new build, and previously working code is no longer working. I am using Source, but I don't quite understand how Source and Scrolled can be conflicting with each other.

    MyTables[CurrentTableIndex].Source = new MyTable.TableSource(myList); MyTables[CurrentTableIndex].Scrolled += (object sender, EventArgs e) => { ... ... }

    Anyway, I tried your suggestion and changed to DataSource which seems to be working. However, the same error is popping up elsewhere now. I have a UITextField where I define the ShouldChangeCharacters event:

    amountField.ShouldChangeCharacters = (textField, range, replacementString) => { .... };

    At this point, I'm worried that these errors are all over the place. Do you recommend that I "downgrade" to a previous version of Xamarin?

    Thanks...

    Thursday, April 30, 2015 1:37 AM
  • User181 posted

    Approximately where in the video do you talk about this (the video is over an hour)?

    ~47 minutes, but the earlier parts make it clearer by showing you how it works in Objective-C. When you have time you should try to watch it from the beginning so it all makes sense.

    At this point, I'm worried that these errors are all over the place.

    Every place you hit this is a potential bug. You should fix them all.

    That said, if you're in a crunch you shouldn't be updating your toolchain. The quickest fix is to roll back until you have time to do it right.

    Thursday, April 30, 2015 1:52 AM
  • User124446 posted

    A quick downgrade to 8.9.1.3 solved the issue for me.

    Thursday, April 30, 2015 1:54 AM
  • User363 posted

    Adam -- I'd like to go ahead and try to fix the issues if possible, and then roll back as a last resort. So what do you recommend instead of using ShouldChangeCharacters?

    Thursday, April 30, 2015 2:00 AM
  • User181 posted

    It depends on what else you're doing with that text field. You're probably setting a Delegate, right? What does that do? Can you show more code around that?

    FWIW, by inspecting the code in the Assembly Browser I see that there is a field called CheckForEventAndDelegateMismatches in UIApplication that can disable this check. You could put this in your app delegate to go back to the old behavior:

    UIApplication.CheckForEventAndDelegateMismatches = false;
    

    But again this was added to detect potential bugs so that may just be masking issues.

    EDIT: The field is a static so you shouldn't use SharedApplication. I fixed the example.

    Thursday, April 30, 2015 3:08 AM
  • User93424 posted

    I had this exception using a custom map in iOs. I solved it by making my map delegate null before assigning it again:

    from this: mapView.Delegate = new MyMapDelegate (false, true, detailCommand); to this:

        mapView.Delegate = null;
        mapView.Delegate = new MyMapDelegate (false, true, detailCommand);
    
    Thursday, April 30, 2015 12:30 PM
  • User363 posted

    Adam -- first of all, your fix works. By setting CheckForEventAndDelegateMismatches to false, the error goes away. However, I'd like to understand the cause and fix my code. I'm not doing anything too fancy inside the delegate:

    amountField.ShouldChangeCharacters = (UITextField textField, NSRange range, string replacementString) =>
    {
            string entry = textField.Text + replacementString;
            if (entry.Contains("."))
            {
                string[] number = entry.Split('.');
                if (number[0].Length > 6) return false;
                if (number[1].Length > 2) return false;
            }
            else
            {
                return entry.Length <= 6;
            }
    
            return true;
    };
    

    I have 3-4 UITextFields on the view at the same time, and they all get initialized the same way. The error only occurs when the text field receives focus.

    Paolo -- thanks for the suggestion; unfortunately, setting the delegate to null first didn't work for me.

    Thursday, April 30, 2015 1:00 PM
  • User181 posted

    I think I'm missing some important code. What else do you do with amountField? It seems like something else somewhere is assigning something to either the Delegate or the WeakDelegate property of amountField. Can you find that code?

    If not then see if you can reduce this to a small solution that reproduces the exception and attach it as a .zip.

    Thursday, April 30, 2015 2:36 PM
  • User181 posted

    had this exception using a custom map in iOs. I solved it by making my map delegate null before assigning it again:

    That shouldn't be necessary if your code is correct. You may just be masking a bug in your code rather than fixing it. The MKMapView class also has both events/callback properties and a Delegate property, but you can't use both of those at the same time. If you are using any of the events in the map view or assigning a callback method to any of its properties (ex: GetViewForAnnotation) and also using the Delegate property then you have a bug.

    Thursday, April 30, 2015 2:40 PM
  • User363 posted

    Here is the additional code. Hopefully this clears it up a bit. FYI, if I comment out the ShouldChangeCharacters delegate, the app works fine -- there are no additional run-time errors.

    private UITextField InitializeAmountField(CGRect rect)
    {
        UITextField amountField = new UITextField(rect);
        amountField.BorderStyle = UITextBorderStyle.Line;
        amountField.Font = UIFont.SystemFontOfSize(22f);
        amountField.KeyboardType = UIKeyboardType.DecimalPad;
        amountField.TextAlignment = UITextAlignment.Right;
        amountField.VerticalAlignment = UIControlContentVerticalAlignment.Center;
        amountField.TextColor = UIColor.FromRGB(0, 64, 0);
        amountField.Delegate = new CatchEnterDelegate();
        amountField.Alpha = 1;
        amountField.Text = "";
        amountField.ClearsOnBeginEditing = true;
        amountField.EditingDidEnd += (object sender, EventArgs e) =>
        {  
                SOME OTHER STUFF... 
        };
    
        amountField.EditingDidBegin += (object sender, EventArgs e) =>
        {  
                SOME OTHER STUFF... 
        };
        amountField.ShouldChangeCharacters = (UITextField textField, NSRange range, string replacementString) =>
        {
                string entry = textField.Text + replacementString;
                if (entry.Contains("."))
                {
                    string[] number = entry.Split('.');
                    if (number[0].Length > 6) return false;
                    if (number[1].Length > 2) return false;
                }
                else
                {
                    return entry.Length <= 6;
                }
                return true;
        };
    
        private class CatchEnterDelegate : UITextFieldDelegate
        {
                public override bool ShouldReturn(UITextField textField)
                {
                        textField.ResignFirstResponder();
                        return true;
                }
        }
    }
    
    Thursday, April 30, 2015 3:10 PM
  • User181 posted

    Aha. There's the bug. This line:

    amountField.Delegate = new CatchEnterDelegate();
    

    Is undone by any of these lines:

    amountField.EditingDidEnd += ...
    amountField.EditingDidBegin += ...
    amountField.ShouldChangeCharacters = ...
    

    That's because the way those events and callbacks work is by creating a delegate object internally and assigning it to the Delegate property, which would overwrite your CatchEnterDelegate object. That's what the exception is telling you about.

    The result is that the ShouldReturn method in your delegate class will never be called. That is dead code.

    The fix in this case is to use the ShouldReturn property instead of the Delegate:

    amountField.ShouldReturn = textField =>
        {
            textField.ResignFirstResponder();
            return true;
        };
    
    Thursday, April 30, 2015 3:49 PM
  • User363 posted

    Thanks Adam - that worked perfectly. I was able to remove the the line setting UIApplication.CheckForEventAndDelegateMismatches to false as well. This can be marked as answered.

    Thursday, April 30, 2015 8:10 PM
  • User43463 posted

    Hi, i got the same problem with the UIWebView in my custom renderer specifically this line:

    webView.ShouldStartLoad += HandleShouldStartLoad;
    

    my function:

    bool HandleShouldStartLoad (UIWebView webView, NSUrlRequest request, UIWebViewNavigationType navigationType) {
        if (request.HttpMethod == "POST" && request.MainDocumentURL.Fragment == "selectOutlet") {
            var data = new NSString (request.Body, NSStringEncoding.UTF8);
            var dataArray = data.ToString ().Split ('=');
    
            var view = Element as CTBOutletTreeView;
            view.SelectOutlet (dataArray [1]);
            return false;
        }
        return true;
    }
    

    I'm not really sure what went wrong here.

    Monday, May 4, 2015 1:58 AM
  • User43463 posted

    I fixed by seting Delegate = null;

    Monday, May 4, 2015 2:23 AM
  • User181 posted

    Anyone fixing it by nulling out the Delegate needs to take a look at where else the Delegate or Source property is being set. Assigning null to the Delegate is almost certainly not be right fix. Please read through my comments above, and if you have time watch the video I linked above to try to understand why this exception is occurring. There is a right way to fix this exception. Assigning null to the Delegate is not the right fix, though.

    Monday, May 4, 2015 3:08 AM
  • User94060 posted

    Same issue here with extending the Xamarin.Forms WebViewRenderer with a ShouldStartLoad and LoadFinished event. Probably a very common approach in many Xamarin.Forms projects. It's hard to follow your advise to check where Delegate is set, as these are Xamarin.Forms internals.

    Wednesday, May 6, 2015 10:07 AM
  • User181 posted

    For Xamarin.Forms renderers this means that you are overriding what the Forms renderer is doing, and that probably means you're breaking some stock behavior. Again, this is telling you about a bug in your code that you didn't know about.

    Wednesday, May 6, 2015 1:00 PM
  • User33110 posted

    I just downloaded the MoibleCRM.iOS sample app and I get the same exception thrown to the application's entry point in Main.cs from within visual studio (related to the map controls). See the attached image file for a screenshot of the exception window.

    Any ideas on how to proceed?

    Wednesday, May 6, 2015 9:13 PM
  • User181 posted

    The sample app has a bug in it. I don't have the sample app code so I can't tell you how to fix it, but it looks like a map view issue. I would guess that they have a custom renderer for a map view that is overriding the delegate (similar to the issue that @Netwerkapp had). Look for that.

    Wednesday, May 6, 2015 9:25 PM
  • User33110 posted

    A bug in the sample app... bummer. I stepped through the code, but nothing jumped out at me. That's not saying much since I am just getting my feet wet with this stuff... and hence the interest in a sample app. Thanks for the prompt reply!

    ( If I end up figuring out a fix for the bug in the sample app, I will post it here. If someone else does and/or wants to take a stab at it... I downloaded the sample app code from here: http://xamarin.com/prebuilt/crm )

    Wednesday, May 6, 2015 9:47 PM
  • User181 posted

    @BrendanMuir , that actually looks like a bug in Xamarin.Forms.Maps itself. I filed a bug report. In the meantime you can use the workaround above (setting UIApplication.CheckForEventAndDelegateMismatches to false).

    Wednesday, May 6, 2015 9:57 PM
  • User33110 posted

    Adding UIApplication.CheckForEventAndDelegateMismatches=false to the Main method as the first line called at application start did the trick - thank you for the quick fix!

    If even the sample app coded by folks who know what they are doing contains some unwanted overriding of delegates.... that just goes to show how easy it is to do if you are not careful... and probably makes a strong argument for why such an exception is helpful. Thanks @adamkemp

    Wednesday, May 6, 2015 10:13 PM
  • User181 posted

    Yeah, this is a very easy thing to do on accident, and the bug is very subtle. That's why I lobbied for them to add this exception. It's going to be painful dealing with it for a while, but it's much better for the long term.

    Wednesday, May 6, 2015 10:16 PM
  • User10166 posted

    @adamkemp I'm getting this in my Forms app in my WebViewCustomRenderer and I can't fix it using the method you described above. As a simple test case, use the WorkingWithWebview sample project (https://github.com/xamarin/xamarin-forms-samples). In the iOS BaseUrlWebViewRenderer add the following:

        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            this.LoadFinished += OnLoadFinished;
        }
        private void OnLoadFinished(object sender, EventArgs eventArgs)
        {
            var i = 0;
        }
    

    and then try to run. The only delegate that is defined is the Application Delegate in Main.cs:

        static void Main (string[] args)
        {
            // if you want to use a different Application Delegate class from "AppDelegate"
            // you can specify it here.
            UIApplication.Main (args, null, "AppDelegate");
        }
    

    Why would the ApplicationDelegate impact the Webview Events? Does this mean I can't use events at all with Forms?

    Thursday, May 7, 2015 1:49 PM
  • User181 posted

    I answered @ChristineBlanda's question in this thread.

    Thursday, May 7, 2015 6:13 PM
  • User41210 posted

    Adam,

    I am using this in my AppDelegate

    _locationManager = new CLLocationManager (); _locationManager.Delegate = new Helpers.CoreLocationManager ();

    and it works great, however I have another class where I need track the event for locations _locationManager.LocationsUpdated += this.ProcessLocationUpdate;

    I get your logic as to how using the delegate breaks the LocationsUpdated piece but I need access to class-related variables when I use _locationManager.LocationsUpdated

    Should I just override CoreLocationManager.ProcessLocationUpdate and pop an event for my class to listen to..is that the recommended model?

    Thanks in Advance

    Thursday, May 7, 2015 11:35 PM
  • User4553 posted

    That would be my suggestion. If you absolutely need the delegate, then any events should be surfaced via the delegate.

    Friday, May 8, 2015 8:00 PM
  • User181 posted

    I believe you can also have multiple instances of CLLocationManager so you could have one instance use the events and the other use the delegate. You shouldn't share the same instance between classes unless you are providing an API to control how it's accessed.

    Friday, May 8, 2015 8:05 PM
  • User14795 posted

    Setting the delegate to null first fixed it for me. Thanks for posting this information I just knew I was going to being rewriting code today.....

    Wednesday, May 27, 2015 3:57 PM
  • User181 posted

    Once again, setting the Delegate to null first does not fix anything. You are only masking the bug. This exception is telling you that something is wrong. You need to fix the problem rather than just going back to pretending it doesn't exist.

    Wednesday, May 27, 2015 4:00 PM
  • User14795 posted

    I understand and the bug will be fixed but I had to release this specific release today and since the code in question has been there since the first release it can wait for the next release to be fixed properly. Without this posting however it would have been much more difficult to determine what was causing the error.

    Wednesday, May 27, 2015 7:43 PM
  • User73524 posted

    Thanks @adamkemp

    I appreciate your insistence that it is masking another bug. Although setting the delegate to null seemed to fix the problem, I decided to go back and look at it further based on your comments.

    I am using a modified version of the nice BindableMapTest example for creating custom pins. One of the things that is done in the BindableMapTest example is setting a delegate and setting RegionChanged as well in OnElementChanged. I commented out RegionChanged and it had the same effect as setting the delegate to null before I created a new one.

    //mkMapView.RegionChanged += delegate
    //{
    //    if (Element == null)
    //    {
    //        return;
    //    }
    
    //    do something
    //};
    
    //mapView.Delegate = null;
    mapView.Delegate = new aMapDelegate(false, true, aDetailCommand);
    

    Also, a number of other troublesome issues seemed to disappear...so it seems that this was causing a bit of internal confusion.

    Again, thanks a lot for taking the time to explain this.

    So, going forward, I should have aMapDelegate handle RegionChanged?

    Friday, May 29, 2015 5:18 AM
  • User73524 posted

    @adamkemp Double thanks - I owe you a beer. PM an address to @hyperubik on twitter if you want to collect.

    Friday, May 29, 2015 5:42 AM
  • User181 posted

    going forward, I should have aMapDelegate handle RegionChanged?

    Yeah, that's the right way to handle it. Alternatively you could convert to using only events/properties without a delegate.

    Friday, May 29, 2015 1:20 PM
  • User1067 posted

    Thanks for suggesting/adding this exception (and explaining why). Of course it's annoying at first, since it breaks seemingly working code. But fixing this actually fixed another problem in our code we've already had worked around. In combination with MvvmCross really weird behaviour can occur due to broken bindings.

    Monday, June 15, 2015 11:14 AM
  • User123267 posted

    Thanks @adamkemp. I also modified the Bindable MapTest sample to use in my application. And it started causing issue with the update iOS(8.10.0) . I followed the same approach as @TimothyLeeRussell . Now the event overwriting issue is gone.

    Wednesday, June 24, 2015 7:53 AM
  • User81908 posted

    There's a bug in your bug fix. I unhook all events and then use a delegate. And get this error.

    locationManager.LocationsUpdated -= this.HandleNewCoordinatesFromStartup;

    locationManager.AllowDeferredLocationUpdatesUntil(pingDistance, pingTime); locationManager.Delegate = new GPSMileageLocationDelegate();

    Wednesday, September 23, 2015 4:58 PM
  • User181 posted

    I don't know which fix you're talking about. The real fix is to not mix events and delegates period. All the other suggestions are just hacking around the problem. You just need to pick one: events or delegates. Any mixture of the two is a recipe for bugs in your code.

    Wednesday, September 23, 2015 6:03 PM
  • User81908 posted

    Not sure I follow. They aren't mixed. All events are unhooked before using a new delegate. I based this on your earlier post:

    Aha. There's the bug. This line:

    amountField.Delegate = new CatchEnterDelegate(); Is undone by any of these lines:

    amountField.EditingDidEnd += ... amountField.EditingDidBegin += ... amountField.ShouldChangeCharacters = ... That's because the way those events and callbacks work is by creating a delegate object internally and assigning it to the Delegate property, which would overwrite your CatchEnterDelegate object. That's what the exception is telling you about.

    The result is that the ShouldReturn method in your delegate class will never be called. That is dead code.

    The fix in this case is to use the ShouldReturn property instead of the Delegate:

    amountField.ShouldReturn = textField => { textField.ResignFirstResponder(); return true; };

    Wednesday, September 23, 2015 6:40 PM
  • User81908 posted

    Let me clarify, my original code (posted above) got a compiler warning. Which I don't think is correct based on your post which I pasted directly above. I shouldn't have said your code had a bug in it.

    Is this a bug:

    amountField.Delegate = new CatchEnterDelegate(); amountField.Delegate = new CatchEnterDelegateA(); amountField.Delegate = new CatchEnterDelegateB();

    Wednesday, September 23, 2015 6:55 PM
  • User181 posted

    They are mixed if you at any point use an event and also at any point use a delegate.

    The reason your attempted fix didn't work is because you assumed that when you unsubscribe the last event handler that it also clears the internal delegate. It doesn't do that, probably for efficiency reasons. The implementation works like this: in the add method for the event (i.e., the code that runs for +=) it looks at the current delegate. If the delegate is null then it creates a new internal wrapper delegate to forward to the event handler. If it isn't null it checks if the type is the internal delegate, and if so it hooks the new event handler up to that internal delegate. Otherwise it throws.

    There is no code in the remove method to null out the delegate (in fact, it runs the same exact code as the add method to verify that you're using its internal delegate type, which means -= will create an internal delegate if it starts null). That means -= does not prevent the exception from happening.

    The fix, again, is to pick: either always use events with that object or never use events. Don't mix them at all, even if you don't ever use them at the same time.

    Wednesday, September 23, 2015 6:59 PM
  • User181 posted

    Is this a bug:

    amountField.Delegate = new CatchEnterDelegate();
    amountField.Delegate = new CatchEnterDelegateA();
    amountField.Delegate = new CatchEnterDelegateB();
    

    As far as I know that code shouldn't trigger any runtime exceptions. That's not the same as the code you put in your first post, though. This is the logic that is used in the delegate's setter method:

        if (UIApplication.CheckForEventAndDelegateMismatches && currentDelegateValue != null && newDelegateValue != null && currentDelegateValue.GetType().IsAssignableFrom(internalDelegateType) && !newDelegateValue.GetType().IsAssignableFrom(internalDelegateType))
    {
        throw new InvalidOperationException(string.Format("Event registration is overwriting existing delegate. Either just use events or your own delegate: {0} {1}", newDelegateValue.GetType(), internalDelegateType));
    }
    

    If the original delegate is not the internal delegate type and neither is the new one then you won't get an exception. You should only get an exception if you try to overwrite an internal delegate with your own or vice versa, and once an internal delegate is assigned it is never unassigned.

    Wednesday, September 23, 2015 7:05 PM
  • User81908 posted

    Having trouble reconciling a logical error when mixing the delegates which will lead to dead code vs an actual application error when overwriting delegates.

    amountField.Delegate = new CatchEnterDelegate(); amountField.Delegate = new CatchEnterDelegateA();

    vs

    amountField.Scroll += HandleMyScroll; amountField.Delegate = new CatchEnterDelegate();

    Both are overwriting an existing delegate. Are you saying the second example causes errors besides logical/dead code bugs?

    Edit: assuming the setter you provided was not used.

    Wednesday, September 23, 2015 7:24 PM
  • User181 posted

    The first example could be a bug, but it's not always a bug. After all, you may just legitimately be changing which delegate you want to use for this object. The delegate property comes from the underlying Objective-C API, and it's just like any other property. It's not meant to be write-once or anything. It's not immutable.

    The second case is a special case because of the way that Xamarin.iOS tries to bridge between the underlying API (which only uses the delegate property) and C# events. It used to be that every time you added an event it just blindly overwrote the underlying delegate property (if it had one of your custom delegates), and likewise if you assigned a new delegate it just blindly overwrote the internal delegate. This was a source of a lot of very subtle bugs (everyone in this thread had one of those bugs that they didn't even know about) because it silently broke either an event handler or a delegate.

    The reason this bug is so subtle is because there's no obvious connection between the delegate and the events. It's not obvious when you're writing the code in your second example that the second line breaks the first line. It is obvious in the first example, though. Therefore Xamarin added code to detect the subtle bug that everyone was running into because that use case is always wrong. The first use case may or may not be wrong, but it's the same as any other property that can get overwritten improperly. It's not a category of bug unique to Xamarin.iOS. The code that Xamarin added (which I quoted above) specifically checks for the case where you are mixing events and delegates, and it throws an exception at runtime when you do that.

    Wednesday, September 23, 2015 7:34 PM
  • User81908 posted

    OK. Thank you for help. I wrote that example hoping to get a clear yes or no as to whether this was an actual bug. If I understand your long response, it is not.

    It may be a logical bug to mix delegates but it will not cause a system crash. Same rules apply to any code.

    Wednesday, September 23, 2015 7:47 PM
  • User115237 posted

    Hi @adamkemp,

    I got the same exception in my project. I read all of the comment, but the problem is I am not the author of the project I am working on and I have a hard time fixing this issue. So my exception comes from a custom map renderer in iOS. In my OnElementChanged function.

    protected override void OnElementChanged(Xamarin.Forms.Platform.iOS.ElementChangedEventArgs<View> e)
    {
        base.OnElementChanged(e);
        if (e.NewElement != null) {
            var mapView = (MKMapView)Control;
            mapView.WeakDelegate = new MyMapDelegate((MyMap)Element);
        }
    }
    

    Here a new MKMapViewDelegate subclass is assigned to the WeakDelegate of the mapView as per in this recipe HERE.

    And this is how my delegate is looking:

    protected class MyMapDelegate : MKMapViewDelegate {
    
        public MyMapDelegate(MyMap map) {
            // some stuff
        }
    
        public override void DidSelectAnnotationView(MKMapView mapView, MKAnnotationView view)
        {
            // some stuff
        }
    
        public override void DidDeselectAnnotationView(MKMapView mapView, MKAnnotationView view)
        {
            // some stuff
        }
    
        public override MKAnnotationView GetViewForAnnotation (MKMapView mapView, IMKAnnotation annotation)
        {
            // some stuff
        }    
    
        protected override void Dispose(bool disposing)
        {
            // some stuff
        }
    
        // as an optimization, you should override this method to add or remove annotations as the
        // map zooms in or out.
        public override void RegionChanged (MKMapView mapView, bool animated) {}
    }
    

    The fix, again, is to pick: either always use events with that object or never use events. Don't mix them at all, even if you don't ever use them at the same time.

    As I understood I should not mix events and delegates but here in the code I see only a delegate and no events. Why is it throwing an error when I don't use any events?

    Thanks in advice :)

    Monday, November 2, 2015 2:44 PM
  • User181 posted

    The base class renderer also uses either events or a delegate (I'm not sure which). You are overriding theirs, which will break things. Since Xamarin didn't give you clean hooks to plug into this functionality it is tedious to do this right. What you have to do is create a wrapper delegate that can forward to another delegate (in addition to whatever you wanted your own delegate to do). Then when you create it you copy the original delegate so you can forward to it, null out the delegate property (to avoid the exception), and then assign your wrapper as the delegate.

    Monday, November 2, 2015 3:04 PM
  • User175849 posted

    In the project I'm working on, a searchable map is part of a step by step process. On back button press (await Navigation.PopAsync()) this exception was being thrown.

    System.InvalidOperationException: Event registration is overwriting existing delegate. Either just use events or your own delegate: MyApp.Mobile.iOS.Approach.SearchMapDelegate MapKit.MKMapView+MKMapViewDelegate at UIKit.UIApplication.EnsureEventAndDelegateAreNotMismatched (System.Object del, System.Type expectedType) [0x0001b] in /Users/builder/data/lanes/2377/73229919/source/maccore/src/UIKit/UIApplication.cs:92 at MapKit.MKMapView.EnsureMKMapViewDelegate () [0x0000b] in /Users/builder/data/lanes/2377/73229919/source/maccore/src/build/ios/native/MapKit/MKMapView.g.cs:1667 at MapKit.MKMapView.removeRegionChanged (System.EventHandler`1 value) [0x00000] in /Users/builder/data/lanes/2377/73229919/source/maccore/src/build/ios/native/MapKit/MKMapView.g.cs:2027 at Xamarin.Forms.Maps.iOS.MapRenderer.Dispose (Boolean disposing) [0x00042] in :0 at Foundation.NSObject.Dispose () [0x00000] in /Users/builder/data/lanes/2377/73229919/source/maccore/src/Foundation/NSObject2.cs:130 at Xamarin.Forms.Platform.iOS.Platform.DisposeModelAndChildrenRenderers (Xamarin.Forms.Element view) [0x00038] in :0 at Xamarin.Forms.Platform.iOS.Platform.HandleChildRemoved (System.Object sender, Xamarin.Forms.ElementEventArgs e) [0x00007] in :0 …

    To Fix the problem, in the MapRenderer on dispose I set the Delegate to null.

    public class SearchMapRenderer : MapRenderer ?{?

    protected override void Dispose(bool disposing) { if (disposing) { MapView.Delegate = null; MapView.WeakDelegate = null; } base.Dispose(disposing); } ??…

    Wednesday, January 6, 2016 5:57 PM
  • User181 posted

    Were you writing to the Delegate property elsewhere in your custom renderer subclass? If so then that's the actual bug. The code you quoted is not a real fix. It's covering up the real problem.

    Wednesday, January 6, 2016 8:13 PM
  • User77446 posted

    @adamkemp Hmmm... Not sure but seems like there's no way to redefine GetViewForAnnotation() or provide any other factory for custom annotation views without settings Delegate to null first. Am I right?

    So when choosing between buggy workaround and doing nothing at all, the first option wins :)

    I got nearly the same code as a couple comments above: https://forums.xamarin.com/discussion/comment/162761/#Comment_162761

    I'm subclassing my renderer from Xamarin.Forms.Maps.iOS.MapRenderer and actually the only method I override is OnElementChanged, so I have no more code related to accessing events or delegates.

    Wednesday, March 16, 2016 10:54 AM
  • User181 posted

    Forms renderers are often not designed for that kind of customization. If you're seeing this exception then using the null trick is still just making a bug: you're preventing the base class from handling some map delegate callbacks, which will break some behaviors in the map (or its interaction with the PCL class).

    The point isn't that using a hack is inherently wrong. The point is that you have to do more than just mask the exception. The exception is telling you that you're breaking something. You have to do something to ensure that you are still allowing the original delegate to receive messages. Only then can you safely do this hack with writing null first.

    That's not easy to do because, again, Forms renderers aren't designed well for this kind of extension. Sometimes it's better to write a whole new renderer than it is to subclass and customize.

    Wednesday, March 16, 2016 2:53 PM
  • User252965 posted

    Hi @adamkemp , I have UITableViewController whose tableview's scrolled event is creating this exception. I haven't used delegate anywhere in the controller. Any ideas on how to correctly fix this?

    Thursday, October 20, 2016 11:07 PM
  • User181 posted

    UITableViewController automatically sets the Delegate and DataSource to itself, which means when you use that class you always have a delegate. You should therefore never use the Scrolled event.

    Since the delegate is already set (to itself) you just need to add a method like this to your UITableViewController subclass:

    [Export("scrollViewDidScroll:")]
    private void ScrollViewDidScroll(UIScrollView scrollView)
    {
    }
    
    Thursday, October 20, 2016 11:15 PM
  • User368434 posted

    I understand this is an old thread, I read through it but still it is not clear fro me how do I subscribe to the Scrolled event in custom ListViewRenderer?

    Wednesday, September 5, 2018 5:40 AM