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:
- Synchronous and asynchronous fading.
- Full opacity on hover.
- 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 has several properties associated to it:
CurrentFade - Gets the status of the form:
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;
Synchronous fading would look like this:
this.FadeOpacity = .75;
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
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:
Timers run a different thread.
- The speed or
FadeRate of the fade could be controlled.
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
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.
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
MouseBounds.MouseInBounds determines if the mouse is inside the form bounds.
By using a filter to watch for
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.
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.
- 12/08/2005 - Version 1.0 released.