如何在不关闭应用程序的情况下关闭登录窗口并显示主窗口?

44

我的项目中有两个表格(登录和主要表格)。

我想要实现的是,如果登录成功,我必须显示主表格并关闭登录表格。

我在登录表格中有一个方法,在登录成功时关闭登录表格。但是主表格没有显示出来。

public void ShowMain()
{
    if(auth()) // a method that returns true when the user exists.
    {             
        var main = new Main();
        main.Show();
        this.Close();
    }
    else
    {
        MessageBox.Show("Invalid login details.");
    }         
}

我尝试在登录过程成功后隐藏登录表单。但这让我感到困扰,因为我知道在程序运行时登录表单仍然存在,它应该被关闭,对吧?

对此应该采取什么正确的方法呢? 谢谢...


尝试在最后调用this.Close(),而不是作为第一个操作。 - Roberto Conte Rosito
1
相同的话,整个应用程序都会关闭。 - yonan2236
你可以尝试使用main.ShowDialog()而不是main.Show()。这样应该可以正常工作,因为ShowDialog是模态窗体。 - Abu Muhammad
15个回答

113
你的主窗体没有显示的原因是,一旦你关闭登录窗体,应用程序的消息泵就会关闭,这会导致整个应用程序退出。Windows消息循环与登录窗体相关联,因为在项目属性中将其设置为启动窗体。查看你的"Program.cs"文件,你会看到负责代码的部分:Application.Run(new LoginForm())。在MSDN上查看该方法的文档,可以更详细地解释这一点。
最好的解决方案是将代码从登录窗体移到"Program.cs"文件中。当程序第一次启动时,你将创建并显示登录窗体作为模态对话框(在单独的消息循环上运行并阻止执行余下的代码,直到它关闭)。当登录对话框关闭时,你将检查其DialogResult属性以查看登录是否成功。如果成功,你可以使用Application.Run开始主窗体(从而创建主消息循环);否则,你可以退出应用程序而不显示任何窗体。类似这样:
static void Main()
{
    LoginForm fLogin = new LoginForm();
    if (fLogin.ShowDialog() == DialogResult.OK)
    {
        Application.Run(new MainForm());
    }
    else
    {
        Application.Exit();
    }
}

你能否添加登录表单中应该填写的内容? - r.hamd
1
在LoginForm中调用this.Close()后,@r.hamd刚刚设置了DialogResult = DialogResult.OK。 - Faruk
在2021年,这仍然是可用选项中最干净的。在许多其他响应中启动登录对话框的建议是一种反模式,并且仅适用于允许未经身份验证的用户交互的应用程序。 - Chris Schaller

11

我会采取另一种方式。

在主表单的OnLoad事件中,将登录窗体显示为对话框。如果对话框结果是OK,则允许主要继续加载;如果结果为身份验证失败,则中止加载并显示消息框。

EDIT 代码示例:

private void MainForm_Load(object sender, EventArgs e)
{
    this.Hide();

    LogonForm logon = new LogonForm();

    if (logon.ShowDialog() != DialogResult.OK)
    {
        //Handle authentication failures as necessary, for example:
        Application.Exit();
    }
    else
    {
        this.Show();
    }
}

另一个解决方案是在 program.cs 的 Main 方法中显示 LogonForm,例如:

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    LogonForm logon = new LogonForm();

    Application.Run(logon);

    if (logon.LogonSuccessful)
    {
        Application.Run(new MainForm());
    }
}

在这个例子中,您的LogonForm必须公开一个LogonSuccessful布尔属性,当用户输入有效凭据时将其设置为true。


1
最开始隐藏主窗体,登录成功后再显示它,这样可以吗? - yonan2236
但是这样做的话,如果主窗体的加载中有一些逻辑,它仍然会被执行...而且你正在改变其他窗体的显示工作流程...听起来有点混乱和面条式编程。 - Davide Piras
5
登录成功前为什么要加载主表单? - Cody Gray
  1. 当我在表单加载时显示登录表单时,它仍然会在后台显示主表单。当我添加代码“this.hide();”时,现在它可以正常工作并且看起来更专业。
  2. 但是当我查看其他评论时,其他人评论说在用户成功登录之前不应该执行主表单进程,所以我使用了您的第二个示例代码,它也可以正常工作。谢谢。
- Anukul Limpanasil

7

这是我的解决方案。创建ApplicationContext来设置应用程序的主窗体,当您想要打开新窗体并关闭当前窗体时,更改主窗体。

Program.cs

static class Program
{
    static ApplicationContext MainContext = new ApplicationContext();

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        MainContext.MainForm = new Authenticate();
        Application.Run(MainContext);
    }

    public static void SetMainForm(Form MainForm)
    {
        MainContext.MainForm = MainForm;
    }

    public static void ShowMainForm()
    {
        MainContext.MainForm.Show();
    }
}

当登录过程完成时。

