Example 1:

Example 2:

Example 3:

Introduction

I was first introduced to the term "Locus" almost an year ago, when I attended a fascinating 2-day seminar with Prof. Jim Coplien on the subject of “Humane User Interfaces”. It was quite amazing to know that the design of user interfaces in software really hasn’t got that much attention and has a long way to go. Anyway, one of the key aspects in designing a good user interface is to understand how the human perception works. First of all, we humans have only one conscious (well, perhaps except for some lunatics ;)). In our mind, the thing that gets high conscious attention or most of our conscious attention is called the "Locus of attention".

Locus- Latin for "the place."

So our Locus is the place where our conscious/mind is set. It is the state of our mind. The Locus can change if an external event alarms our mind. For example: when we are reading a book, if a rapid ball of fire moves from right to left in the horizon, it is likely to catch our attention. This biological mechanism is built in us for survival. If a dangerous event happens, our mind pays attention to it since it is something that can kill us, harm us etc.

It is obvious that our Locus can be changed. This can be taken into consideration while designing a user interface that requires user’s attention. But usually it is misused or overly used; causing distracted user interfaces that makes us unproductive and tired. Modal forms attract our locus but since they are so repetitive and since they block our work we tend to click OK automatically and close these nags. Locus changes should be done as and when needed with great care and thought. If the locus changes for a brief time, our productivity is not harmed but after that we loose concentration in our previous task. Switching back to the previous task is quite slow and causes productivity problems.

Why do we need Locus Effects?

It is quite evident from the introduction and from daily experience that to change or attract a user’s attention we need to perform some sort of alarming event. This event can be a sound (clap your hands in a room and everyone will look at you), a visual effect (in the same room wave with a red handkerchief) or a physical sensation (somebody touching your shoulder while you are staring at VS.NET).

I want to provide in this article a means to perform visual Locus Effects (maybe in future I might write a version where the computer touches your shoulder ;)).

We need Locus Effects if we want to draw the user’s attention temporarily to some activity that is being done without harming productivity, while making this change pleasant.

A good example of an application that uses the concept of Locus Effects is shown in DevExpress’ CodeRush. This highly productive commercial VS.NET add-on uses a cool set of effects in order to draw your attention to changes. For example: After setting a bookmark (Alt+Home) if you go somewhere else in the file and click Esc, the cursor returns to the bookmark and shows the original cursor position using an effect that looks like a radar beacon.

Another example for a locus effect (again a beacon) is Microsoft's mouse pointer highlight feature - If you turn it on and press Ctrl a (not so nice..) beacon draws your attention to the current position of the mouse. (Control Panel/Mouse/Pointer Options/Show location of pointer..)

So Locus Effects can be used in different scenarios where setting the attention of the user can help in improving productivity. Sample scenarios:

Showing a position in a text editor (after find, find and replace, etc.).

Showing bookmarks in a text editor.

Showing a certain control that needs to be clicked.

Showing a validation error or warning on the form that needs user changes.

Using the framework (or, How do I show a Locus Effect?)

Using the LocusEffects framework is quite straightforward and easy. First, add the LocusEffectsProvider component to VS components toolbox by:

Show tool box.

Add/Remove Items....

Browse and select the assembly BigMansStuff.LocusEffects.dll.

Select the component: LocusEffectsProvider.

Select OK.

Now drag and drop LocusEffectsProvider to your form, user control or component.

Initialization

Assuming we dragged a LocusEffectsProvider component and named it "locusEffectsProvider": In the load event handler, or in another initialization event, initialize the component by calling:

locusEffectsProvider.Initialize();

Showing a predefined Locus Effect

If a certain predefined effect is good enough for the job and needs no customizations, simple show it by calling locusEffectsProvider.ShowLocusEffect. For example showing the predefined arrow Locus Effect:

Customizations: Creating and showing a custom Locus Effect

If customization is needed, create an instance of a predefined Locus Effect class, change some of its properties and register it once (code snippets from test application showing creation of a custom curved arrow Locus Effect):

Extensions: Creating a new Locus Effect type

If none of the predefined Locus Effect classes are satisfactory even after customization of their styles, then a developer can choose to add a new type of effect that inherits from the framework classes. This is more complex (but not too complex) and requires knowledge of the framework internals as described below. But once the new class is ready, the usage pattern is identical to creating and using a predefined Locus Effect as shown above.

Locus Effects framework

Framework Internals

The framework is built from a few core classes. LocusEffectsProvider is the main component and provides a facade for managing and controlling locus effects. When ShowLocusEffect is called LocusEffectsProvider starts showing the Locus Effect by delegating the call to the registered Locus Effect instance:

So far, nothing exotic, but now we get to the fun stuff. BaseStandardEffect implements a standard effect, which is a normal sequence built up of Lead in, Body and Lead out animation stages. In each stage of the sequence there is a time duration. During the animation an accurate progress (step) is calculated based on the time. This is done in OnLeadInStep, OnAnimationBodyStep and OnLeadOutStep. The effect has paint methods for each stage that take the step progress into account and draw (i.e. render) the effect into an effect bitmap - PaintLeadIn, PaintBodyAnimation and PaintLeadOut.

