最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Switching forms with WinForms - briefly seeing previous form before next form appears - Stack Overflow

programmeradmin3浏览0评论

I am working on a C# WinForms application on .NET 8.0 and facing an issue with form transitions.

I have three forms:

  • Form1 - Initial screen (main form)
  • Form2 - Password screen (modal dialog)
  • Form3 - Final destination form

Expected flow:

  • The user clicks a button on Form1, which opens Form2 for password entry
  • After successful authentication, Form3 should be opened
  • Form1 should not appear again after Form2 closes

Actual behavior:

  • Form1 opens Form2 (password screen) successfully
  • User enters the password and submits
  • Before Form3 appears, Form1 briefly flickers and appears for a second
  • After that, Form3 loads correctly
  • This brief flicker of Form1 is unexpected and causes a bad user experience

Here is how I'm handling form transitions - code in Form1 (opens Form2 for authentication):

private void btnOpenForm2_Click(object sender, EventArgs e)
{
    Form2 passwordForm = new Form2();

    if (passwordForm.ShowDialog() == DialogResult.OK) // Blocking call
    {
        Form3 finalForm = new Form3();
        finalForm.Show();
        this.Hide(); // Hide Form1
    }
}

Code in Form2 (password screen):

private void btnSubmit_Click(object sender, EventArgs e)
{
    // Assume authentication is successful
    this.DialogResult = DialogResult.OK;this.Close();
}

What I've tried

  1. Hiding Form1 before opening Form2
  2. Used this.Hide(); before ShowDialog().
    Still, Form1 briefly appears before Form3.
    Setting TopMost = true for Form3 - no improvement.
  3. Using BeginInvoke() to delay Form3 loading
  4. Explicitly setting Form2's Owner

How do I prevent Form1 from appearing again briefly between Form2 closing and Form3 opening? Is there a way to transition smoothly from Form2 → Form3 without Form1 appearing again?

I am working on a C# WinForms application on .NET 8.0 and facing an issue with form transitions.

I have three forms:

  • Form1 - Initial screen (main form)
  • Form2 - Password screen (modal dialog)
  • Form3 - Final destination form

Expected flow:

  • The user clicks a button on Form1, which opens Form2 for password entry
  • After successful authentication, Form3 should be opened
  • Form1 should not appear again after Form2 closes

Actual behavior:

  • Form1 opens Form2 (password screen) successfully
  • User enters the password and submits
  • Before Form3 appears, Form1 briefly flickers and appears for a second
  • After that, Form3 loads correctly
  • This brief flicker of Form1 is unexpected and causes a bad user experience

Here is how I'm handling form transitions - code in Form1 (opens Form2 for authentication):

private void btnOpenForm2_Click(object sender, EventArgs e)
{
    Form2 passwordForm = new Form2();

    if (passwordForm.ShowDialog() == DialogResult.OK) // Blocking call
    {
        Form3 finalForm = new Form3();
        finalForm.Show();
        this.Hide(); // Hide Form1
    }
}

Code in Form2 (password screen):

private void btnSubmit_Click(object sender, EventArgs e)
{
    // Assume authentication is successful
    this.DialogResult = DialogResult.OK;this.Close();
}

What I've tried

  1. Hiding Form1 before opening Form2
  2. Used this.Hide(); before ShowDialog().
    Still, Form1 briefly appears before Form3.
    Setting TopMost = true for Form3 - no improvement.
  3. Using BeginInvoke() to delay Form3 loading
  4. Explicitly setting Form2's Owner

How do I prevent Form1 from appearing again briefly between Form2 closing and Form3 opening? Is there a way to transition smoothly from Form2 → Form3 without Form1 appearing again?

Share Improve this question edited Feb 17 at 5:00 marc_s 755k184 gold badges1.4k silver badges1.5k bronze badges asked Feb 17 at 3:11 Shakthi KumarShakthi Kumar 212 bronze badges New contributor Shakthi Kumar is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 4
  • 1 "The user clicks a button on Form1, which opens Form2" Ok, but why? What else do you need a user to perform without authenticating? IMHO delete form1 completely. – Jeremy Lakeman Commented Feb 17 at 3:40
  • If Form1 isn't to be used again then why are you hiding it rather than closing it? I'm guessing because it's the startup form but that's not a good reason. You should edit the Main method so that Form3 is displayed there, so that the app will not close. Set a property in Form1 and check that in the Main method when it closes. Based on that, either let the app exit or open Form3. – jmcilhinney Commented Feb 17 at 5:00
  • I dislike idea of opening either form from another. I'd do everything in Main method, then you can open all forms modally one after another, checking results and anizing "loop" to show either form. If you need Application.Run, then just put this logic into ApplicationContext constructor. – Sinatr Commented Feb 17 at 9:54
  • @JeremyLakeman I upvoted ▲ your comment because I had the same initial thought. But then I came up with at least one possible why: Suppose we can login or continue in "demo mode" with reduced features for example? I still agree that if logging in is one's only choice then the first screen is unnecessary. – IV. Commented Feb 18 at 19:43