private void BtLogin_Click(object sender, EventArgs e)
    {
        //Login Process Here.

        Program.SetMainForm(new Portal());
        Program.ShowMainForm();

        this.Close();
    }

我希望这能对你有所帮助。

5

这很简单。

下面是代码。

 private void button1_Click(object sender, EventArgs e)
 {  
        //creating instance of main form
        MainForm mainForm = new MainForm();

        // creating event handler to catch the main form closed event
        // this will fire when mainForm closed
        mainForm.FormClosed += new FormClosedEventHandler(mainForm_FormClosed);
        //showing the main form
        mainForm.Show();
        //hiding the current form
        this.Hide();
  }

  // this is the method block executes when main form is closed
  void mainForm_FormClosed(object sender, FormClosedEventArgs e)
  {
       // here you can do anything

       // we will close the application
       Application.Exit();
  }

3

这是最优雅的解决方案。

private void buttonLogin_Click(object sender, EventArgs e)
{
    MainForm mainForm = new MainForm();
    this.Hide();
    mainForm.ShowDialog();
    this.Close();
}

;-)


2
static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Login();
    } 

    private static bool logOut;

    private static void Login()
    {
        LoginForm login = new LoginForm();
        MainForm main = new MainForm();
        main.FormClosed += new FormClosedEventHandler(main_FormClosed);
        if (login.ShowDialog(main) == DialogResult.OK)
        {
            Application.Run(main);
            if (logOut)
                Login();
        }
        else
            Application.Exit();
    }

    static void main_FormClosed(object sender, FormClosedEventArgs e)
    {
        logOut= (sender as MainForm).logOut;
    }
}

public partial class MainForm : Form
{
    private void btnLogout_ItemClick(object sender, ItemClickEventArgs e)
    {
        //timer1.Stop();
        this.logOut= true;
        this.Close();
    }
}

欢迎来到 Stack Overflow。请在您的回答中添加一些评论。 - Tony Rad

2

这里有一个简单的解决方案,你的问题是当你的登录窗体关闭时整个应用程序都会关闭,对吗?如果是这样的话,那么请进入项目属性,在应用程序选项卡中将关闭模式更改为“当最后一个窗体关闭时”,这样你就可以使用Me.close而不会关闭整个程序。


4
这仅适用于 VB.Net 项目。 - Sacha K

0

在成功登录之前,展示登录并关闭登录窗体的最佳方式是将登录窗体放置在FrmMain中的InitializeComponent后。

 public FrmMain()
 {
      FrmSplash FrmSplash = new FrmSplash();
      FrmSplash.ShowDialog();

      InitializeComponent();
      //Login Section
 {

0

试试这个:

public void ShowMain()
    {
        if(auth()) // a method that returns true when the user exists.
        { 
            this.Close();
            System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(Main));
            t.Start();
        }
        else
        {
            MessageBox.Show("Invalid login details.");
        }         
    }
   [STAThread]
   public void Main()
   {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new Main());

   }

如果我没记错的话,由于 Windows API 和 COM 接口的调用系统,您必须在不同的线程公寓中调用新表单。

一个建议:这个系统很容易被攻破,因为您可以更改 MSIL 中的 if 条件,所以需要更强大的系统来保护您的软件,比如混淆或远程登录之类的东西。

希望这可以帮助到您。


1
如何把简单的事情变得困难系列:DD - Davide Piras
不,WinForms并非设计用于多线程单元模型的工作。我不太确定你在提倡什么,或者这段代码是如何工作的。ShowMain方法在哪里被调用?为什么你需要创建一个新线程呢? - Cody Gray
我从未说过Windows在多线程模型下运行,我在哪里说了这句话?因为获取Windows表单的限制,在COM技术下,API需要获取单线程公寓来执行不同的程序。如果您关闭登录线程,所有的表单都将关闭。这会使主表单处于活动状态。ShowMain由Application.Run()调用......在Windows Form应用程序启动时,您会调用一个ShowForm吗?拜托了... - Manuel

0

你应该反过来做:

首先加载mainform,在其onload事件中使用showdialog()显示你的loginform,这将防止mainform在你从loginform得到结果之前显示。

编辑: 由于这是一个登录表单,如果你不需要从你的mainform中获取任何变量(这在实践中是不好的设计),你应该像Davide和Cody建议的那样在你的program.cs中实现它。


我真的想象不出有哪种情况下一个表单需要从另一个表单获取变量 - Cody Gray
@Cody:所以,这使得你的答案是不可避免的。有时候,你需要基于原始代码/思路为提问者提供一些东西,让他们更好地理解问题所在。 - Bolu
@Bolu:重点不是“我是对的,你是错的”,而是在面向对象语言中,一个表单访问另一个表单的变量始终是不良设计。我认为让OP也理解这一点很重要。 - Cody Gray
@Cody,没错。就像OP问如何吃苹果,因为他不能用笔切苹果,我的回答是:“用刀”,而你的回答更有道理:“你不需要切它,直接吃掉它”... - Bolu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接