Click here to Skip to main content
Click here to Skip to main content

Fading Forms In and Out

By , 13 Aug 2007
 
Screenshot - FadeForm.jpg

Introduction

Disclaimer: I am not the first to make a form fader, but I couldn't find one that did exactly what I wanted and so I created this.

I have had need in the past to cause my forms to perform fade transitions from one opacity to another. It was usually on load, close and window state changes. I finally decided to bring it all together in a nice, extendable Form. The FadeForm...

  • fades in on open.
  • fades out on close.
  • partially fades out on focus lost.
  • fades in on focus.
  • fades out on minimize.
  • fades in on restore.
  • must not annoy the user. :-) A form fader definitely has that potential.

Using the Code

To use FadeForm, just extend it instead of Form and you are ready to go.

public class Form1 : FadeForm
{
     ...
}

It is defaulted to use the fade-in from nothing on open and out to nothing on close. It will also fade to 85% opacity when not the active window. You can set it to whatever you want, however.

//This would set the form with its default values.
this.ActiveOpacity=1;
this.InactiveOpacity=.85;
this.MinimizedOpacity=0;

You may, from time to time, want to disable the fade effect.

this.DisableFade(); //Turn off fade effects
this.EnableFadeDefaults(); //Turns on fade effects

You can also change the transition time.

this.FadeTime=1; //1 sec transition

You can also do a one-time fade to any value.

this.TargetOpacity=.25; //Fades the form to 25% opacity

Points of Interest

The opening and focus change events were easy to deal with. It was appropriate to use the built-in event listeners.

public FadeForm()
{
    ...
    this.timer.Tick += new System.EventHandler(this.timer_Tick);
    
    this.Deactivate += new System.EventHandler(this.FadeForm_Deactivate);
    this.Activated += new System.EventHandler(this.FadeForm_Activated);
    this.Load += new System.EventHandler(this.FadeForm_Load);
}

private void FadeForm_Load(object sender, EventArgs e)
{
   this.Opacity = minimizedOpacity;
   this.TargetOpacity = activeOpacity;
}

private void FadeForm_Deactivate(object sender, EventArgs e)
{
    this.TargetOpacity = inactiveOpacity;
}

private void FadeForm_Activated(object sender, EventArgs e)
{
    this.TargetOpacity = activeOpacity;
}

The minimize and close events where a little trickier because the actions had to be postponed until the fade transition was complete. I had to override WndProc in order to catch the request for those actions and postpone the action until the transition was done.

private const int WM_SYSCOMMAND = 0x112;
private const int WM_COMMAND = 0x111;
private const int SC_MINIMIZE = 0xF020;
private const int SC_RESTORE = 0xF120;
private const int SC_CLOSE = 0xF060; 

// Intercepts WindowMessages before they are processed.
protected override void WndProc(ref Message m)
{
    if (m.Msg==WM_SYSCOMMAND||m.Msg == WM_COMMAND) 
    {
        //Fade to zero on minimze
        if (m.WParam == (IntPtr)SC_MINIMIZE) 
        { 
            heldMessage = m;
            this.TargetOpacity = minimizedOpacity;
            return;
         }

         //Fade in if the window is restored from the taskbar
         else if (m.WParam == (IntPtr)SC_RESTORE 
           && this.WindowState == FormWindowState.Minimized) 
         { 
             base.WndProc(ref m);  
             this.TargetOpacity = activeOpacity;
             return;
         }

         //Fade out if the window is closed.
         else if (m.WParam == (IntPtr)SC_CLOSE) 
         { 
             heldMessage = m; 
             this.TargetOpacity = minimizedOpacity;
             return;
         }
     }
     base.WndProc(ref m);
}

Once that was done, all I had to do was perform the transitions.

