全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:2585
推到 Plurk!
推到 Facebook!

Adding Vertical Text and Color Bar to a PopupMenu

 
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#1 引用回覆 回覆 發表時間:2003-09-06 00:18:07 IP:211.76.xxx.xxx 未訂閱

Adding Vertical Text and Color Bar to a PopupMenu

By Clayton Todd 資料來源:http://community.borland.com/article/0,1410,26632,00.html 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. http://delphi.ktop.com.tw/loadfile.php?TOPICID=11578196&CC=258944 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. We want to replace the default OnMeasureItem and OnDrawItem with our own methods.
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;
}
//---------------------------------------------------------------------------
/*開心的事情別隱藏在心裡,分享給別人知道會更快樂的*/
/*得到新知識別隱藏在心裡,分享給別人了解會更清楚的*/
發表人 - axsoft 於 2003/09/06 00:23:44
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#2 引用回覆 回覆 發表時間:2003-09-06 00:33:21 IP:211.76.xxx.xxx 未訂閱

Owner Drawn Menus - Update

By Clayton Todd 資料來源:同上 This is an update to an article I wrote, "Adding vertical text and a color bar to a popup menu". The previous mentioned article contained many hard coded items and was difficult to customize. This update seeks to make the code easier to customize and better explain some of the possibilities that can be accomplished when you set the OwnerDraw property of a Menu to true. This article does not include any code for drawing vertical text, you can refer to the previous article for that example, but the drawing routines in this article are better then the ones in the previous article. By setting the OwnerDraw property to true we are specifiy that we want to draw all the stuff that makes up the menu items. If you set the OwnerDraw property to true, add some menu items and run your program, your menus will look a little strange. So you need to provided your own code for the OnDrawItem event. You can also provide your own code for the OnMeasureItem event if we want to change the sizes of the menu, and in this article we will be doing that. Getting Started Open up Builder and start a new application. Add a TMainMenu Component and an TImageList Component. Hook up the ImageList to the Menu and the Menu to the Form. Be sure to set the OwnerDraw property of the TMainMenu Component to true. I have included an example project if you wish to use it. The code in this article is based off of it. Place the code below in the header file in its correct place
    const AnsiString BLANK_LINE="-";    private:        // User declarations
   TColor MainMenuBackground;
   TColor MainMenuHighlightColor;
   TColor MainMenuTextColor;
   TColor MainMenuTextBackground;
   TColor MainMenuHighlightTextColor;
   TColor Custom;
   TColor VerticalColor;
   TColor MenuColor;
   TColor HighlightColor;
   TColor BorderColor;
   TColor BorderEraseColor;
   TColor NormalTextColor;
   TColor NormalTextBackground;
   TColor HighlightTextColor;
   TColor DisabledTextColor;
   int VerticalWidth;
   int FocusRectRightIndent;
   int FocusRectLeftIndent;
   int LeftTextPos;
   int SideBuffer;
   int MenuIncreaseWidth;
   int Offset;       int MenuItemHeight;
   int ItemOffset;
   TIcon *Icon;
protected:
   void __fastcall MyExpandItemWidth(TObject *Sender, TCanvas *ACanvas,
      int &Width, int &Height);
   void __fastcall MyDrawItem(TObject* Sender, TCanvas* ACanvas,
   const TRect &ARect, bool Selected);        -------------------------------------------------------------------------------- 
