Оригинал находится здесь. Автор Clayton Todd. орока.
Загрузите обновленную версию статьи с CodeCentral. Обновление предоставляет более широкие возможности и имеет лучший код рисования пункта меню. Оно не включает вертикальный текст, в отличие от этой статьи.

Рис. 1. Пример из обновления
У некоторых компонентов C++Builder 'а есть свойство OwnerDraw. Это позволяет вам устанавливать его значение таким образом, что либо приложение автоматически выполняет отрисовку элемента, либо вы выполняете отрисовку самостоятельно, написав соответствующий код.
У компонента TPopupMenu присутствует это свойство, и, устанавливая его значение в true, мы можем во всплывающем меню рисовать всякие интересные вещи. Это также справедливо для компонента TMenu, но в этой статье я буду говорить только о TPopupMenu. Эта статья демонстрирует создание всплывающего меню с вертикальным текстом и цветной полосой, похожего на главное меню Windows, появляющееся при нажатии кнопки Пуск. Ниже показано, как будет выглядеть ваше меню:

Рис. 2. Внешний вид меню, рассматриваемого в данной статье.
Итак, начинаем. Вот что вам надо сделать:
1. Загрузите проект с Code Central. 2. Создайте новый проект. 3. Добавьте компонент всплывающего меню (PopupMenu). 4. Добавьте компонент списка изображений (ImageList). 5. Установите значение свойства PopupMenu формы в добавленное вами всплывающее меню. 6. Установите значение свойства OwnerDraw всплывающего меню в true . 7. Установите значение свойства Images всплывающего меню в добавленный вами ImageList. 8. Добавьте изображения в ImageList. 9. Добавьте элементы в PopupMenu. 10. Добавьте следующий код в ваши заголовочные файлы:
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;
Добавьте следующий код в закрытую часть класса:
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;
Мы хотим заменить стандартные OnMeasureItem и OnDrawItem нашими собственными обработчиками событий. Эти события происходят при самостоятельной отрисовке пунктов меню.
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) { // необходимо сделать меню шире, чтобы оставить место для вертикального // текста Width += TEXT_SPACE; } //---------------------------------------------------------------------------
Для дополнительной информации о структуре LOGFONT смотрите Windows SDK.
void TForm1::CreateVerticalFont() { ZeroMemory(&VerticalFont, sizeof(VerticalFont)); VerticalFont. lfHeight = -18; VerticalFont. lfEscapement = 900; VerticalFont. lfOrientation = 900; VerticalFont. lfWeight = FW_BOLD; StrPCopy(VerticalFont. lfFaceName, "Arial"); } //---------------------------------------------------------------------------
Поскольку значение свойства OwnerDraw PopupMenu установлено в true, нам необходимо самим выполнять всю отрисовку.
Сначала нам необходимо нарисовать вертикальный текст и цветную полосу - иначе бы статья не называлась "Добавление вертикального текста и цветной полосы во всплывающее меню". Затем нам необходимо нарисовать текст для пунктов меню, нарисовать иконки и прямоугольник выбора для пункта меню и иконки.
У вас есть доступ к свойству Canvas PopupMenu. Давайте начнем рисование:
void __fastcall TForm1::DrawNewItem(TObject *Sender, TCanvas *ACanvas, const TRect &ARect, bool Selected) { TMenuItem *MenuItem = ((TMenuItem*)Sender);
// получаем размеры, необходимые для отметки пункта меню CheckmarkSize = GetSystemMetrics(SM_CXMENUCHECK); MenuHeight = ARect. Height() * MenuItem->Parent->Count; VerticalBarLength = MenuHeight / 4;
// собираемся рисовать вертикальный текст только один раз // тем не менее, если рабочий стол обновляется, то вертикальный текст // исчезает 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; // оставляем место для иконки TempRect. Left += LOWORD(CheckmarkSize)+ICON_SPACE;
ACanvas->Pen->Style = psClear;
// прямоугольник выбора // запустите программу без этого и посмотрите разницу 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(Selected) { // смещаем 2 прямоугольника для объемного вида 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);
// здесь мы извлекаем иконку из 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) { delete Icon; } //---------------------------------------------------------------------------
|