//Performs fade increment.
private void timer_Tick(object sender, EventArgs e)
{
    double fadeChangePerTick = timer.Interval * 1.0 / 1000 / fadeTime;

    //Check to see if it is time to stop the timer
    if (Math.Abs(targetOpacity - this.Opacity) < fadeChangePerTick)
    {
        //There is an ugly black flash if you set the Opacity to 1.0
        if (targetOpacity == 1) this.Opacity = .999;
        else this.Opacity = targetOpacity;
        
        //Process held Windows Message.
        base.WndProc(ref heldMessage);
        heldMessage = new Message();
        
        //Stop the timer to save processor.
        timer.Stop();
    }
    else if (targetOpacity > this.Opacity) this.Opacity += fadeChangePerTick;
    else if (targetOpacity < this.Opacity) this.Opacity -= fadeChangePerTick;
}

It is interesting to notice that the opacity is never actually 1. That was a hack on my part to keep the window from flashing black. I am not sure what the cause of this is, but the fix was easy, so I may never know.

You can see that I stop the timer after I reach my target opacity. You may wonder how it gets started. Every time I set TargetOpacity, the set method starts the timer. There's no sense running the timer and wasting processor power when you don't need it.

private double TargetOpacity
{
    set
    {
        targetOpacity = value;
         if (!timer.Enabled) timer.Start();
    }
    get 
    { 
        return targetOpacity; 
    }
}

I think I made it about as easy as possible. I always like to adhere to the KISS principle: Keep it simple, stupid.

Thoughts

I plan to add a Notify(int n) method that will oscillate the opacity n times in order to get the user's attention. I also think it would be nice to give a choice between mouseover-based and focus-based transitions.

History

  • 13 August, 2007 -- Original version posted

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Nicholas Seward
Instructor / Trainer
United States United States
Member
I am a mechanical engineer that works as a high school math teacher in Mulberry, Arkansas. I use programming as one way to keep my mind sharp.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalblack flashmemberroundy7215 Mar '09 - 9:23 
i was plagued with the issue of the black flash in my own version of fading forms in/out, then found your fix - rock on!!!
GeneralRe: black flashmemberNicholas Seward15 Mar '09 - 15:49 
I still don't know why the black flash happens. I suspect that windows uses a lightweight rendering engine when dealing with opaque forms and then transitions to the heavy weight one when rendering transparent forms. If that was in fact true it would make it very problematic for windows to double buffer the display while the new engine was starting up. If anybody can point me to a resource that will provide a definite answer for why a black flash occurs, I am still very curious. The .999 hack, while simple, has got to increase the cpu usage. (I assume transparent forms are harder to display.)
 
Nicholas Seward

GeneralRe: black flashmemberAtanas Palavrov1 Apr '09 - 16:37 
I found that flash happen because Form.Opacity set method changes form style, and Windows kernel erases background then repaints window. I found this with remote debugginng in virtual machine with breakpoints in overriden WndProc, OnPaint, OnPaintBackground - in call stack you can see what realy happen. Avoiding to set opacity to 1 solves this problem but resizing and overlaping window with other windows still repaints with flicks. This kernel behavior is completely confising for me, could anyone explain it?
GeneralRe: black flashmemberAtanas Palavrov12 May '09 - 9:14 
I dig a better workaround, just put this in your Form class:
 
	public new double Opacity
	{
		get { return base.Opacity; }
		set
		{
			if(Opacity<1&&value==1)
			{
				TransparencyKey = Color.Transparent;	
			}
 
			if (Opacity == 1 && value < 1)
			{
				base.Opacity = value;
				TransparencyKey = Color.Empty;
			}
			else
			{
					base.Opacity = value;
			}
		}
	}

GeneralRe: black flash [modified]memberNicholas Seward12 May '09 - 10:13 
I tried your prop on the demo and TransparencyKey caused some unintended side effects. When the form was suppose to be 100% opaque I can see through the input boxes.
 
