Santosh
Santosh

Reputation: 2495

C# Windows form closing automatically

I have a windows application where my first windows form is Login. After successful login, it has to open "Home" form. I see "Home" form while debugging, but once the code enters into Dispose method in Home.Designer.cs, my application stops.

My Login page code looks like following:

private void loginbtn_Click(object sender, EventArgs e)
{
        String username = "admin";
        String password = "admin";

        String @uname = Unametxtbox.Text;
        String @pass = Passtextbox.Text;

        if (@uname.Equals(username) && @pass.Equals(password))
        {                
            MessageBox.Show("Login Successful");                
            Home home = new Home();
            home.Show();
            this.Close();
        }
        else
        {
            MessageBox.Show("Invalid Credentials!");
        }
}

My Home.cs page looks like following:

public partial class Home : Form
{
    public Home()
    {
        InitializeComponent();
    }
} 

And the Home.Designer.cs has following code:

partial class Home
{
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Home));
        this.label1 = new System.Windows.Forms.Label();
        this.pictureBox1 = new System.Windows.Forms.PictureBox();
        this.closebtn = new System.Windows.Forms.PictureBox();
        this.groupBox1 = new System.Windows.Forms.GroupBox();
        this.Storedbtn = new System.Windows.Forms.Button();
        this.Soldbtn = new System.Windows.Forms.Button();
        this.Transbtn = new System.Windows.Forms.Button();
        this.Supbtn = new System.Windows.Forms.Button();
        this.Empbtn = new System.Windows.Forms.Button();
        this.Custbtn = new System.Windows.Forms.Button();
        ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
        ((System.ComponentModel.ISupportInitialize)(this.closebtn)).BeginInit();
        this.groupBox1.SuspendLayout();
        this.SuspendLayout();
  }

    #endregion

    private System.Windows.Forms.Label label1;
    private System.Windows.Forms.PictureBox pictureBox1;
    private System.Windows.Forms.PictureBox closebtn;
    private System.Windows.Forms.GroupBox groupBox1;
    private System.Windows.Forms.Button Storedbtn;
    private System.Windows.Forms.Button Soldbtn;
    private System.Windows.Forms.Button Transbtn;
    private System.Windows.Forms.Button Supbtn;
    private System.Windows.Forms.Button Empbtn;
    private System.Windows.Forms.Button Custbtn;
}

If I comment this.Close(); in loginbtn_Click, I can see the Home windows form, but the Login Windows form doesn't get closed.

What I am missing here, Thanks in advance.

Upvotes: 3

Views: 1224

Answers (3)

IV.
IV.

Reputation: 9133

Your post states that your objective is to show the login form first, requiring user credentials before showing the main form. One way to achieve this is to force the creation of the main window handle while also preventing it from becoming visible by overriding SetVisibleCore until the user succeeds in logging in. Exit the app if login is cancelled (or if user validation fails of course). With a valid login, the app proceeds with HomeForm as the main application window as it should be.

login flow


public partial class HomeForm : Form
{
    public HomeForm()
    {
        InitializeComponent();
        // Ordinarily we don't get the handle until
        // window is shown. But we want it now.
        _ = Handle;
        // Call BeginInvoke on the new handle so as not to block the CTor.
        BeginInvoke(new Action(()=> execLoginFlow()));
        // Ensure final disposal of login form. Failure to properly dispose of window 
        // handles is the leading cause of the kind of exit hang you describe.
        Disposed += (sender, e) => _loginForm.Dispose();
        buttonSignOut.Click += (sender, e) => IsLoggedIn = false;
    }
    private LoginForm _loginForm = new LoginForm();
    protected override void SetVisibleCore(bool value) =>
        base.SetVisibleCore(value && IsLoggedIn);

    bool _isLoggedIn = false;
    public bool IsLoggedIn
    {
        get => _isLoggedIn;
        set
        {
            if (!Equals(_isLoggedIn, value))
            {
                _isLoggedIn = value;
                onIsLoggedInChanged();
            }
        }
    }

