This article will give you an idea of some of the things you can do with a PopupMenu, or Menu component if you wish, when the OwnerDraw property is set to True. I'll get you started with an example of creating a popupmenu with vertical text and a color bar similar to the Windows Start button menu. Take a look at Image 1 to see what the finished project may look like.
Image 1.
Download the project.
Create a new Project
Add a PopupMenu Component
Add a ImageList Component
Set the Forms PopupMenu property to the PopupMenu you added.
Set the PopupMenus OwerDraw property to true
Set the PopupMenus Images property to the ImageList you added
Add some Images to the ImageList
Add some Items to the PopupMenu
Add the following code to you header files.
const TEXT_SPACE = 15;
const ICON_SPACE = 35;
const MENU_TEXT_HEIGHT = 18;
const SPACE_BETWEEN_MENUS = 1;
const MENU_TEXT_LEFT = 2;
const MENU_ITEM_OFFSET = 19;
Add the following code to the private section of you class.
private:
void __fastcall ExpandMenuItemWidth(TObject *Sender, TCanvas *ACanvas,
int &Width, int &Height);
void __fastcall DrawNewItem(TObject *Sender, TCanvas *ACanvas,
const TRect &ARect, bool Selected);
void CreateVerticalFont();
TLogFont VerticalFont;
TIcon *Icon;
TFont *OldFont;
TRect VerticalDrawingRect,
TempRect;
AnsiString VerticalText;
int MenuHeight,
VerticalBarLength;
long int CheckmarkSize,
OldForegroundColor,
OldBackgroundColor;
bool VerticalBarDrawn;
The rest of the code goes in your source file.
void __fastcall TForm1::FormCreate(TObject *Sender)
{
if(PopupMenu1->Items->Count > 0)
{
for(int i=0; i <= PopupMenu1->Items->Count-1; i++)
{
PopupMenu1->Items->Items[i]->OnMeasureItem = ExpandMenuItemWidth;
PopupMenu1->Items->Items[i]->OnDrawItem = DrawNewItem;
}
}
CreateVerticalFont();
Icon = new TIcon;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::PopupMenu1Popup(TObject *Sender)
{
VerticalBarDrawn = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ExpandMenuItemWidth(TObject *Sender, TCanvas *ACanvas, int &Width, int &Height)
{
// We need to make the Menu wider to make room for the vertical font
Width += TEXT_SPACE;
}
//---------------------------------------------------------------------------
Check out the LOGFONT structure in the windows sdk for more options.
void TForm1::CreateVerticalFont()
{
ZeroMemory(&VerticalFont,sizeof(VerticalFont));
VerticalFont.lfHeight = -18;
VerticalFont.lfEscapement = 900;
VerticalFont.lfOrientation = 900;
VerticalFont.lfWeight = FW_BOLD;
StrPCopy(VerticalFont.lfFaceName, "Arial");
}
//---------------------------------------------------------------------------
Since the PopupMenus OwnerDraw property is true we need to do all the drawing. First we need to draw the vertical text and color bar, otherwise this article would not be titled Adding Vertical Text and Color Bar to a PopupMenu. Then we need to draw the actual text for the Menu Item, then draw the Icons, and then we need to draw the selection rectangle for the Menu Item and the Icon.Evil Grin
You have access to the Canvas of the PopupMenu. Let the drawing begin.
void __fastcall TForm1::DrawNewItem(TObject *Sender, TCanvas *ACanvas,
const TRect &ARect, bool Selected)
{
TMenuItem *MenuItem = ((TMenuItem*)Sender);
// We get the default size that is set aside on the menu for the checkmark
CheckmarkSize = GetSystemMetrics(SM_CXMENUCHECK);
MenuHeight = ARect.Height() * MenuItem->Parent->Count;
VerticalBarLength = MenuHeight / 4;
// Only going to draw the vertical text once. However if the desktop refreshes, bye bye vertical text and color bar
if(!VerticalBarDrawn)
{
OldFont = (TFont*)SelectObject(ACanvas->Handle,CreateFontIndirect(&VerticalFont));
OldForegroundColor = SetTextColor(ACanvas->Handle, clWhite);
OldBackgroundColor = SetBkColor(ACanvas->Handle, clRed);
VerticalDrawingRect = Rect(0, 0, CheckmarkSize+TEXT_SPACE, MenuHeight);
VerticalText = VerticalText.StringOfChar(' ',VerticalBarLength);
VerticalText.Insert(" Vertical Text",1);
ExtTextOut(ACanvas->Handle, -1, MenuHeight, ETO_CLIPPED,
&VerticalDrawingRect, VerticalText.c_str(), VerticalBarLength, NULL);
SelectObject(ACanvas->Handle,OldFont);
SetTextColor(ACanvas->Handle, OldForegroundColor);
SetBkColor(ACanvas->Handle, OldBackgroundColor);
VerticalBarDrawn = true;
}
TempRect = ARect;
// Make room for our icon
TempRect.Left += LOWORD(CheckmarkSize)+ICON_SPACE;
ACanvas->Pen->Style = psClear;
// Fix the selection rectangle. Run the program with out this and see the difference.
ACanvas->Rectangle(
TempRect.Left-MENU_TEXT_LEFT,
MenuItem->MenuIndex*MENU_ITEM_OFFSET-SPACE_BETWEEN_MENUS,
ARect.Width(),
MenuItem->MenuIndex*MENU_ITEM_OFFSET+MENU_TEXT_HEIGHT);
DrawText(ACanvas->Handle,MenuItem->Caption.c_str(),MenuItem->Caption.Length(),
&TempRect, 0);
// If the menu item is selected with draw a raised rect around the icon otherwise we erase the raised rectangle
if(Selected)
{
ACanvas->Pen->Style = psSolid;
ACanvas->Pen->Color = clWhite;
ACanvas->Rectangle(
24,
MenuItem->MenuIndex * MENU_ITEM_OFFSET-1,
24+MENU_ITEM_OFFSET,
MenuItem->MenuIndex * MENU_ITEM_OFFSET+MENU_TEXT_HEIGHT-1);
ACanvas->Pen->Color = clGray;
ACanvas->Rectangle(
25,
MenuItem->MenuIndex * MENU_ITEM_OFFSET,
24+MENU_ITEM_OFFSET,
MenuItem->MenuIndex * MENU_ITEM_OFFSET+MENU_TEXT_HEIGHT-1);
// Here is where we retrieve the icon from the ImageList
ImageList1->GetIcon(MenuItem->ImageIndex,Icon);
ACanvas->Draw(26,MenuItem->MenuIndex * MENU_ITEM_OFFSET,Icon);
}
else
{
ACanvas->Pen->Style = psClear;
ACanvas->Rectangle(
24,
MenuItem->MenuIndex * MENU_ITEM_OFFSET-2,
25+MENU_ITEM_OFFSET+2,
MenuItem->MenuIndex * MENU_ITEM_OFFSET+MENU_TEXT_HEIGHT+2);
ImageList1->GetIcon(MenuItem->ImageIndex,Icon);
ACanvas->Draw(26,MenuItem->MenuIndex * MENU_ITEM_OFFSET,Icon);
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
// Clean up after our messy selves
delete Icon;
}
//---------------------------------------------------------------------------