Also, it is possible to get in an infinite loop if someone was incrementally trying to change the opacity until it reached 1. Your code will never gets the opacity to 1. (Edit: Sorry I wasn't reading your code right.) This did inspire a slicker hack. The prop below keeps the user from having to know about the opacity hack. As far as they know the opacity actually gets to 1.
 
public new double Opacity
{
  get 
  {
    if (base.Opacity >= .999) return 1;
    else return base.Opacity; 
  }
  set
  {
    if (value == 1)
    {
      base.Opacity=.999;
    }
    else
    {
      base.Opacity = value;
    }
  }
}

 
Nicholas Seward
modified on Thursday, May 14, 2009 10:58 AM

GeneralRe: black flashmemberAtanas Palavrov14 May '09 - 3:54 
Hi Nicholas,
 
You are awfully right about TextBox-es, i dig little bit more and found following:
 
When for TransparencyKey is set to Color.Transparent (which ARGB representation is (0, 255, 255, 255)) all White colors on form are transparent - yes this is extremely ugly.
 
My next try was for Color.FromArgb(0,0,0,0) - result was that all Black colors are transparent ... not acceptable too.
 
And they are transparent not only for eyes but for mouse too.
 
Final experiment was Color.FromArgb(0,1,1,1) and result was acceptabale - smooth fade in/out without nasty side effects.
 
It seems that Layered window logic skips the Alpha channel in form colors when looking for TransparencyKey, you can set Alpha blending on entire form bu not pixel per pixel.
 
Anyway, from my point of view this is not a perfect solution, it is not better or worst than setting Opacity to .999
 
The real solution is to override all methods that sets Layered form state and to add bool Layered property that overrides default logic based on Opacity and TransparencyKey values.
 
Regards,
Atanas
GeneralRe: black flashmemberNicholas Seward14 May '09 - 5:49 
I agree. The Color.FromArgb(0,1,1,1) and Opacity=.999 are both not great solutions.
 
However, someone in the comments below had slow rendering times due to the .999 hack. I bet your hack will fix the problem. Of course you can't ever set any form element to the color (1,1,1) but who would do that anyways. But if someone did they would be hard pressed to know what is wrong so the search for the better solution continues.
 
Nicholas Seward

GeneralSystem ERROR Occured in Applicationmembercoolpul6 Sep '08 - 2:50 
The application was good but a System error occurs in the application when the transition speed is reduced to 0.00 through arrows. There should be a limit as it is kept while increasing.
 
Regards,
Pulkit Lall.
modified on Saturday, September 6, 2008 8:57 AM

General.999 opacity and layout execution timemembernukefusion26 Nov '07 - 4:10 
Hi there,
 
Thanks for the code, very useful. I did notice one problem with the form opacity being set to .999 in code. One of my forms with about 3 nested split containers would perform its layout extremely slowly on initial display and on resizing of the form.
After checking my suspendlayout() and resumelayout() calls I tracked it down to this line of code setting the opacity. Removing it solved my problem and layout performed quickly again. I don't know why this is but just thought I would add a comment on this in case anyone notices a similar problem.
GeneralGeneral Commentsmemberchris17522 Aug '07 - 2:25 
I really like this code. I think it is pretty cool.
 
Comments...
1. I think you should make DisableFade a boolean property which doesn't change the other properties.
2. On all your properties you might want to include the category attribute, description attribute and refreshproperties attribute in the System.ComponentModel namespace. This way when the user is using the designer of a form derived from your class the descriptions will show in the properties grid.
3. Instead of using private void FadeForm_Load(object sender, EventArgs e) try using protected override void OnLoad(EventArgs e) { base.OnLoad(e); }
4. Look at using OnActivated and OnDeactivate.
5. FadeForm doesn't have a designer file so you can remove the "partial" from the class declaration.
6. You can remove the "using System.Drawing" namespace.
7. Running code anaysis on this file throws off some warnings but not too many.
 
Chris

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 13 Aug 2007
Article Copyright 2007 by Nicholas Seward
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid