English/U.S. English is not my first language. I'm Italian. Explaining things to others is not my profession, I'm a cryptic programmer :-) ... I live in a dark dungeon all alone :-) I'm a little ironic, but then, I'm a mad programmer :-).
I love the Outlook-style controls... I love them very much... My problem is that it's very difficult to find an XP-themed one. So, one day (around a year ago) I decided to build one... I fought against the poor Themes documentation and, in the end, I won. This is the result of my victory...
The BAD things
It doesn't support: small icons, ellipsis on the buttons, drag and drop, in-place-editing of items, sorting of items, moving items, a good and easily expandable event model, owner-draw items, non-pushable icons (now the pushed icon remembers it was pushed and is drawn in a different style... normally this is an extended style of an OutBar, not the default style), disabling the themed part (the OutBar will work on W95/W98/NT etc.). I'm not even sure it will draw correctly with non-default themes (I've tried it with all the default XP themes, but I don't have StarDock or similar programs).
The GOOD things
It's quick to redraw (it uses double buffering techniques), it has a dual interface: message based (
OBM_*) and method based (you know, a thin wrapper around the messages, like the ATL and the WTL do with Windows, with many methods that are composed of a single line:
SendMessage(m_hWnd, OBM_something, lParam, wParam)), it has a find method (technically two, one for the Folders and one for the Items), it's fancy-drawn with owner draw scrollbars (try stretching it in width and length) and an owner draw ListIcon (built from scratch... the first version used a ListView, but I fought a losing battle against its scrolling, because I wanted the icons to be horizontally centered, so I decided to create it from zero), it fully supports keyboard and mouse (arrows, pgup/pgdown/home/end/wheel), the hot item is drawn differently (only when you use Themes), it's UNICODE and W64 ready, it compiles under W4 warnings.
The SO 'N' SO things
It's licensed under the MIT license (a simplified BSD license), it uses many Windows controls (one for itself, one for each folder, and one for the icons). The last item is in the SO 'N' SO category because I consider a good thing that each part of the OutBar can be controlled separately (and the program can
SetFocus() correctly) and that the user can Tab/Shift-Tab between parts of the OutBar, while it's bad that it consumes many controls.
How can I use it?
Remember: this code is tested with VC++ 8.0/WTL 7.5!! I don't want to write three pages to tell you things that you can discover yourself simply by opening the wtlOutBarDemo project (hint: all the "important" lines are marked with
// WTLOUTBAR so you can easily search for them). If you know how to work with a
CListViewCtrl, then you know how to work with a
CWtlOutBarImpl (yes, I "borrowed" the interface of the ListView). In the end, the most difficult thing to do is probably to create the ImageList that will contain the icons. There are two ways of doing it: you can have all the icons in separate .ico files and do the
AddIcon one at a time, or you can play the little mad scientist and fuse all the icons in a single bitmap and then use
CreateFromImage. Small note: the wtlOutBarMDIDemo is a demo of the OutBar and not of the Split+MDI thing. I hope it does work, but I'm not very sure. Splitter windows and MDI frame windows are complex arguments, and they are like the "undiscovered land" of WTL.
What are all the classes?
CWtlOutBarT<Tbase> where 99% of the time,
TBase = CWindow, a thin wrapper around the OutBar. It's composed of many one line methods that simply call the right message. Unless you want to do strange things, you don't need to directly use this class.
CWtlOutBarImplT<TBase> where 99% of the time,
TBase = CWtlOutBarT<CWindow>. The "meat" class. You will probably declare it somewhere in your main form and it'll sit there. If you derive it from
CWtlOutBarT<CWindow>, you can easily use it with the methods (so you can
CMemoryStripDC is an utility class. I needed a reusable
CMemoryDC, so I created one (the OutBar is drawn in "strips", each icon with its description is a strip).
Is there something strange, something magical in your OutBar?
Mmm... Yes... The class supports two styles (they are "basic" styles, not-extended).
OBS_SHAREIMAGELISTS (that is identical to
LVS_SHAREIMAGELISTS, so I won't explain it here... OK... I'll explain it): If you don't use the style the OutBar considers to have the ownership of the ImageList, it will destroy it when the OutBar is destroyed. If you use the style, the OutBar won't destroy the ImageList at the end of its life.
OBS_FORCESELECT: If you set this style, the OutBar will ALWAYS have a selected item (unless there are no items). If you delete the selected item, another item will be selected (in an intelligent way... it'll first try to go down and then try to go up from the previously selected item).
There are three notifications that the class will throw:
OBN_FOLDEROPEN is quite clear: it's triggered when the user opens a folder.
OBN_ITEMSELECT is triggered when the user selects an item (by clicking on it or using the keyboard).
OBN_ITEMSELECTAUTO is triggered when the program selects an item (by using
SetSelectedItem or by triggering the
OBS_FORCESELECT effects, for example).
Hey, but I want some documentation
You are lucky, you are very lucky. I decided to learn how to use Doxygen, so I commented all the "user" part of the source (you know, the not-internal part). You can read it in HTML format or in CHM format (the Windows HTML Help format)... and you can read it when you look at the source (because the comments are in the source, the doxygen simply extracts them and creates an easy-to-read file)... So, before leaving you a little baffled, I'll teach you a single thing (a thing I discovered after writing the OutBar).
m_wtlOutbar is the name of the OutBar. This will give the ListView part of the OutBar a static edge (a more 3D-like aspect).
Hey, but I'm a little lazy and I really want some examples
OK… I'll explain the wtlOutBarDemo example… Create a Dialog based WTL project. Copy wtloutbar.h and stringcex.h in the folder of the project and add them to the project. Add some icons to the project, and give them IDs like
IDI_ICONx. In the stdafx.h, add this line near the
#define WINVER line:
#define _WIN32_WINNT 0x0501
It's required for the Theme support.
#include <span class="code-keyword"><atlwin.h></span>, add:
#include <span class="code-keyword"><atlcoll.h> </span>
#include <span class="code-keyword"><atltheme.h></span>
#include <span class="code-keyword"><tmschema.h></span>
#include <span class="code-string">"wtloutbar.h"</span>
The first (atlcoll.h) is because I use ATL collections. The second (atltheme.h) is for the themes. The third (tmschema.h) is because Microsoft decided that it was a good thing to split the base header for the themes and the header that contains all the "codes" necessary to write theme based apps. The fourth (wtloutbar.h) is the OutBar library (it will auto-include stringcex.h).
Now, you should go to the properties of your project, and in the Linker->Input page, add delayimp.lib to Additional Dependencies and uxtheme.dll to Delay Loaded DLLs. In this way, your program will be compatible with pre-XP systems that don't have the ComCtl32.dll v6 library (the library used for the Themes... Yes... I know, it isn't exactly correct what I've written... it's an oversimplification... so shoot me :-)).
Now, go to the mainfrm.h file, and after the
#pragma once line, add:
#define IDC_OUTBAR 10000
This will be the ID of the control (yes, 10000 is a random number; it shouldn't be used by anyone and then, IDs are local, so it won't be a problem, but if you don't like it, change it!). And under the
public: line, add:
This is the control. Very simple. Now, go in the
OnInitDialog(…). Just before the
Return TRUE, add:
il.Create(32, 32, ILC_COLOR8|ILC_MASK, 6, 1);
ATLVERIFY(il.AddIcon(ico) != -1);
ATLVERIFY(il.AddIcon(ico) != -1);
This will create an ImageList with a base space for six icons (you can use different values) and that will grow one icon at a time. Then, it'll begin to load the icons and add them to the ImageList. We are loading the icons from the .exe file, so you don't have to truly "free" them after adding them to the ImageList (so the use of the
ico.Detach()). You can add more icons… simply repeat the last three lines.
rect.right /= 3;
This will calculate the area of the client Rect (one third of the width of the window).
ATLVERIFY(m_wtlOutbar.Create(m_hWnd, &rect, NULL,
WS_CHILD|WS_VISIBLE|OBS_FORCESELECT, 0, IDC_OUTBAR));
This will create the OutBar. We are using the
We set the ImageList of the OutBar. Note that
OBSIL_NORMAL is the only image list style supported. Note that we are creating the ImageList "locally" (the scope of the ImageList is the
OnInitDialog method). It's OK, because the OutBar will take the ownership of the ImageList (we haven't used the
We prepare the memory for 10 folders (we won't use them).
ATLVERIFY(m_wtlOutbar.AddFolder(OBFF_TEXT|OBFF_PARAM, _T(""), 3) != -1);
ATLVERIFY(m_wtlOutbar.InsertFolder(0, OBFF_TEXT|OBFF_PARAM, _T(""), 5) != -1);
We add two folders (technically, we add a folder, and then we insert another folder before the just added folder). We set the caption and the
PARAM of the folders (the 3 and 5 are the
PARAMs. The 0 of the
InsertFolder is the location where the folder will be inserted.
m_wtlOutbar.AddItem(0, OBIF_TEXT|OBIF_IMAGE|OBIF_PARAM, _T("[1-1]"), 1, 10);
m_wtlOutbar.AddItem(1, OBIF_TEXT|OBIF_IMAGE|OBIF_PARAM, _T("[2-2]"), 2, 20);
m_wtlOutbar.InsertItem(0, 1, OBIF_TEXT|OBIF_IMAGE|OBIF_PARAM, _T("[1-2]"), 3, 30);
m_wtlOutbar.InsertItem(0, 2, OBIF_TEXT|OBIF_IMAGE|OBIF_PARAM, _T("[1-3]"), 4, 40);
We add two items (one for each folder) and then we insert another two (technically, we use the Insert as an Add (technically, the Add is just a shortcut to the Insert)). We set the text, the image (the 1, 2, 3, and 4 are the icon numbers, zero based), and the
PARAM (10, 20, 30, and 40). The first number (0, 1, 0, 0) is the number of the folder where the items will be added. The second number used by the
InsertItem method is the location where the item will be inserted.
Now, you have created the OutBar... You can try to compile, then open the dictionary and look for some new bad words (because surely, some small errors creep in the program), correct everything and retry :-)
In the example, I even used this in an
OnSize event handler:
m_wtlOutbar.SetWindowPos(NULL, 0, 0, LOWORD(lParam) / 3,
lParam is the new width of the window). In this way, the OutBar is always proportional to the window. Now, you could add the Notify handlers... Add these to the
NOTIFY_HANDLER(IDC_OUTBAR, OBN_FOLDEROPEN, OnFolderOpen)
NOTIFY_HANDLER(IDC_OUTBAR, OBN_ITEMSELECT, OnItemSelect)
NOTIFY_HANDLER(IDC_OUTBAR, OBN_ITEMSELECTAUTO, OnItemSelectAuto)
Then, create three Notify handlers. To find out what is the new Folder Open, use:
int iFolder = m_wtlOutbar.GetOpenFolder();
To find out which item is selected, use:
int iFolder = m_wtlOutbar.GetSelectedFolder()
int iItem = m_wtlOutbar.GetSelectedItem()
Truly finished!! :-)
- wtloutbar.h - 1.0 - May 2005 - Initial release (not published).
- wtloutbar.h - 1.1 - May 2006 - Small improvements, added comments.
- wtloutbar.h - 1.11 - May 2006 - Bug-fix in the
WM_NCDESTROY handler. Put
CMemoryStripDC in another header.
- wtloutbar.h - 1.12 - Jun 2006 - Corrected small memory leak (the HRGN wasn't deallocated). Changed the way the memory is freed during destruction.
- wtloutbar.h - 1.12R2 - Nov 2007 - Fixed the MDI demo. Finally, it seems to work correctly. Thanks to Nenad for the good idea on how to do it.
I'm a BSD guy, so I'll license this library under the MIT license (a simplified BSD license).
Copyright (c) 2005, 2006 Massimiliano Alberti xanatos(at)geocities.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.