Windows Forms and Peek-a-boo!

  • Thread starter Thread starter Ayende Rahien
  • Start date Start date
A

Ayende Rahien

Excetremely annoying problem, I've an application with a long startup time.
So I created another form with my logo in it to as a splash screen.
The splash screen is run from another thread and is communicated solely
through static method and Invoke()'s
However, when I close my second form, the first one (main window) is hiding
under all the windows on the desktop.
If I don't close the splash screen, then everything is fine.

I tried everything, but it's just not working.

//Form1.cs - the main window of my app

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using System.Drawing.Printing;

namespace PrintTest

{



/// <summary>

/// Summary description for Form1.

/// </summary>

public class Form1 : System.Windows.Forms.Form

{

/// <summary>

/// Required designer variable.

/// </summary>

private System.ComponentModel.Container components = null;

private System.Windows.Forms.Button button1;

public Form1()

{

//

// Required for Windows Form Designer support

//

InitializeComponent();


}

/// <summary>

/// Clean up any resources being used.

/// </summary>

protected override void Dispose( bool disposing )

{

if( disposing )

{

if (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()

{

this.button1 = new System.Windows.Forms.Button();

this.SuspendLayout();

//

// button1

//

this.button1.Location = new System.Drawing.Point(96, 56);

this.button1.Name = "button1";

this.button1.Size = new System.Drawing.Size(152, 104);

this.button1.TabIndex = 0;

this.button1.Text = "button1";

this.button1.Click += new System.EventHandler(this.button1_Click);

//

// Form1

//

this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

this.ClientSize = new System.Drawing.Size(292, 266);

this.Controls.AddRange(new System.Windows.Forms.Control[] {

this.button1});

this.Name = "Form1";

this.Text = "Form1";

this.ResumeLayout(false);

}

#endregion

/// <summary>

/// The main entry point for the application.

/// </summary>

[STAThread]

static void Main()

{

Form2.Dispaly();

System.Threading.Thread.Sleep(5000);

Form2.Vanish();

Application.Run(new Form1());

}



private void button1_Click(object sender, System.EventArgs e)

{


}


}

}

// Form2.cs - the splash screen

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;

namespace PrintTest
{
/// <summary>
/// Summary description for Form2.
/// </summary>
public class Form2 : System.Windows.Forms.Form
{
private System.ComponentModel.IContainer components;
private static Form2 That;
private static bool Open;
private System.Windows.Forms.Timer Timer1;
public Form2()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
Timer1.Enabled=true;

}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(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()
{
this.components = new System.ComponentModel.Container();
this.Timer1 = new System.Windows.Forms.Timer(this.components);
//
// Timer1
//
this.Timer1.Tick += new System.EventHandler(this.Timer1_Tick);
//
// Form2
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Name = "Form2";
this.Opacity = 0;
this.Text = "Form2";
this.TopMost = true;

}
#endregion

public static void Start()
{
That=new Form2();
//That.Show();//doesn't work
//That.ShowDialog();//works, but hide the main window when exiting
Application.Run(That);//works, but hide the main window when exiting
}

public static void Dispaly()
{
if(Open)
return;
Open=true;
Thread t = new Thread(new ThreadStart(new MethodInvoker(Start)));
t.Start();
}
public static void Vanish()
{
That.Invoke(new MethodInvoker(VanishInvoked));
}
private static void VanishInvoked()
{
Open = false;
That.Timer1.Enabled = true;
}

private void Timer1_Tick(object sender, System.EventArgs e)
{
if(Open)
{
this.Opacity+=0.1;
if(That.Opacity>=100)
Timer1.Enabled=false;
}
else
{
if(this.Opacity>25)
this.Opacity-=0.1;
else
{
this.Close();
That = null;//free resources
}
}
}
}
}
 
Hi,

You are messing with visual controls in non UI thread (method Start) which
is usually a very bad thing to do.
IOW that's your main problem.
Plus, I hope that you are aware that the line
System.Threading.Thread.Sleep(5000);
stops your thread for 5 seconds thus your app start time is 5 second longer.
 
Ayende,

Have you tried to call Dispose on the Form2 instance before you are done
with it?

Also, I think that you should have two application loops (i.e. call the
static Run method on the Application class). One to handle your splash
screen (which would then start the thread), and then when that is done, call
Run again, with your new form.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Ayende Rahien said:
Excetremely annoying problem, I've an application with a long startup time.
So I created another form with my logo in it to as a splash screen.
The splash screen is run from another thread and is communicated solely
through static method and Invoke()'s
However, when I close my second form, the first one (main window) is hiding
under all the windows on the desktop.
If I don't close the splash screen, then everything is fine.

I tried everything, but it's just not working.

//Form1.cs - the main window of my app

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using System.Drawing.Printing;

namespace PrintTest

{



/// <summary>

/// Summary description for Form1.

/// </summary>

public class Form1 : System.Windows.Forms.Form

{

/// <summary>

/// Required designer variable.

/// </summary>

private System.ComponentModel.Container components = null;

private System.Windows.Forms.Button button1;

public Form1()

{

//

// Required for Windows Form Designer support

//

InitializeComponent();


}

/// <summary>

/// Clean up any resources being used.

/// </summary>

protected override void Dispose( bool disposing )

{

if( disposing )

{

if (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()

{

this.button1 = new System.Windows.Forms.Button();

this.SuspendLayout();

//

// button1

//

this.button1.Location = new System.Drawing.Point(96, 56);

this.button1.Name = "button1";

this.button1.Size = new System.Drawing.Size(152, 104);

this.button1.TabIndex = 0;

this.button1.Text = "button1";

this.button1.Click += new System.EventHandler(this.button1_Click);

//

// Form1

//

this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

this.ClientSize = new System.Drawing.Size(292, 266);

this.Controls.AddRange(new System.Windows.Forms.Control[] {

this.button1});

this.Name = "Form1";

this.Text = "Form1";

this.ResumeLayout(false);

}

#endregion

/// <summary>

/// The main entry point for the application.

/// </summary>

[STAThread]

static void Main()

{

Form2.Dispaly();

System.Threading.Thread.Sleep(5000);

Form2.Vanish();

Application.Run(new Form1());

}



private void button1_Click(object sender, System.EventArgs e)

{


}


}

}

// Form2.cs - the splash screen

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;

namespace PrintTest
{
/// <summary>
/// Summary description for Form2.
/// </summary>
public class Form2 : System.Windows.Forms.Form
{
private System.ComponentModel.IContainer components;
private static Form2 That;
private static bool Open;
private System.Windows.Forms.Timer Timer1;
public Form2()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
Timer1.Enabled=true;

}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(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()
{
this.components = new System.ComponentModel.Container();
this.Timer1 = new System.Windows.Forms.Timer(this.components);
//
// Timer1
//
this.Timer1.Tick += new System.EventHandler(this.Timer1_Tick);
//
// Form2
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Name = "Form2";
this.Opacity = 0;
this.Text = "Form2";
this.TopMost = true;

}
#endregion

public static void Start()
{
That=new Form2();
//That.Show();//doesn't work
//That.ShowDialog();//works, but hide the main window when exiting
Application.Run(That);//works, but hide the main window when exiting
}

public static void Dispaly()
{
if(Open)
return;
Open=true;
Thread t = new Thread(new ThreadStart(new MethodInvoker(Start)));
t.Start();
}
public static void Vanish()
{
That.Invoke(new MethodInvoker(VanishInvoked));
}
private static void VanishInvoked()
{
Open = false;
That.Timer1.Enabled = true;
}

private void Timer1_Tick(object sender, System.EventArgs e)
{
if(Open)
{
this.Opacity+=0.1;
if(That.Opacity>=100)
Timer1.Enabled=false;
}
else
{
if(this.Opacity>25)
this.Opacity-=0.1;
else
{
this.Close();
That = null;//free resources
}
}
}
}
}
 
I had this same problem, but I was able to find a pretty decent workaround
using the API. I use the following:

The splash form is handed the window handle of the main form just as the
main form completes its startup work (a public property on the splash form
accepts this).

Then, the splash form uses:
GetWindowThreadProcessId
followed by "AttachThreadInput" to hook the window message handlers together
Then is calls "SetActiveWindow", and then uses AttachThreadInput to put the
window message handlers back the way they were.

This has been working fine, and ends up with the main form active.

Jerry
Ayende Rahien said:
Excetremely annoying problem, I've an application with a long startup time.
So I created another form with my logo in it to as a splash screen.
The splash screen is run from another thread and is communicated solely
through static method and Invoke()'s
However, when I close my second form, the first one (main window) is hiding
under all the windows on the desktop.
If I don't close the splash screen, then everything is fine.

I tried everything, but it's just not working.

//Form1.cs - the main window of my app

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using System.Drawing.Printing;

namespace PrintTest

{



/// <summary>

/// Summary description for Form1.

/// </summary>

public class Form1 : System.Windows.Forms.Form

{

/// <summary>

/// Required designer variable.

/// </summary>

private System.ComponentModel.Container components = null;

private System.Windows.Forms.Button button1;

public Form1()

{

//

// Required for Windows Form Designer support

//

InitializeComponent();


}

/// <summary>

/// Clean up any resources being used.

/// </summary>

protected override void Dispose( bool disposing )

{

if( disposing )

{

if (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()

{

this.button1 = new System.Windows.Forms.Button();

this.SuspendLayout();

//

// button1

//

this.button1.Location = new System.Drawing.Point(96, 56);

this.button1.Name = "button1";

this.button1.Size = new System.Drawing.Size(152, 104);

this.button1.TabIndex = 0;

this.button1.Text = "button1";

this.button1.Click += new System.EventHandler(this.button1_Click);

//

// Form1

//

this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

this.ClientSize = new System.Drawing.Size(292, 266);

this.Controls.AddRange(new System.Windows.Forms.Control[] {

this.button1});

this.Name = "Form1";

this.Text = "Form1";

this.ResumeLayout(false);

}

#endregion

/// <summary>

/// The main entry point for the application.

/// </summary>

[STAThread]

static void Main()

{

Form2.Dispaly();

System.Threading.Thread.Sleep(5000);

Form2.Vanish();

Application.Run(new Form1());

}



private void button1_Click(object sender, System.EventArgs e)

{


}


}

}

// Form2.cs - the splash screen

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;

namespace PrintTest
{
/// <summary>
/// Summary description for Form2.
/// </summary>
public class Form2 : System.Windows.Forms.Form
{
private System.ComponentModel.IContainer components;
private static Form2 That;
private static bool Open;
private System.Windows.Forms.Timer Timer1;
public Form2()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
Timer1.Enabled=true;

}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(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()
{
this.components = new System.ComponentModel.Container();
this.Timer1 = new System.Windows.Forms.Timer(this.components);
//
// Timer1
//
this.Timer1.Tick += new System.EventHandler(this.Timer1_Tick);
//
// Form2
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Name = "Form2";
this.Opacity = 0;
this.Text = "Form2";
this.TopMost = true;

}
#endregion

public static void Start()
{
That=new Form2();
//That.Show();//doesn't work
//That.ShowDialog();//works, but hide the main window when exiting
Application.Run(That);//works, but hide the main window when exiting
}

public static void Dispaly()
{
if(Open)
return;
Open=true;
Thread t = new Thread(new ThreadStart(new MethodInvoker(Start)));
t.Start();
}
public static void Vanish()
{
That.Invoke(new MethodInvoker(VanishInvoked));
}
private static void VanishInvoked()
{
Open = false;
That.Timer1.Enabled = true;
}

private void Timer1_Tick(object sender, System.EventArgs e)
{
if(Open)
{
this.Opacity+=0.1;
if(That.Opacity>=100)
Timer1.Enabled=false;
}
else
{
if(this.Opacity>25)
this.Opacity-=0.1;
else
{
this.Close();
That = null;//free resources
}
}
}
}
}
 
Miha Markic said:
Hi,

You are messing with visual controls in non UI thread (method Start) which
is usually a very bad thing to do.
IOW that's your main problem.

I fixed it so everything is Invoke()ed, but it doesn't help.
Plus, I hope that you are aware that the line
System.Threading.Thread.Sleep(5000);
stops your thread for 5 seconds thus your app start time is 5 second
longer.

Yes, that is a test app to show the problem, I want a reasonable time frame
to show the splash and make it go away.
 
Jerry Ham said:
I had this same problem, but I was able to find a pretty decent workaround
using the API. I use the following:

The splash form is handed the window handle of the main form just as the
main form completes its startup work (a public property on the splash form
accepts this).

Then, the splash form uses:
GetWindowThreadProcessId
followed by "AttachThreadInput" to hook the window message handlers together
Then is calls "SetActiveWindow", and then uses AttachThreadInput to put the
window message handlers back the way they were.

This has been working fine, and ends up with the main form active.

Thanks a lot!
It now works!!!
Actually, you got me thinking, and then I did a Form1.Activate() after
closing Form2, and it show the main form!
 
Back
Top