Flash and fade a notification

P

Petr Danes

Normal procedure to prevent a user from doing something illegal, immoral or
fattening is to disable controls that allow such an action whenever
conditions exist that make such an action undesirable. But sometimes testing
for such conditions is too difficult or time-consuming to be practical in
real time. In such a case, the tests are performed only after the user
initiates a request for an action. When it is subsequently found that the
requested action cannot be performed, some method must be employed to inform
the user.


A common choice is to display a dialog box with an error message explaining
the problem, but this can be annoying, since it requires an action from the
user to dismiss the dialog.



Another common choice is the Beep routine, which does not require the user
to acknowledge it. But it also has problems: not all users have speakers, or
have them turned on, or adequate volume or the ability to hear at all. And
even when all that is fine, a simple chirp does little to indicate what the
problem is.



A somewhat better solution is to make visible an error text somewhere on the
active form, explaining what's wrong. This does not irritate the user with
the necessity to dismiss a dialog and clearly indicates what is amiss. But
it makes life more difficult for the programmer: the error message must be
cleared away at some point, and since there is no way to predict the user's
next action, the message-clearing code must be invoked from all possible
routines accessible to the user. When new actions are added, they must
include calls to the clearing code, which is easy to forget, and leaving a
glaring error message displayed until the user does something, anything, can
make the form look unattractive.



I have recently started using a method which addresses all these problems. I
have written a routine which, given a control, changes the color of the
control to something specified, then slowly fades it back to it's previous
state. My first use was the background behind a set of toggle buttons which,
when depressed, can cause certain lookups to fail. Rather than shouting an
error dialog and forcing the user to deal with it, I flash the background
behind the group of toggle buttons red, and let it slowly (about ? second)
fade back to its normal background gray. It shows the user where the problem
is (a depressed toggle somewhere in the group) and does not require any
action on his part to acknowledge it. The effect is somewhat like a caution
light that flashes on and dims away to nothing, sort of a visual chime, but
specifically indicating where there is a problem. I find it much more
pleasing and less intrusive than any other notification method I've invented
so far.



Naturally, this presumes some level of sophistication on the part of the
user. A complete rookie could not be expected to understand the significance
of the color flash, but this is intended to be a low-key reminder to
experienced users that they've overlooked something. Once they are aware of
the problem, they know what to do about it. Counting code could be added to
display a more detailed error message if the same mistake was repeated
several times, to accommodate less experienced users. Instructions would
then be fairly simple - if you don't understand what's wrong when you've
made a mistake, just repeat the action once or twice and the software will
go into more detail about it for you.



The code was designed to flash an area, but it can certainly be modified to
display error text messages in the same way as it now does background -
flash on, then fade away. The time delay should be adequate to allow the
user to read the message, but not so long as to unnecessarily delay his
progress. That pretty much means it should be used only for very short
texts, which can be comprehended at a glance, else the user may have to
repeat the offending action, maybe even several times, to read the entire
message. I've had personal experiences with devices that acted that way, and
can testify that such behavior raises the aggravation factor of using a
device by a lot.



The code calculates the difference between the current color value and the
requested color value for all three color channels, sets the new color, then
loops around stepping the three channels back to their original state. Each
color channel is computed and incremented separately, else the color change
would not be smooth. It does not matter what the original color or
notification color are - the code can handle any combination of colors. I
have it defaulting to red if the flash color is not specified.



Constants are included to control the speed and smoothness of the fade. The
nap value is better to give a fairly constant fade time. If you place too
much reliance on the steps constant to control fade rate, it will vary based
on the abilities of the computer. Nap uses a fixed pause time and so is more
uniform across machines of varying capability. The Sleep API declaration is
only necessary if you want the routine to use this. If not, you can remove
it and the Sleep statement (the first statement inside the Do loop).



My only gripe with the routine is a slight screen flicker during the fade. I
someone knows how to fix that, I'd be happy to hear about it.



Pete







Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)



Private Sub FlashBlock(ByRef ctl As Control, Optional ByVal fc& = vbRed)

Dim oc&, ro&, go&, bo&

Dim rf!, gf!, bf!, rs!, gs!, bs! ' These must be reals, integers would be
too readily truncated to unusable values.

Const steps& = 15, nap& = 50



' Audio cue, just as an extra

Beep

oc = ctl.BackColor

ctl.BackColor = fc



' Old color values

ro = vbRed And oc

go = (vbGreen And oc) \ 256