Add a comment  | 

2 Answers 2

Reset to default 0
  • Make Form3 the main form

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form3());
        }
    }
    
  • When the main form launches open the startup form

    public partial class Form3 : Form
    {
        public Form3()
        {
            InitializeComponent();
        }
    
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
    
            //Open startup form and wait
            var entry = new Form1();
            if (entry.ShowDialog() != DialogResult.OK)
            {
                // if password failed close application
                Close();
            }
        }
    }
    
  • Make the startup form open the login form when a button is pressed and pass the DialogResult value through to the main form

    partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    
        private void okButton_Click(object sender, EventArgs e)
        {
            //Open password form and passthrough results to main form
            var dlg = new Form2();
            this.DialogResult = dlg.ShowDialog();
            this.Close();
        }
    }
    
  • In the password form set the DialogResult value based on the authentication

The in the end run the program and the initial form will show. If the button is pressed the login form will show. When ok button is pressed in the login form, the main form shows. If the cancel button is pressed in the login form then the entire application will end.

Note that WinForms run on a single thread started with Application.Run(). When this form end, the entire application end. This is the reason you need Form3 as the main form.

Your post describes an onboarding flow that takes the user through two preliminary screens before finally showing the main form. The problem is that you're seeing a flicker - briefly showing an unwanted form. And since you have a password protecting the main form contents, it's a fair and reasonable assumption that sensitive information could be revealed if the main form blinks on (even for a instant) before being authorized to do so.

The key to suppressing this is to override SetVisibleCore and prevent it from setting the base class visibility to true until your condition has been met.

protected override void SetVisibleCore(bool value)
{
    base.SetVisibleCore(value && OnboardingState == OnboardingState.Authorized);
}

Now you can run the MainForm of the app in the normal way, and it will no longer be even briefly visible while you do your onboarding sequence.

enum OnboardingState{Screen1, Screen2, Authorized, }

public partial class MainWindow : Form
{
    public MainWindow()
    {
        InitializeComponent();

        // IMPORTANT: Make sure that Handle is not null.
        _ = Handle;
        BeginInvoke(() => OnboardingState = OnboardingState.Screen1);
    }
    protected override void SetVisibleCore(bool value)
    {
        base.SetVisibleCore(value && OnboardingState == OnboardingState.Authorized);
    }

    // Respond to changes of the onboarding state
    // enum by showing the corresponding window.
    OnboardingState OnboardingState
    {
        get => _onboardingState;
        set
        {
            if (!Equals(_onboardingState, value))
            {
                _onboardingState = value;
                switch (_onboardingState)
                {
                    case OnboardingState.Screen1:
                        using (var dlg = new OnboardingForm1())
                        {
                            if (DialogResult.OK == dlg.ShowDialog(this))
                            {
                                OnboardingState = OnboardingState.Screen2;
                            }
                            else Close();
                        }
                        break;
                    case OnboardingState.Screen2:
                        using (var dlg = new OnboardingForm2())
                        {
                            if (DialogResult.OK == dlg.ShowDialog(this))
                            {
                                OnboardingState = OnboardingState.Authorized;
                            }
                            else Close();
                        }
                        break;
                    case OnboardingState.Authorized:
                        Show();
                        break;
                }
            }
        }
    }
    OnboardingState _onboardingState = (OnboardingState)(-1);
}

First Screen

public partial class OnboardingForm1 : Form
{
    public OnboardingForm1()
    {
        InitializeComponent();
        buttonLogin.Click += (sender, e) => DialogResult = DialogResult.OK;
    }
}

Second Screen

public partial class OnboardingForm2 : Form
{
    public OnboardingForm2()
    {
        InitializeComponent();
        Shown += (sender, e) => ActiveControl = null;
        textBoxUid.TextChanged += localUpdateVisibility;
        textBoxPswd.TextChanged += localUpdateVisibility;
        void localUpdateVisibility(object? sender, EventArgs e)
        {
            textBoxPswd.Visible = !string.IsNullOrWhiteSpace(textBoxUid.Text);
            buttonLogin.Visible = textBoxUid.Visible && !string.IsNullOrWhiteSpace(textBoxPswd.Text);
        }
        buttonLogin.Click += (sender, e) => DialogResult = DialogResult.OK;
    }
}
发布评论

评论列表(0)

  1. 暂无评论