m_owner is an instance of the LocusEffectsProvider component, which owns an EffectWindow window. This special window is what makes the animation rock and roll. It is a per pixel alpha window or a layered window. Layered windows were first introduced in Windows 2000 and provide full support for RGBA bitmaps and transparency. They do not work via the regular WM_PAINT mechanism and are updated by setting a new bitmap to them each time an update is needed. This is in contrast to normal windows which have regions and work through WM_PAINT messages. So EffectWindow is a layered window which has features such as anchoring (i.e. setting the direction of the bitmap in relation to the locus area), bitmap manipulation (rotation, transparency control , shadow, color overlay). Each time a paint is triggered from the animation thread, the ApplyGraphics method of EffectWindow is called. This is how we update the bitmap of the window with the new rendered effect bitmap. CalculateBitmapBounds takes care of moving the effect bitmap to the correct position using anchoring mode rules. SetWindowBitmap does the actual Windows API call that sets the new effect bitmap on the layered window.

Things to note

EffectWindow inherits from a LayeredWindow class which inherits from NativeWindow. Inheritance from System.Windows.Forms.Form class is not a good idea since all sorts of problems occur. In particular a nasty parking window is created per thread(!). Also a native window is faster and has much less footprint and negative side effects caused by default .NET implementation. We do not need all the complexities of full message support.

ImageBlender was extended from its original source code so it supports RGBA bitmaps.

Concrete effect classes can override AnimateEffect and provide their own concrete implementation.

Documentation

The framework is quite heavily documented, and I used NDoc to generated a compiled HTML file (LocusEffects.chm) which is provided with the project and includes documentation for each class, its methods and properties.

Predefined effects

Arrow effect

(LocusEffectsProvider.DefaultLocusEffectArrow or "DefaultArrow")

Beacon effect

(LocusEffectsProvider.DefaultLocusEffectBeaconor "DefaultBeacon")

Bitmap effect

(LocusEffectsProvider.DefaultLocusEffectBitmap or "DefaultBitmap")

Text effect

(LocusEffectsProvider.DefaultLocusEffectText or "DefaultText")

Things I have learned

Performance

Performance in GDI+, .NET and Windows Forms can be very slow. GDI+ itself is slower than GDI, and is (currently) not accelerated by graphics cards. All the processing is done in CPU time. Windows Forms is a very productive wrapper around Win API but it is heavy and has a lot of implementation issues which are slow, because the framework is generic. One specific thing to note is the usage of Bitmaps. Bitmaps in Win Forms are wrappers to GDI+ Bitmaps. This is all OK, however when you try to move from a GDI+ Bitmap to a regular GDI Bitmap you need a handle (HBITMAP). Calling GetHBitmap() is going to work but with a heavy price. The image is copied to a new GDI Bitmap and then the handle is returned to that new GDI bitmap. This is slow. Add to this a call of SelectObject into the Device Context each time, and it is slow too. Finally, UpdateLayeredWindow is also slow by nature. (See MSDN help about it.)

So what I have done (using Lou Amadio's help) is I have created a DIBSection manually, and selected it into the Device Context once. Now when I render the bitmap in ApplyGraphics I use Graphics.FromHDC(). This API is fast because GDI+ detects that the Device Context is attached to a DIBSection underneath and it uses directly without an intermediate buffer. Using Graphics.FromBitmap() is slow as it creates an intermediate buffer! Beware!

Layered Windows - These cool creatures are nice but slow. Empiric tests show that bitmaps over 300x300 start to clog your CPU. So it does not mean that large Locus Effects cannot be used, but there is a small price to it. Until Microsoft does not improve that API to make it much faster, there is nothing that can be done on that subject. Luckily most Locus Effects are smaller than this size, so there is no big problem.

What's next...

Depending on the feedback from users and my spare time, I would like to add the following things in future revisions:

Conclusion

This is my first article – I’ve been a CodeProject member for a long time, but finally I found the idea, time (and guts...) to write my own article and contribute something back to this really great community. The idea in this article is not new, but when I looked for such an implementation in the web (CodeProject too) there was nothing that was ready, so I decided to write it. There are no limits to imagination and new cool effects and ideas can be added easily to the framework.

The project is written in VS.NET 2003, I have not provided solutions for the older VS.NET format, but it should work on the older VS.NET with minor modifications. VS.NET 2005 (Beta 2) was tested and works fine. Also notice that the APIs used here require Windows 2000 and above. It will not work on Win9X, WinNT! These old operating systems do not support Layered Windows.

If anyone extends the framework and adds new effects, please send me the extension so that I can make it a part of the next version of the framework. Oh, and if you like it, use it, if you have any ideas on how to improve it or just have any comment - please tell me.

Philosophical insights from this article :)

Beware: Writing a CodeProject article is addictive! I am not kidding. It's like reading a book. Once you start it you cannot stop it. But it is a lot of fun.

