Getting NullReferenceException unless I expand base class object while debugging

221 views Asked by At

First off, I'm using Xamarin Studio 6.1.3 in case that makes any difference.

I'm creating a simple app and want a login form to appear as a sheet. I followed the Xamarin tutorial for creating sheets (https://developer.xamarin.com/guides/mac/user-interface/working-with-dialogs/#Creating_a_Custom_Sheet) but am running into an issue.

Per the tutorial, I have created a class:

using System;
using Foundation;
using AppKit;

namespace SampleProject
{
  public partial class UserLoginController : NSViewController
  {
    private NSViewController _presentor;

    public string Username
    {
        get { return TxtUsername.StringValue; }
        set { TxtUsername.StringValue = value; }
    }

    public string Password
    {
        get { return TxtPassword.StringValue; }
        set { TxtPassword.StringValue = value; }
    }

    public NSViewController Presentor
    {
        get { return _presentor; }
        set { _presentor = value; }
    }

    public UserLoginController(IntPtr handle) : base(handle)
    {

    }

    private void CloseDialog()
    {
        Presentor.DismissViewController(this);
    }

    partial void BtnCancelClick(NSObject sender)
    {
        RaiseDialogCanceled();
        CloseDialog();
    }

    partial void BtnLoginClick(NSObject sender)
    {
        RaiseDialogAccepted();
        CloseDialog();
    }

    public EventHandler DialogAccepted;

    internal void RaiseDialogAccepted()
    {
        if (this.DialogAccepted != null)
            this.DialogAccepted(this, EventArgs.Empty);
    }

    public EventHandler DialogCanceled;

    internal void RaiseDialogCanceled()
    {
        if (this.DialogCanceled != null)
            this.DialogCanceled(this, EventArgs.Empty);
    }


  }
}

And I have added an override for PrepareForSegue in my ViewController class:

    public override void PrepareForSegue(NSStoryboardSegue segue, NSObject sender)
    {
        base.PrepareForSegue(segue, sender);

        switch (segue.Identifier)
        {
            case "UserLoginSegue":
                UserLoginController loginSheet = segue.DestinationController as UserLoginController;
                loginSheet.Username = ""; //This line throws NullReferenceException unless I set a breakpoint and expand loginSheet.Base before allowing this line to execute.
                loginSheet.Password = "";
                loginSheet.Presentor = this;
                loginSheet.DialogAccepted += (object s, EventArgs e) => { Console.WriteLine("OK Clicked"); };
                loginSheet.DialogCanceled += (object s, EventArgs e) => { Console.WriteLine("Cancel Clicked"); };
                break;
        }
    }

See the comment in the above code block. I basically set a breakpoint on that line and when it triggers, I inspect the loginSheet object. If I expand the Base object to inspect it and then continue execution, everything works as expected. If I don't, I get a NullReferenceException whenever code tries to access any fields/properties/methods in the UserLoginController class.

I am completely baffled as to why this is happening. I set a breakpoint in the constructor of UserLoginController and verified it is being called with a handle and that the base constructor should be called as well.

I've read through the tutorial several times and don't see anything that I'm missing. Can't seem to find anybody else having the same problem.

My ultimate question is: What can I do to make the code work as expected?

For the sake of learning (which may shed light on the problem): What is going on behind the scenes when I inspect the base object of my UserLoginController class while debugging?

2

There are 2 answers

2
John On BEST ANSWER

The solution turned out to be that I need to check the View property of my UserLoginController.

I added the following line:

var theView = loginSheet.View;

and everything works as expected. I have yet to dig into the View property to see what it is doing behind the scenes that makes everything work.

Here is the modified, working PrepareForSegue override method:

public override void PrepareForSegue(NSStoryboardSegue segue, NSObject sender)
{
    base.PrepareForSegue(segue, sender);

    switch (segue.Identifier)
    {
        case "UserLoginSegue":
            UserLoginController loginSheet = segue.DestinationController as UserLoginController;
            var theView = loginSheet.View;
            loginSheet.Username = "";
            loginSheet.Password = "";
            loginSheet.Presentor = this;
            loginSheet.DialogAccepted += (object s, EventArgs e) => { Console.WriteLine("OK Clicked"); };
            loginSheet.DialogCanceled += (object s, EventArgs e) => { Console.WriteLine("Cancel Clicked"); };
            break;
    }
}
2
Chris Hamons On

When you expand an object in the debugger, all properties not marked up with certain attributes are read via reflection so that we can display them in the IDE.

Maybe one of those properties has a side effect? You should be able to reproduce the effect using reflection, then bisect the property list to see who's effecting your behavior.