Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / WTL
Article

Outlook Bar for WTL (with XP support!!)

Rate me:
Please Sign up or sign in to vote.
4.73/5 (17 votes)
7 Nov 2007MIT10 min read 64.4K   2.4K   60   10
A themed Outlook Bar for WTL (works even without themes).

Sample Image - wtloutbar.gif

Remember

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 :-).

Introduction

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 GetFocus()/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 LoadIcon/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 AddFolder, AddItem, GetSelectedFolder, GetSelectedItem etc.)
  • 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, OBN_ITEMSELECT, and OBN_ITEMSELECTAUTO.

  • 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.GetHWNDHandle(-1).ModifyStyleEx(0, WS_EX_STATICEDGE);

where 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_ICON1IDI_ICONx. In the stdafx.h, add this line near the #define WINVER line:

#define _WIN32_WINNT 0x0501

It's required for the Theme support.

After the #include <atlwin.h>, add:

#include <atlcoll.h> 
#include <atltheme.h>
#include <tmschema.h>
#include "wtloutbar.h"

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:

CWtlOutBarImpl m_wtlOutbar;

This is the control. Very simple. Now, go in the OnInitDialog(…). Just before the Return TRUE, add:

CImageList il; 
il.Create(32, 32, ILC_COLOR8|ILC_MASK, 6, 1); 

CIconHandle ico; 
ATLVERIFY(ico.LoadIcon(IDI_ICON1)); 
ATLVERIFY(il.AddIcon(ico) != -1); 
ATLVERIFY(ico.Detach()); 

ATLVERIFY(ico.LoadIcon(IDI_ICON2)); 
ATLVERIFY(il.AddIcon(ico) != -1); 
ATLVERIFY(ico.Detach());

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 rect; 
GetClientRect(&rect); 
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 OBS_FORCESELECT style.

m_wtlOutbar.SetImageList(il, OBSIL_NORMAL);

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 OBS_SHAREIMAGELIST style).

ATLVERIFY(m_wtlOutbar.SetFolderCount(10));

We prepare the memory for 10 folders (we won't use them).

ATLVERIFY(m_wtlOutbar.AddFolder(OBFF_TEXT|OBFF_PARAM, _T("[2]"), 3) != -1); 
ATLVERIFY(m_wtlOutbar.InsertFolder(0, OBFF_TEXT|OBFF_PARAM, _T("[1]"), 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.

Finished!!

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, 
            HIWORD(lParam), SWP_NOMOVE|SWP_NOZORDER);

(where 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 BEGIN_MSG_MAP():

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!! :-)

History

  • 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_DESTROY/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.

License

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.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior)
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralThare is a bug in this code Pin
qqj122812-Dec-10 3:09
qqj122812-Dec-10 3:09 
GeneralGreat! Pin
Weidong Fang4-Jun-08 14:53
Weidong Fang4-Jun-08 14:53 
Questioncrash! Pin
free2000fly6-Nov-07 12:41
free2000fly6-Nov-07 12:41 
AnswerRe: crash! Pin
xanatos6-Nov-07 23:32
xanatos6-Nov-07 23:32 
GeneralRe: crash! Pin
xanatos7-Nov-07 4:22
xanatos7-Nov-07 4:22 
GeneralRe: crash! Pin
xanatos7-Nov-07 13:50
xanatos7-Nov-07 13:50 
QuestionHow can I add this to my MFC application. Pin
nkajale19-Sep-06 11:36
nkajale19-Sep-06 11:36 
AnswerRe: How can I add this to my MFC application. Pin
xanatos20-Sep-06 4:28
xanatos20-Sep-06 4:28 
Generalerror C2065: 'CIconHandle' : undeclared identifier [modified] for wtlOutBarDemo Demo project. Pin
michael. zhao28-May-06 4:38
michael. zhao28-May-06 4:38 
GeneralRe: error C2065: 'CIconHandle' : undeclared identifier [modified] for wtlOutBarDemo Demo project. Pin
xanatos28-May-06 4:58
xanatos28-May-06 4:58 
It was added to WTL 7.5. You know that WTL 7.1 is quite old (it's dated 2003 the original version), right?
If you NEED to use WTL 7.1 then you have 2 possibilities:

A) you can decompress a newer WTL 7.5, open atluser.h and extract from circa line 670 to line 878 (class CIconT, typedef CIcon, typedef CIconHandle)
OR
B) you can replace the CIconHandle with something like

HICON ico;

ATLVERIFY((ico = ::LoadIcon(ATL::_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCE(IDI_ICON1))) != 0);
ATLVERIFY(il.AddIcon(ico) != -1);

clearly repeat the two lines for each icon.

You can remove the ico.detach() (all of them)

Please tell me if it works with WTL 7.1!

-- modified at 11:03 Sunday 28th May, 2006

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.