It is so addictive that I am already thinking of my next article (Oh, my wife is going to kill me..).

Revisions

1.0.0 - 3/7/2005: Initial

1.0.1 - 7/7/2005

Fixed: Effects were not shown on non primary monitors in a multiple monitor configuration.

Share

About the Author

I've been punching code since the age of 9 when I got my first computer - A Sinclair Spectrum with 48Kb of RAM!
That was a great time, when peek and pokes were the way to do stuff.
Along the way I moved on to PC and never left it (EDIT: Since 2010 a false statement - I fell in love with Android).
I wrote in X86 Assembly, Logo , Basic, C, C++, Pascal, Delphi, Java and in the last 13 years C#.

I am also an amature photographer using Nikon D100, and taking pictures mostly of scenary & nature.
Some of my pictures are presented at: My photo gallery
(Titles are in Hebrew, but pictures have an international language.. )

Comments and Discussions

i found your code simple and usefull but i am facing difficulty in implementing your logic as your code is not for beginers. i am a newbie and not much familiar to c sharp. can you make a simple code which contains a textbox and a arrow animation. you sample project is to heavy to understand.
please sir,
i want your this technique to be used in my project

I'm not really sure what you are trying to do and unfortunately I cannot teach the basics of C# over this article.
The demo project has really simple C# code.
Using a locus effect in your projects requires only a few lines of code.

I'm really not sure what you're trying to do.
If the last window of the application has closed, then the application is finished. In that case the process dies - If you need to run 'post mortem' locus effects, then another process is needed (=spawned) before the original app dies, in order handle that.
In principle, Locus Effects can point to any point in the screen - but it does need an activator form.
So you can create a small windows app that has a hidden window and let Locus Effects run of it.

"The true sign of intelligence is not knowledge but imagination." - Albert Einstein

I want to have my app (Zeta Uploader Windows Client [^]) that uploads a file to a server, to show a short notification at the end of the process, after the upload finished (and before the upload window is being destroyed, but after it becomes hidden).

Since the app could run for several minutes and might very well be in the background, behind other apps, my idea was to show a locus effect with a text "Upload finished, URL copied to clipboard" as a topmost message.

Not sure whether this works, what I remember from looking at your sources (IIRC) is that the locus effect is hidden when the owning window is not in the foreground.

Uwe,
Currently an activator form is needed.
I have no bandwidth to change it now, but will try to see if I can remove that requirement (and make it an option).
It may take a few days until I get to it.
OR - you can try and do it your self.

Thanks,
Yuval

"The true sign of intelligence is not knowledge but imagination." - Albert Einstein

Please get the latest version from Google Code, and let me know if it works for you or not.
For activatorForm simply pass null.
The test application tests that in the 'Show' with absolute screen coordinates, under Misc tab

A long sleep is not good as you're are putting the main UI thread to sleep. What I wrote is a busy loop which forces the message pump to work (by calling Application.DoEvents) but still does not make the CPU work 100% of the time. Locus effects is after all a window that needs that message pump to work.

I checked-in the revised code to Google Code.

"The true sign of intelligence is not knowledge but imagination." - Albert Einstein

Yuval Naveh wrote an article on Locus effects. At the bottom there was a revision list and one revision stated that a fix was made for effects not showing on non-primary monitors in a mutliple monitor configuration.

I'm having the same trouble with a project I'm working on and so I was wondering what the code change was in Yuval's project that fixed the problem for him?

Thanks Yuval for your reply. I'm not having trouble with your project, just wondering what changes you made to get it to work on multiple monitors since I have an unrelated project that's experiencing the same kind of problem (drawing an image via gdi and using UpdateLayeredWindow on an XP pro machine with 2 monitors works on the primary monitor but doesn't show up on the secondary one). So I thought if I knew what differences there were for your project to fix that problem I might learn from it.

First, I'm glad to share information between projects.
Second, it is a relief to know the Locus Effects does not have a bug

As for your question:
I think the way I handled it was to use Screen.FromPoint & SystemInformation.VirtualScreen.
If you look at EffectWindow.CalculateBitmapBounds you will see the framework's calculation of where to position the effect.

lol, yeah, I'm glad I didn't find a bug for you either. Actually your project is a bit complicated for me right now but it's a good learning experience because I've not used C# before, so I was just trying to find the piece that hopefully made the difference. I really appreciate you getting back to me so fast as I've been stuck on this for a while now. Hopefully I'll figure this out tonight with this input.

Cheers, and happy holidays!

By the way, I checked out a few of your photos (I'm a photographer myself on the side with a B&W darkroom at home)--nice!

yeay! found my problem. I was making a call to CreateCompatible bitmap with a size that was too small. I figured it out by playing with values in my project after looking at what values you were passing to UpdateLayeredWindow in the debugger. I'll probably look at your code in more detail when I get around to making it faster.

I think this article is great from a human factors perspective too. I went back to get my masters in human factors (finished in early 2008), but there's always more to learn. thanks again for your post--great work!