We need to make sure that all the menu items under a main menu item have their OnDrawItem, and OnMeasureItem customized. We want to set it so when you click on the main menu item all the menu items under it have those two events set to our customized events. This example will go one sub-menu deep in setting the events. If you have more sub-menus you can easily adjusted it to go into deeper levels. Recursion should also work and reduce manually handling more and more sub-menus. You will have to share this OnClick with any other main menu bar items or write seperate OnClick handlers for each. By writing seperate ones you could make the menu items of 2 different main menu items act completely different or as I do here just use the same code for all the menu items. Example of sub-menus. This will assign our customized event handlers to the OnMeasureItem and OnDrawItem of all the menu items. If you want to restore the default handlers during runtime you will have to save them which is done the same way that we reassign them. By saving them you can turn OwnerDraw on and off during runtime and restore the old event handlers.
    void __fastcall TForm1::Example1Click(TObject *Sender)
{       TMenuItem *MenuItem = ((TMenuItem*)Sender);
   if(MenuItem->Count > 0)
   {
      for(int i=0; i <= MenuItem->Count-1; i++)
      {
         MenuItem->Items[i]->OnMeasureItem = MyExpandItemWidth;
         MenuItem->Items[i]->OnDrawItem = MyDrawItem;
         if(MenuItem->Items[i]->Count > 0)
         {
            for(int x=0; x <= MenuItem->Items[i]->Count-1; x++)
            {
               MenuItem->Items[i]->Items[x]->OnMeasureItem = MyExpandItemWidth;
               MenuItem->Items[i]->Items[x]->OnDrawItem = MyDrawItem;
            }
         }
      }
   }
}        --------------------------------------------------------------------------------
You can customize how the menu bar items are drawn by adding our own drawing code to the OnDrawItem event for the main menu items. If you have other items on the menu bar you can set them to all share the same code if you want them to all be drawn the same or provide customized drawing code for each one based on how you want to draw them. I usually share the event handler with all the main menu items so the drawing is consistant and code is simpler.
    void __fastcall TForm1::Example1DrawItem(TObject *Sender, TCanvas *ACanvas,
      TRect &ARect, bool Selected)
{
   TRect FocusRectBorder;
   TRect FocusRectFill;
   TMenuItem *MenuItem = ((TMenuItem*)Sender);       AnsiString Text = MenuItem->Caption;       // Color the background behind the text
   ACanvas->Brush->Color = MainMenuBackground;
   ACanvas->FillRect(ARect);       // For any blank items acting as seperators
   if(Text == "")
      return;       if(Selected)
   {
      // Draw the outline of the selection box
      FocusRectBorder = ARect;
      ACanvas->Brush->Color = BorderColor;
      ACanvas->FrameRect(FocusRectBorder);          // Draw the inside of the selection box
      // Make is a little smaller so it does not erase the outline
      FocusRectFill = ARect;
      FocusRectFill.Top += SideBuffer;
      FocusRectFill.Right -= SideBuffer;
      FocusRectFill.Left += SideBuffer;
      FocusRectFill.Bottom -= SideBuffer;
      ACanvas->Brush->Color = MainMenuHighlightColor;
      ACanvas->FillRect(FocusRectFill);          // Set the color that we want our text drawn when the item is selected
      ACanvas->Font->Color = MainMenuHighlightTextColor;
   }
   else
   {
      ACanvas->Font->Color = MainMenuTextColor;
   }       int TextLength;
   TRect TextRect;       TextLength = Text.Length();
   TextRect = ARect;
   // This determines where the text is drawn.
   TextRect.Left += 5;
   TextRect.Top += 1;       // Draw the text
   DrawText(ACanvas->Handle,Text.c_str(), TextLength, &TextRect, 0);    }        --------------------------------------------------------------------------------
