Search code examples
c#formscompact-frameworkwindows-ceapplication-shutdown

How can I cleanly shortcircuit my app from a canceled login form, and invoke my main form when login is successful?


I have a login form that displays prior to the main form, using this code in Program.cs:

static void Main()
{
    AppDomain.CurrentDomain.UnhandledException += Unhandled;
    frmLogin loginForm;
    using (loginForm = new frmLogin())
    {
        if (loginForm.ShowDialog() == DialogResult.OK)
        {
            NRBQConsts.userName = loginForm.UserName;
            NRBQConsts.pwd = loginForm.Password;
            NRBQConsts.currentSiteNum = loginForm.SiteNumber;
        }
        else
        {
            Application.Exit(); // klang!
        }
    }
    Application.Run(new frmMain());
}

static void Unhandled(object sender, UnhandledExceptionEventArgs exArgs)
{
    ExceptionLoggingService.Instance.WriteLog(String.Format("From 
application-wide exception handler: {0}", exArgs.ExceptionObject));
}

If the user chooses the "Close" button (rather than attempting to log in), the login form should close, the main form should not display, and the app should shut down. If the user chooses the "OK" button, the login form should close, and the main form should then display.

What happens is choosing the "Close" button causes the app to crash with the clangy, Gong Show-esque "crash" sound. There is this exception msg in my log file:

Date: 4/9/2009 8:57:51 PM Message: Reached frmLogin.buttonOK_Click Date: 4/9/2009 8:57:51 PM Message: From application-wide exception handler: System.NullReferenceException: NullReferenceException at NRBQ.frmLogin.GetSiteNum() at NRBQ.frmLogin.buttonOK_Click(Object sender, EventArgs e) at System.Windows.Forms.Control.OnClick(EventArgs e) at System.Windows.Forms.Button.OnClick(EventArgs e) at System.Windows.Forms.ButtonBase.WnProc(WM wm, Int32 wParam, Int32 lParam) at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam) at Microsoft.AGL.Forms.EVL.EnterModalDialog(IntPtr hwnModal) at System.Windows.Forms.Form.ShowDialog() at NRBQ.Program.Main()

Here is the pertinent code in the login form:

public partial class frmLogin : Form
{
    public string UserName { get { return textBoxUsername.Text; } }
    public string Password { get { return textBoxPwd.Text; } }
    public string SiteNumber { get { return GetSiteNum(); } }

    private string GetSiteNum()
    {
        String candidateSiteNum = 
    listBoxSitesWithFetchedData.SelectedItem.ToString();
        if (String.IsNullOrEmpty(candidateSiteNum))
        {
            candidateSiteNum = "42"; // TODO: Remove before deploying
        }
        return candidateSiteNum;
    }

    public frmLogin()
    {
        InitializeComponent();
    }

    private void buttonClose_Click(object sender, EventArgs e)
    {
        ExceptionLoggingService.Instance.WriteLog("Reached 
    frmLogin.buttonClose_Click");
    }

    private void buttonOK_Click(object sender, EventArgs e)
    {
        ExceptionLoggingService.Instance.WriteLog("Reached 
    frmLogin.buttonOK_Click");
        if (SanityCheck())
        {
            this.Close(); // closing this login form should make the main 
    form active/visible
        }
        else
        {
            MessageBox.Show("You have not yet provided some key data; be 
    sure to enter a username and a password");
        }
    }

    private bool SanityCheck()
    {
        // For now, anyway, no site has to be selected; if none, use "42" by 
    default
        return ((!(String.IsNullOrEmpty(textBoxUsername.Text.Trim())))
            && (!(String.IsNullOrEmpty(textBoxPwd.Text.Trim()))));
    }

Note: The "OK" button has its DialogResult set to "OK"; the "Close" button has its DialogResult set to "Cancel"

Why does Application.Exit() cause the app to crash? What is the kinder, gentler way to terminate the app?

And why does choosing the Okay button not allow the main form to subsequently display? The login form does close, even when selecting the OK button (if the sanity check passes), but that's all...the main form does not display.

UPDATE

I modified my code, based on the answers below, and now the Cancel works fine (closes the app with no klang/crash), but I'm still not seeing my main form after selecting the "OK" button on the login form, even though I'm programatically setting the DialogResult to OK in the login form code.

Pertinent code below.

PROGRAM.CS

static void Main()
{
    AppDomain.CurrentDomain.UnhandledException += Unhandled;
    frmLogin loginForm;
    using (loginForm = new frmLogin())
    {
        if (loginForm.ShowDialog() == DialogResult.OK)
        {
            HHSConsts.userName = loginForm.UserName;
            HHSConsts.pwd = loginForm.Password;
            HHSConsts.currentSiteNum = loginForm.SiteNumber;
            // TODO: Verify that these vals are valid
            Application.Run(new frmMain());
        }
    }
}

FRMLOGIN.CS

private void buttonClose_Click(object sender, EventArgs e)
{
    ExceptionLoggingService.Instance.WriteLog("Reached 
frmLogin.buttonClose_Click");
    this.DialogResult = DialogResult.Cancel; // <= necessary?
    this.Close();
}

private void buttonOK_Click(object sender, EventArgs e)
{
    ExceptionLoggingService.Instance.WriteLog("Reached 
frmLogin.buttonOK_Click");
    this.DialogResult = DialogResult.OK; 
    if (SanityCheck())
    {
        this.Close();
    }
    else
    {
        MessageBox.Show("You have not yet provided some key data; be 
sure to enter a username and a password");
    }
}

This is what I see in my log file:

Date: 4/9/2009 9:56:47 PM Message: Reached frmLogin.buttonOK_Click Date: 4/9/2009 9:56:48 PM Message: From application-wide exception handler: System.NullReferenceException: NullReferenceException at HHS.frmLogin.GetSiteNum() at HHS.frmLogin.get_SiteNumber() at HHS.Program.Main()

As Merle Haggard sang, "So now it's back, to the coderoom again..."

UPDATE 2

I added a messagebox to GetSiteNum():

private string GetSiteNum()
{
    ExceptionLoggingService.Instance.WriteLog("Reached 
frmLogin.GetSiteNum()");
    String candidateSiteNum = 
listBoxSitesWithFetchedData.SelectedItem.ToString();
    // TODO: Remove after testing
    MessageBox.Show(String.Format("candidateSiteNum = {0}", 
candidateSiteNum));
    if (String.IsNullOrEmpty(candidateSiteNum))
    {
        candidateSiteNum = "03"; // TODO: Remove before deploying
    }
    return candidateSiteNum;
}

...and I don't see it, so it's blowing up on:

String candidateSiteNum = listBoxSitesWithFetchedData.SelectedItem.ToString();

...right after selecting the "OK" button on this login form.


Solution

  • Don't call Close() against your login form, just set DialogResult like below. Also note that I'm not setting DialogResult.OK until after SanityCheck() tells us we are good to go:

        private void buttonClose_Click(object sender, EventArgs e)
        {
            ExceptionLoggingService.Instance.WriteLog("Reached frmLogin.buttonClose_Click");
            this.DialogResult = DialogResult.Cancel; 
        }
    
        private void buttonOK_Click(object sender, EventArgs e)
        {
            ExceptionLoggingService.Instance.WriteLog("Reached frmLogin.buttonOK_Click");
            if (SanityCheck())
            {
                this.DialogResult = DialogResult.OK; 
            }
        }
    

    You should also make sure an item in the ListBox has been selected in your SanityCheck() function:

        private bool SanityCheck()
        {
            if (listBoxSitesWithFetchedData.SelectedIndex == -1)
            {
                MessageBox.Show("You must select a site first!");
                return false;
            }
    
            bool pass = ((!(String.IsNullOrEmpty(textBoxUsername.Text.Trim())))
                && (!(String.IsNullOrEmpty(textBoxPwd.Text.Trim()))));
            if (!pass)
            {
                MessageBox.Show("You have not yet provided some key data; be sure to enter a username and a password");
            }
    
            return pass;
        }