2

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.

Santosh
  • 2,355
  • 10
  • 41
  • 64
  • 3
    "_my first windows form is Login_". Does that mean that it is actually mean that the login form is the actual main window of your app? It sounds quite like it, because if you close the main window of the app, the app normally exits (unless there is other program logic implemented to prevent this). So, and if your login form is being set to be your app's main window, then you get exactly the behavior you observed. In this case, see here: [Close form without exit application](https://stackoverflow.com/questions/3442921/close-form-without-exit-application) –  Dec 08 '22 at 19:59
  • To complement the last comment, you should have some "higher" class that shows the login form, and then instantiate the home if login is successful (can read a public property set when the login is checked), i.e., shift the responsibility to open the home form out of the login form. Could also be the home form itself, by the way, that starts as hidden, and will execute this.Show when login is ok. – Pac0 Dec 08 '22 at 20:01
  • Yeah, i agree with @Pac0. Make the actual main form (Home?) the main window of your app. Let it open first, and as one of the first steps after loading/initializing the main window open the login form as a modal dialog. –  Dec 08 '22 at 20:05
  • See [Multiple users in Windows Forms](https://stackoverflow.com/a/8898718/719186) – LarsTech Dec 08 '22 at 20:28
  • 1
    https://stackoverflow.com/a/10769349/17034 – Hans Passant Dec 08 '22 at 20:57
  • You really need to edit the question to reveal the contents of `Program.cs`. Like other comments indicated, if you set `Login` as main form, then `.Close` will shut down the entire application. So instead you should pop up `LoginForm` just like a normal form, but set `Home` as main form. – Lex Li Dec 09 '22 at 06:52

3 Answers3

2

Try using hide instead close.

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

for more information you can visit microsoft offical site.

omer
  • 105
  • 1
  • 7
  • 1
    This worked. But looks like a temporary solution. If I close the Home windows form, the application is not exiting. – Santosh Dec 08 '22 at 20:12
  • Actually there is many ways to do it. But I am updating my answer with the easiest one. Let me know if it helped. – omer Dec 08 '22 at 20:23
0

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.

TheBeardedQuack
  • 449
  • 4
  • 15
0

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.

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 = "********";
        }
    }
}

login flow

IVSoftware
  • 5,732
  • 2
  • 12
  • 23