bo = (vbBlue And oc) \ 65536



' New color values, which will be stepped back toward the old ones.

rf = vbRed And fc

gf = (vbGreen And fc) \ 256

bf = (vbBlue And fc) \ 65536



' Step values to march back towards the original color.

rs = (rf - ro) / steps

gs = (gf - go) / steps

bs = (bf - bo) / steps



' Cycle around stepping the new color values back towards the old one until
all have returned to their original value.

' Real arithmetic requires some adjusting, else floating point imprecision
might not get us exactly back to our stating values.

' Abs is necessary, since we don't know whether the new values are greater
or smaller than the old values.

Do

If nap > 0 Then Sleep nap

If ro <> rf Then If Abs(ro - rf) > Abs(rs) Then rf = rf - rs Else rf =
ro

If go <> gf Then If Abs(go - gf) > Abs(gs) Then gf = gf - gs Else gf =
go

If bo <> bf Then If Abs(bo - bf) > Abs(bs) Then bf = bf - bs Else bf =
bo

ctl.BackColor = RGB(Int(rf), Int(gf), Int(bf))

DoEvents ' Necessary to let go, so that the display actually gets
updated.

Loop Until ro = rf And go = gf And bo = bf

End Sub
 
D

Dirk Goldgar

Petr Danes said:
A common choice is to display a dialog box with an error message
explaining the problem, but this can be annoying, since it requires an
action from the user to dismiss the dialog.

Don't forget the option of a timed message box, which can be implemented as
a form that uses its Timer event to close itself. When I use this
technique, I also have an "OK, yeah, I get it!" button on the form, for
impatient users who are quick on the uptake.
I have recently started using a method which addresses all these problems.
I have written a routine which, given a control, changes the color of the
control to something specified, then slowly fades it back to it's previous
state. My first use was the background behind a set of toggle buttons
which, when depressed, can cause certain lookups to fail. Rather than
shouting an error dialog and forcing the user to deal with it, I flash the
background behind the group of toggle buttons red, and let it slowly
(about ? second) fade back to its normal background gray. It shows the
user where the problem is (a depressed toggle somewhere in the group) and
does not require any action on his part to acknowledge it. The effect is
somewhat like a caution light that flashes on and dims away to nothing,
sort of a visual chime, but specifically indicating where there is a
problem. I find it much more pleasing and less intrusive than any other
notification method I've invented so far.

Cool! Nice idea, nice job.
 
P

Petr Danes

Don't forget the option of a timed message box, which can be implemented
as a form that uses its Timer event to close itself. When I use this
technique, I also have an "OK, yeah, I get it!" button on the form, for
impatient users who are quick on the uptake.

Never used that, seems like it might rattle some users, who expect a dialog
box to stay put until it's dismissed. But it's an interesting technique,
I'll give it a try.

Cool! Nice idea, nice job.

Thanks, Dirk. Coming from someone with your credentials, that means a lot to
me.

Pete
 
P

Petr Danes

Nice!!

Thank you, I'm pretty pleased with the effect.

A possible enhancement: I could see expanding it to allow for multiple
controls, maybe with the use of a ParamArray parameter or an array of
controls. Then when something is bad due to certain combinations of
values
in several controls, they all can flash.

Yes, that could be useful, although I see potential problems if the controls
in the array were not all of the same type or with the same initial
background color. But the color issue could be addressed with arrays of
color and step values, and the different types of controls could be handled
by either error checking, to test if all controls in the group have the
property that you want to fade (ForeColor, BackColor, BorderColor), or
insisting that all controls be of exacty the same type, or just letting it
crash if the programmer feeds it some nonsense.

I do not get the flicker on my speedy computer. I can try it on a
significantly slower one later on.

I'm running A2007 / XP 2002 SP3 on a Dual-Core E5300 @ 2.60 GHz, but I do
not have a separate graphics card. The flicker is pretty minimal, certainly
not enough to bother users, in fact, I doubt if they will even notice. I
just tend to whine when I can't get something to behave -exactly- the way I
want. The app that's doing this is one where I have a label as a colored
background behind a set of toggle buttons. The only flicker is the one
toggle button that's currently depressed. The label is steady and so are the
undepressed toggles. Probably not worth the headache to pursue it.

Thanks a lot for sharing!

You're welcome, glad you like it. I've gotten so much useful information and
so many great ideas from these newsgroups, I'm stoked when I can contribute
something of my own.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top