Introduction
Writing software has become an art. I have always felt that programmers who write software that targets a "windowing" environment have one of two choices:
- Write the software; give it a workable UI and leave it.
- Write the software; spend time and effort making it look good and satisfy the senses of the customer.
In my experience, I have found that if the software looks good or exciting or just generally appealing to the end user, the process of adoption of the software in the user's life becomes less painful. Developers who take pride in presenting a "beautiful" user interface to their technology that the user never sees can consider themselves artists in their own right.
In an effort to make my software more "beautiful", I've created the CKCSideBannerWnd
class. The class brings to life the concept of banners, which can be used to give the user context as to what the purpose is of the current window that they are staring at.
Background
Every time I use software, I always take notes of what I liked about the UI, what I didn't like, what was a good idea, what wasn't and, more importantly, why. One of the concepts that I've always enjoyed is that of a banner (like those found in InstallShield/Wise installation wizards, CPropertyEx
derived wizards, etc).
Personally, I feel that those kinds of "well placed" banners give the software UI a more appealing, more professional look. So I took it upon myself to go and whip up a class that can pretty much do what the mentioned banners can do, but a little bit more (like attaching the banner to any edge of a window).
I also felt that the programmer should not have to worry about making allowances for the banner, and that it should be a quick exercise to attach a banner to any window. I have to admit, 2 of my own software packages have already been "upgraded" with the banner control :)
Using the Code
First off, to use the CKCSideBannerWnd
class, you will need to include the following files in your project:
- KCSideBannerWnd.h
- KCSideBannerWnd.cpp
You will also need to copy the WndUtil.h file into the same directory as the KCSideBannerWnd.* files. Once you have the class at your disposal, using it is simple. To attach a banner to a dialog, add a member to your dialog class like this:
#include "KCSideBannerWnd.h"
class CYourDialog : public Dialog
{
public:
CYourDialog();
protected:
CKCSideBannerWnd m_banner;
};
Now add the following BOLDED code below to your OnInitDialog()
function:
BOOL CYourDialog::OnInitDialog()
{
CDialog::OnInitDialog();
m_banner.Attach(this);
}
If your dialog has the sizing borders, override the OnSize()
function and add the BOLDED code below:
void CYourDialog::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
if ( m_banner.m_hWnd )
m_banner.UpdateSize();
}
CKCSideBannerWnd
can be attached to any other window, as well. Simply follow the above mentioned steps for attaching the banner to views, etc.
How It Works
The basic working of the control is as follows:
- When
Attach()
is called, get hold of the parent window (passed in as first parameter).
- Make space for the banner by growing the parent window in the appropriate direction.
- Enumerate all of the parent's child windows (excluding the banner) and offset them accordingly.
- Create the banner control and attach it.
The beauty of this technique is that the programmer does not specifically have to make space for the banner on any of his/her already defined dialogs or windows. Just a simple attach and the control will make room for itself.
Points of Interest
This article would have been published a lot sooner had it not been for an interesting nuance that I ran into with Windows and its combobox handling. I had been testing this control on a number of different dialogs and everything was working great, until I used it on a dialog box which contained 2 combobox controls.
When the banner was attached, the combobox controls "lost" their ability to display selected text or text that had been entered by the user on the keyboard. In fact, it seemed as if the edit control of the combobox had just stopped working. (The listbox control worked just fine :))
I posted to the forums, but no one seemed to be able to give me an answer as to why this was happening. So off I went on a bug(??) hunt to see what I was doing wrong to cause combobox controls to partially stop working.
What I found eventually was that when EnumChildWindows()
is called, and it in turn calls the user supplier handler with each child HWND
, one of the children that it found was in fact the edit control of the combobox. The problem came in with me moving the actual edit control, as well as the combobox control.
The solution to this problem was to add a check in the enumeration function which called GetParent()
against the so-called child HWND
and checked that the parent was in fact that parent as I knew it (the dialog or owning window). The edit control of the combobox control did in fact return the combobox's HWND
as its parent and, since I was no longer moving both combobox and its contained edit control, everything worked great.
Class Documentation
-
BOOL Attach(CWnd* pWnd, unsigned int uFlags =
KCSB_ATTACH_LEFT, unsigned int uiID = 0xFFF0)
Call this function to attach the banner to a parent window.
uFlags
can be on of the following (Exclusive Flags):
KCSB_ATTACH_LEFT : |
Attaches the banner on the left of the window |
KCSB_ATTACH_TOP : |
Attaches the banner to the top of the window |
KCSB_ATTACH_RIGHT : |
Attaches the banner to the right of the window |
KCSB_ATTACH_BOTTOM : |
Attaches the banner to the bottom of the window |
The uiID
is the ID that will be used by the parent to refer to this control. It is defaulted to 0xFFF0.
-
void SetSize(int nSize)
Adjusts the size of the banner. This is relative in terms of adjusting either the height or the width of the banner and is dependent on where the banner is positioned. In other words, if the banner is on the the left or right, the width will be adjusted. If the banner is on the top or bottom, the height will be adjusted.
-
int GetSize()
Returns the current size of the banner.
-
void UpdateSize()
Call this when the size of the parent window has changed.
-
void SetPosFlag(unsigned int uFlags)
Call this to reposition the banner. The same flags apply as discussed in Attach()
.
-
unsigned int GetPosFlag()
Returns the current position flag.
-
void SetFillFlag(unsigned int uFlag)
Call this to set the fill type of the banner. Possible values are:
KCSB_FILL_FLAT : |
Fills the banner with the color set with SetColBkg() . |
KCSB_FILL_GRADIENT : |
Fills the banner with a gradient starting at the color set with SetColBkg() and moving to the color set with SetColBkg2() . |
KCSB_FILL_TEXTURE : |
Fills the background of the banner with a bitmapped texture that can be set with SetTexture() . |
-
unsigned int GetFillFlag()
Returns the current fill flag.
-
void SetTitle(const char* lpszTitle)
Sets the title (main string) of the banner.
-
CString GetTitle()
Returns the current title of the banner.
-
void SetCaption(const char* lpszTitle)
Sets the caption (secondary string) of the banner.
-
CString GetCaption()
Returns the current caption of the banner.
-
void SetColBkg(COLORREF col)
Sets the primary background color. When a gradient fill is active, this will be the color that the gradient starts at.
-
COLORREF GetColBkg()
Returns the primary background color.
-
void SetColBkg2(COLORREF col)
Sets the secondary background color. When a gradient fill is active, this will be the color that the gradient will move towards. In flat fill mode, this color serves no purpose.
-
COLORREF GetColBkg2()
Returns the secondary background color.
-
void SetColEdge(COLORREF col)
Sets the color of the edge.
-
COLORREF GetColEdge()
Returns the color of the edge.
-
COLORREF SetColTxtTitle(COLORREF col)
Sets the color of the title text.
-
COLORREF GetColTxtTitle()
Returns the color of the title text.
-
COLORREF SetColTxtCaption(COLORREF col)
Sets the color of the caption text.
-
COLORREF SetColTxtCaption()
Returns the color of the caption text.
-
void SetEdgeOffset(CSize szOffset)
Sets the XY offset of the title text from the edge.
-
CSize GetEdgeOffset()
Returns the XY offset of the title text from the edge.
-
void SetCaptionOffset(CSize szOffset)
Sets the XY offset of the caption from the start of the title. In other words, if the edge offset is (5, 5) and the caption offset is also (5, 5), then the caption will be offset 5 pixels away from the title.
-
CSize GetCaptionOffset()
Returns the XY offset of the caption text relative to the title.
-
void SetTitleFont(CFont* pFont)
Sets the font used to draw the title text.
-
void GetTitleFont(LOGFONT* pFont)
Returns the font used to draw the title text in the LOGFONT
structure passed into the function.
-
void SetCaptionFont(CFont* pFont)
Sets the font used to draw the caption text.
-
void GetCaptionFont(LOGFONT* pFont)
Returns the font used to draw the caption text in the LOGFONT
structure passed into the function.
-
bool SetIcon(HICON hIcon,
UINT uiIconPos = KCSB_ICON_RIGHT | KCSB_ICON_VCENTER,
bool bSelfDelete = true)
Sets the ICON that will be drawn in the banner. bSelfDelete
indicates to the control whether you will be deleting the HICON
resource (bSelfDelete = false
) or whether the control can delete it when it's no longer needed (bSelfDelete = true
).
uiIconPos
can be one or more of the following values:
KCSB_ICON_LEFT : |
Draws the icon on the left of the banner. If the banner is attached to the left of the window, the icon will be drawn at the bottom. If the banner is attached to the right of the window, the icon will be drawn at the top.
Note: This flag cannot be combined with the KCSB_ICON_RIGHT flag.
|
KCSB_ICON_RIGHT : |
Draws the icon on the right of the banner. If the banner is attached to the left of the window, the icon will be drawn at the top. If the banner is attached to the right of the window, the icon will be drawn at the bottom.
Note: This flag cannot be combined with the KCSB_ICON_LEFT flag.
|
KCSB_ICON_TOP : |
Draws the icon at the top of the banner. If the banner is attached to the left of the window, the icon will be drawn on the left. If the banner is attached to the right of the window, the icon will be drawn on the right.
Note: This flag cannot be combined with the KCSB_ICON_VCENTER or KCSB_ICON_BOTTOM flags.
|
KCSB_ICON_VCENTER : |
Draws the icon vertically centered in the banner.
Note: This flag cannot be combined with the KCSB_ICON_TOP or KCSB_ICON_BOTTOM flags
|
KCSB_ICON_BOTTOM : |
Draws the icon at the bottom of the banner. If the banner is attached to the left of the window, the icon will be drawn on the right. If the banner is attached to the right of the window, the icon will be drawn on the left.
Note: This flag cannot be combined with the KCSB_ICON_VCENTER or KCSB_ICON_TOP flags
|
-
void SetIconPos(UINT uiIconPos)
Set the icon's position in the banner. Use the flags described in SetIcon()
.
-
UINT GetIconPos()
Returns the icon positional flags (refer to SetIcon()
).
-
void SetTexture(HBITMAP hBitmap, bool bSelfDelete = true)
Sets the bitmapped texture that will be used to draw the background when the control's fill flag is set to KCSB_FILL_TEXTURE
(See SetFillFlag()
). The bSelfDelete
flag indicates whether you will delete the HBITMAP
resource (bSelfDelete = false
) or whether the control can delete it when it no longer needs it (bSelfDelete = true
).
-
HBITMAP GetTexture()
Returns the HBITMAP
of the texture that is used to draw the textured background.
Things I'd Like to Try When I Have More Time...
Personally, I have never written a control that "makes space" for itself, and the concept has grasped my imagination somewhat. I have been contemplating the idea of developing a background app/service that monitors all HWND
creations and attaches a banner to all windows of say, type DIALOG. This would merely be for purposes of fun and educational value... but still a cool idea, I think.
History
- 2003-10-22
- 2003-10-23
- Added
#pragma comment(lib, "MSIMG32.LIB")
to the KCSIDEBANNERWND.H file to alleviate the necessity of changing the project settings - Thanks to Warren Stevens.
- Added a release build of the project to the *.zip file download and removed all unnecessary files (e.g.: *.NCB files) - Thanks again to Warren :).
- Fixed a bug with Cyrillic (and hopefully other fonts that may have suffered).
- 2003-10-24
- Added
GetColTxtTitle()
, SetColTxtTitle()
, GetColTxtCaption()
and SetColTxtCaption()
.
- 2003-10-27
- Added ability to provide a bitmap for the background (see
SetTexture()
) .
- Added ability to indicate whether the control can clean up the
HICON
and HBITMAP
resources or whether the controlling program will clean up after itself.
- 2003-10-29
- Removed the total dependence on MSIMG32.LIB. The control now checks if it can dynamically load MSIMG32.DLL and, if not, it will use its own
Gradient
function (thanks to John A. Johnson). This idea was given to me by Dominik Reichl and he got the idea from Irek Zielinski's CStaticGradient control.