Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Not Just Another Form Fader

0.00/5 (No votes)
7 Apr 2006 2  
A form fader with support for synchronous and asynchronous fading as well as FadeOnLoad, FadeOnClose, and FadeOnHover options.

Introduction

The goal of this project was not to develop just another form fader, but instead to incorporate the features of what makes others unique and perhaps to improve upon those. The major limitation of most other form faders is the lack of customization. Many faders have the code necessary to include features such as fading on demand, but only offer fading during loading and closing.

It was decided that the features which should be targeted for this project include:

  1. Synchronous and asynchronous fading.
  2. Full opacity on hover.
  3. Ability to interrupt a fade with another fade.

Using the code

Using this form fader is no different than most. Functionality of the form fader is contained in the FormFader.FormFader class. To use the fader, simply add a reference to the FormFader library and inherit from FormFader.FormFader.

A FormFader has several properties associated to it:

  • CurrentFade - Gets the status of the form: FadeIn, FadeOut, or NotFading.
  • FadeAmount - Gets/sets the incremental value between each iteration of the fade.
  • FadeRate - Gets/sets (in milliseconds) the time between each iteration of the fade.
  • FadeOnLoad - Gets/sets a value to determine if a synchronous fade from 0 opacity to FadeOapcity should occur when loading.
  • FodeOnClose - Gets/sets a value to determine if a synchronous fade from the current Opacity to 0 should occur when closing.
  • FadeOpacity - Gets/sets the value the form should be faded to.
  • FullOpacityOnHover - Gets/sets a value to determine if having mouse over the form should force an asynchronous fade to 100%.

Synchronous and asynchronous fading

Fading the form is simple. First, it requires the setting of the FadeOpacity property. Next, a simple method call will then determine if the form is faded synchronously or asynchronously.

Asynchronous fading would look like this:

this.FadeOpacity = .75;
this.Fade();

Synchronous fading would look like this:

this.FadeOpacity = .75;
this.FadeAndWait();

Full opacity on hover

Setting this value to true will force an asynchronous fade to 100% when the mouse is located within the bounds of the form (including the non-client area). When the mouse is moved outside the bounds, the form fades back to the current value of FadeOpacity.

Ability to interrupt a fade with another fade

If a fade is started and is currently processing (CurrentFade != NotFading) and another Fade() call is made, the first fade is stopped and the second fade takes priority. One reason this is important is to allow fast response when FullOpacityOnHover is set to true. Without the ability to interrupt a current fade, the stack of several calls to Fade() would cause severe performance issues and potential overflow leading to random crashes.

How it works

The majority of the code in charge of fading the form is handled in the Tick event of a single Timer. Using a Timer instead of a loop (like many other form faders) was beneficial for a few reasons:

  1. Timers run a different thread.
  2. The speed or FadeRate of the fade could be controlled.
  3. A Timer can be stopped.

Most of the code for the fader can be best understood by exploring the full source. However, one area I wish to elaborate on is the FullOpacityOnHover property. More specifically, the ability to detect when the mouse is over the form.

Although WinForms provides MouseEnter and MouseLeave events, it's limited only to the control specified. Moving the mouse over a child control of a form would fire a MouseLave event for the form and a MouseEnter event for the child control. To determine when the mouse was inside any portion of the form, a message filter MouseBounds was developed.

For every FormFader, a new instance of MouseBounds filter is added. Once added, the custom event MouseBounds.MouseBoundsChanged is fired every time the mouse either leaves or enters the form. The bool property MouseBounds.MouseInBounds determines if the mouse is inside the form bounds.

By using a filter to watch for WM_NCMOUSEMOVE, WM_MOUSEMOVE, WM_NCMOUSELEAVE, and WM_MOUSELEAVE messages, and then checking for mouse location:

_mouseInBounds = formFader.Bounds.Contains(Cursor.Position)

with a high level of performance, it can determined if the mouse is inside or outside the form bounds.

Avoiding flickers

The quick and dirty way to avoid most flicker is to use a transparency key. Pick a color that won't affect your program such as lime green, or some other ugly color that is not used by anything on your form.

The reason a form will sometimes flicker, often has has to do with a bug in WinForms and how it deals with transparent windows. WinForms is in fact just a wrapper to the Windows API. When dealing with opacity, WinForms will use a Windows API to convert your form to what is known as a layered window.

�Using a layered window can significantly improve performance and visual effects for a window that has a complex shape, animates its shape, or wishes to use alpha blending effects.� � MSDN

However, WinForms does not make a window a layered window until it feels it is necessary. The flicker you are seeing is WinForms trying to convert a non layered window into a layered one.

The reason why setting a transparency key works is because WinForms will force any window with a transparency key to be of the type layered. Set the transparency key at design time, and at runtime, the window will always be a layered window. With no need to convert, the flicker goes away.

Points of interest

As demonstrated in the sample project, you can attach event handlers to several events that indicate property changes.

History

  • 12/08/2005 - Version 1.0 released.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here