    private void onIsLoggedInChanged()
    {
        if (IsLoggedIn)
        {
            WindowState = FormWindowState.Maximized;
            Text = $"Welcome {_loginForm.UserName}";
            Visible = true;
        }
        else execLoginFlow();
    }

    private void execLoginFlow()
    {
        Visible = false;
        while (!IsLoggedIn)
        {
            _loginForm.StartPosition = FormStartPosition.CenterScreen;
            if (DialogResult.Cancel == _loginForm.ShowDialog(this))
            {
                switch (MessageBox.Show(
                    this,
                    "Invalid Credentials",
                    "Error",
                    buttons: MessageBoxButtons.RetryCancel))
                {
                    case DialogResult.Cancel: Application.Exit(); return;
                    case DialogResult.Retry: break;
                }
            }
            else
            {
                IsLoggedIn = true;
            }
        }
    }
}

Login form

public partial class LoginForm : Form
{
    public LoginForm()
    {
        InitializeComponent();
        StartPosition = FormStartPosition.Manual;
        FormBorderStyle = FormBorderStyle.FixedToolWindow;
        textBoxUid.Text = "Admin";
        textBoxUid.TextChanged += onOnyTextChanged;
        textBoxPswd.TextChanged += onOnyTextChanged;
        buttonLogin.Enabled = false;
        buttonLogin.Click += (sender, e) => DialogResult = DialogResult.OK;
    }
    private void onOnyTextChanged(object sender, EventArgs e)
    {
        buttonLogin.Enabled = !(
            (textBoxUid.Text.Length == 0) || 
            (textBoxPswd.Text.Length == 0)
        );
    }
    public string UserName => textBoxUid.Text;
    protected override void OnVisibleChanged(EventArgs e)
    {
        base.OnVisibleChanged(e);
        if (Visible)
        {
            textBoxPswd.Clear();
            textBoxPswd.PlaceholderText = "********";
        }
    }
}

Upvotes: 0

TheBeardedQuack
TheBeardedQuack

Reputation: 449

Edit: Reason for issue

Without digging too much in the weeds, the lifetime of the application is tied to your main window in WinForms. There are certain things that can extend that lifetime such as detached threads, but I won't go into that.

In your code here, the call to this.Close() effectively terminates the application because as far as the Windows event loop is concerned, once your primary window is gone you've finished with the app.

There is another issue though, even if you weren't calling this.Close() you're creating a Home object, which lives inside a local variable within the function. As soon as you reach the end of the if statement, your last reference to the object has gone. When the garbage collector invokes it won't be able to reach your Home object, which triggers it to be destroyed and freed. The object needs to be stored somewhere that will persist after your function call finishes.

Solution

I've worked with WinForms for a good while and as far as I'm aware there's no real way to handle "pages" like this. There are other frameworks such as WPF which do this much better. If I'm wrong about WinForms handling this badly perhaps someone will correct me.

One relatively simple approach (with WinForms) is to use an empty Form as the main page for your application, there's a property called IsMdiContainer which if you set to true, will allow this form to contain other forms.

In this main window form, I'll add a function to load a new page which accepts the page to load. It closes down the current page and opens the new one up. There's various different ways to do this depending on what information you need to pass between pages. The crude but easy way of passing info would be to only the most keep important information in the main window, then the children can access it but it does couple your classes together more.

You don't have to use Forms or the "Multiple Document Interface" feature, but I like to have the OnLoad and OnFormClosing functions available to me. If you don't need these you can just use a UserControl for your pages instead, and leave IsMdiParent set to its default of false.

Upvotes: 0

omer
omer

Reputation: 105

Try using hide instead close.

Hide();
Home home = new Home();
home.ShowDialog();
Close();

for more information you can visit microsoft offical site.

Upvotes: 2

Related Questions