Inside our custom OnMeasureItem event handler we can change the Height and Width of the menu.
    void __fastcall TForm1::MyExpandItemWidth(TObject *Sender,
   TCanvas *ACanvas, int &Width, int &Height)
{
   Width += MenuIncreaseWidth;
   Height += Offset;
   MenuItemHeight = Height;
   ItemOffset = Offset/2;
}        --------------------------------------------------------------------------------
Now for each one of the Menu Items we are going to draw them how ever we wish. The Canvas you get access is the area of the menu item you are currently drawing. So you could set each menu item to have different backgrounds, vertical color bars, etc... You could load a bitmap file and draw it to the canvas with either Draw or Stretch Draw. This way you could have large custom image backgrounds.
    void __fastcall TForm1::MyDrawItem(TObject* Sender, TCanvas* ACanvas,
   const TRect &ARect, bool Selected)
{
   int TopPos, TextLength;
   AnsiString Text;
   TRect TempRect;
   TRect VerticalRect;
   TRect FocusRectBorder;
   TRect FocusRectFill;
   TRect TextRect;       TMenuItem *MenuItem = ((TMenuItem*)Sender);       Text = MenuItem->Caption;       // Draw the Background erases anything that was there before.
   ACanvas->Brush->Color = MenuColor;
   ACanvas->FillRect(ARect);       // Draw any seperator lines
   if(Text==BLANK_LINE)
   {
      // Draw the Vertical Bar
      VerticalRect = ARect;
      VerticalRect.Top -= SideBuffer;
      VerticalRect.Right = VerticalWidth;
      VerticalRect.Bottom += SideBuffer;
      ACanvas->Brush->Color = VerticalColor;
      ACanvas->FillRect(VerticalRect);          // Draw the Blank Line
      ACanvas->MoveTo(VerticalWidth,ARect.Top+ARect.Height()/2);
      ACanvas->LineTo(ARect.Right,ARect.Top+ARect.Height()/2);
      return;
   }       // This is for non seperator lines       TextLength = Text.Length();       if(Selected)
   {
      // Have to draw the vertical bar section to fill in the area that is not
      // covered by the selection rect
      VerticalRect = ARect;
      VerticalRect.Top -= SideBuffer;
      VerticalRect.Right = VerticalWidth;
      VerticalRect.Bottom += SideBuffer;
      ACanvas->Brush->Color = VerticalColor;
      ACanvas->FillRect(VerticalRect);          if(MenuItem->Enabled)
      {
         // The item is selected and enabled             // Draw the focus rect outline border
         FocusRectBorder = ARect;
         FocusRectBorder.Left += FocusRectLeftIndent - SideBuffer;
         FocusRectBorder.Right -= FocusRectRightIndent - SideBuffer;
         ACanvas->Brush->Color = BorderColor;
         ACanvas->FrameRect(FocusRectBorder);             // Fill in the focus rect.  Making it a little smaller so as to not
         // draw over the outline
         FocusRectFill = ARect;
         FocusRectFill.Right -= FocusRectRightIndent;
         FocusRectFill.Left += FocusRectLeftIndent;
         FocusRectFill.Bottom -= SideBuffer;
         FocusRectFill.Top += SideBuffer;
         ACanvas->Brush->Color = HighlightColor;
         ACanvas->FillRect(FocusRectFill);             // Set the way we want to draw our text
         ACanvas->Font->Color = HighlightTextColor;
         ACanvas->Font->Style = TFontStyles() << fsBold;
      }
      else
      {
         // Menu item is not enabled so do not draw any selection
         // rect and change the text color to reflect its unenabled state
         // NOTE: The icon is still drawn normally and not disabled
         ACanvas->Font->Style = TFontStyles();
         ACanvas->Brush->Color = NormalTextBackground;
         ACanvas->Font->Color = DisabledTextColor;
      }       }
   else
   {
      // Fill in the vertical area
      VerticalRect = ARect;
      VerticalRect.Top -= SideBuffer;
      VerticalRect.Right = VerticalWidth;
      VerticalRect.Bottom += SideBuffer;
      ACanvas->Brush->Color = VerticalColor;
      ACanvas->FillRect(VerticalRect);          // Set the text background color and font based on if it is enabled or not          if(MenuItem->Enabled)
      {
         ACanvas->Brush->Color = NormalTextBackground;
         ACanvas->Font->Color = NormalTextColor;
      }
      else
      {
         ACanvas->Brush->Color = NormalTextBackground;
         ACanvas->Font->Color = DisabledTextColor;
      }       }       // Calculate out the Rect we want to draw our text in
   TextRect = ARect;
   TextRect.Left += LeftTextPos;
   if(Offset > 0)
      TextRect.Top += Offset/2 + SideBuffer;
   else
      TextRect.Top += 2 + SideBuffer;       TextRect.Top += SideBuffer;       // Draw any menu item icons
   if(Menu->Images != NULL)
   {
      Icon = new TIcon();
      Menu->Images->GetIcon(MenuItem->ImageIndex,Icon);
      ACanvas->Draw(5,ARect.Top+ItemOffset+1,Icon);
      delete Icon;
   }       // Draw the text
   DrawText(ACanvas->Handle,Text.c_str(), TextLength, &TextRect, 0);
}        --------------------------------------------------------------------------------
Now we have the menus drawing, but what if we want the width of the menu to larger, or the height, or different colors. Unlike the first article all the variables have been setup so that you can change them in one place and it works for the whole program. Example of Offset = 25;
    void __fastcall TForm1::FormCreate(TObject *Sender)
{
   // Custom light blue color
   Custom=TColor(RGB(185,239,245));       // Used to draw and highlight the main menu items
   MainMenuBackground = clSilver;
   MainMenuHighlightColor = Custom;
   MainMenuTextColor = clBlack;
   MainMenuTextBackground = clSilver;
   MainMenuHighlightTextColor = clRed;       // Used to draw and color the menu items
   VerticalColor = clSilver;
   MenuColor = clWhite;
   HighlightColor = Custom;
   BorderColor = clBlack;
   NormalTextColor = clBlack;
   NormalTextBackground = clWhite;
   HighlightTextColor = clBlack;
   DisabledTextColor = clSilver;       // Width of the vertical bar on the right
   VerticalWidth = 26;       // Space buffer between the sides of the menu and the sides of the focus rect
   FocusRectRightIndent = 3;
   FocusRectLeftIndent = 3;       // Position to draw the text
   LeftTextPos = 35;       // Space between the focus rect outline and the focus rect
   SideBuffer = 1;       // Assign an image list makes the width wider, so we need to adjust when you do and don't have one.
   if(Menu->Images == NULL)
      MenuIncreaseWidth = 100;
   else
      MenuIncreaseWidth = 50;       // Offset will increase the Height of the menu items.  Also used to make sure the icons are aligned correctly.
   Offset = 5;
}        --------------------------------------------------------------------------------
I mentioned earlier about drawing larger bitmaps to the menu items canvas. In the OnMeasureItem you could have something like the following. I have seen some commercial menu components that allow bitmap backgrounds, the code below shows how easy you can do it also. if(Image == NULL) Image = new Graphics::TBitmap(); Image->LoadFromFile("menupic.bmp"); // We need to subtract the width of the images in an ImageList if one is being used. Width = Image->Width-Menu->Images->Width; Height = Image->Height; Then in the OnDrawItem you can simply draw the image to the canvas. ACanvas->Draw(0,0,Image); Doesn't cover everything, but should get you started.
/*開心的事情別隱藏在心裡,分享給別人知道會更快樂的*/
/*得到新知識別隱藏在心裡,分享給別人了解會更清楚的*/
發表人 - axsoft 於 2003/09/08 09:59:19
系統時間:2024-11-22 16:35:03
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!