DELPHI的奇异菜单的编写
翻译者: 李均宇 email: e271828@163.net,okMyDelphi@163.net
Custom Menus, Text, Lines / Delphi 4, 5 自定义菜单,文本,线/ Delphi 4, 5 Fancy Menus, etc. 奇异菜单,等等 Custom Menus, Rotated Text, and Special Lines 自定义菜单,旋转文本,和特殊的线条
Before Delphi 4, it was difficult to customize a menu (add a bitmap, change a font, etc.), because owner drawing (i.e. custom drawing) - although implemented by Windows - was not exposed by the TMainMenu class. Since Delphi 4, however, this situation has been rectified, and we can have our way with menus. 在Delphi 4之前,要想自定义一个菜单是困难的(例如加上一个BMP图像,改变字体等),因为owner drawing事件(也就是custom drawing事件)-虽然是由Windows来执行,但是却并不在TMainMenu class中出现.自从Delphi 4开始后, 这种情况有了改变,我们于是有了可以自定义菜单的功能了.
This article will highlight some techniques you can use to customize the appearance of menus in your Delphi applications. We''''ll discuss text placement, menu sizing, font assignment, and using bitmaps and shapes to enhance a menu''''s appearance. Just for fun, this article also features techniques for creating rotated text and custom lines. All of the techniques discussed in this article are demonstrated in projects available for download。 这篇文章将主要着重论述可以用来自定义你的DELPHI应用程序中的菜单的外形的一些技术巧.我们将论述文本的放置,菜单的大小,字体的设置,以及用BMP文件和SHAPE控件来加强菜单的显示效果。仅仅出于娱乐的目的,这篇文章也将对旋转的文本和自定义线条的技巧进行特写。这篇文章所论述到的所有技巧都已在工程文件中通过了调试并且可以到网上下载这些工程文件。 Custom Fonts and Sizes 设置字体和大小 To create a custom menu, set the OwnerDraw property of the menu component -TMainMenu or TPopupMenu - to True, and provide event handlers for its OnDrawItem and OnMeasureItem events. For example, an OnMeasureItem event handler is declared like this: 为了创建一个自定义的菜单,将TmainMenu或TpopupMenu组件的OwnerDraw属性设为TRUE,并且创建它的OnDrawItem和OnMeasureItem的事件过程。例如,一个OnMeasureItem事件过程可以声明如下:
procedure TForm1.Option1MeasureItem(Sender: TObject; ACanvas: TCanvas; var Width, Height: Integer);
Set the Width and Height variables to adjust the size of the menu item. The OnDrawItem event handler is where all the hard work is done; it''''s where you draw your menu and make any special settings. To draw the menu option with Times New Roman font, for example, you should do something like this: 设置上面事件过程中的菜单项的Width 和Height变量到合适的大小.所有主要的事情都要由OnDrawItem事件来触发;它是你要重画菜单和作任何特殊设置的地方。举例,为了用Times New Roman字体来重画菜单项,你可以如下面这样做:
procedure TForm1.Times1DrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect; Selected: Boolean); begin ACanvas.Font.Name := ''''Times New Roman''''; ACanvas.TextOut(ARect.Left+1, ARect.Top+1, (Sender as TMenuItem).Caption); end;
This code is flawed, however. If it''''s run, the menu caption will be drawn aligned with the left border of the menu. This isn''''t default Windows behavior; usually, there''''s a space to put bitmaps and checkmarks in the menu. Therefore, you should calculate the space needed for this checkmark with code like that shown in Figure 1. Figure 2 shows the resulting menu. 然而这段代码是有缺陷的。如果运行这段代码,菜单项的标题(caption)会在菜单项中靠左对齐.这并不是Windows的默认行为,通常,在菜单左边那儿有一个空间用来放置BMP图像和选择标志的。因此,你应该用代码计算要多少空间来放置这个选择标志的,就象Figure 1中显示的那样。Figure 2显示的是菜单的运行效果。
procedure TForm1.Times2DrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect; Selected: Boolean); var dwCheck : Integer; MenuCaption : string; begin // Get the checkmark dimensions. 获取选择标志所需的像素数 dwCheck := GetSystemMetrics(SM_CXMENUCHECK); // Adjust left position. 调整左边位置 ARect.Left := ARect.Left + LoWord(dwCheck) + 1; MenuCaption := (Sender as TMenuItem).Caption; // The font name is the menu caption.
ACanvas.Font.Name := ''''Times New Roman''''; // Draw the text. 画文本 DrawText(ACanvas.Handle, PChar(MenuCaption), Length(MenuCaption), ARect, 0); end;
Figure 1: This OnDrawItem event handler places menu item text correctly. [译者省略掉所有的FigureS,以下同样] Figure 2: A menu drawn with custom fonts.
If the text is too large to be drawn in the menu, Windows will cut it to fit. Therefore, you should set the menu item size so all the text can be drawn. This is the role of the OnMeasureItem event handler shown in Figure 3. 如果文本太长,Windows会自动裁剪长度来合适。因此,你应该设置菜单大小使所有的文本都可以显示出来。在OnMeasureItem事件中也应如此,这在Figure 3可以看到。
procedure TForm1.Times2MeasureItem(Sender: TObject; ACanvas: TCanvas; var Width, Height: Integer); begin ACanvas.Font.Name := ''''Times New Roman''''; ACanvas.Font.Style := []; // The width is the space of the menu check 这个长度是菜单的选择标志的长度 // plus the width of the item text. 再加上菜单项的长度 Width := GetSystemMetrics(SM_CXMENUCHECK) + ACanvas.TextWidth((Sender as TMenuItem).Caption) + 2; Height := ACanvas.TextHeight( (Sender as TMenuItem).Caption) + 2; end;
Figure 3: This OnMeasureItem event handler insures that an item fits in its menu.
Custom Shapes and Bitmaps 设置图形和位图 It''''s also possible to customize menu items by including bitmaps or other shapes. To add a bitmap, simply assign a bitmap file to the TMenuItem.Bitmap property - with the Object Inspector at design time, or with code at run time. To draw colored rectangles as the caption of a menu item, you could use the OnDrawItem event handler shown in Figure 4. Figure 5 shows the result. 用位图和其它图形来设置菜单是可能的事.要想添加一个位图,只需在设计时简单地在Object Inspector中把一个BMP文件赋给TmenuItem的Bitmap属性即可,或者运行时用代码赋值也可以。要想用一个有颜色的矩形来代替菜单标题,你可以使用OnDrawItem事件,例如在Figure 4中显示的那样。在Figure 5中显示的是结果。
procedure TForm1.ColorDrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect; Selected: Boolean); var dwCheck : Integer; MenuColor : TColor; begin // Get the checkmark dimensions. dwCheck := GetSystemMetrics(SM_CXMENUCHECK); ARect.Left := ARect.Left + LoWord(dwCheck); // Convert the caption of the menu item to a color. 将菜单项的标题转换为颜色 MenuColor := StringToColor((Sender as TMenuItem).Caption); // Change the canvas brush color. 改变画布canvas的画笔颜色 ACanvas.Brush.Color := MenuColor; // Draws the rectangle. If the item is selected, 画矩形,如果菜单项是被选择的 // draw a border. 画边框 if Selected then ACanvas.Pen.Style := psSolid else ACanvas.Pen.Style := psClear; ACanvas.Rectangle(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom); end;
Figure 4: Using the OnDrawItem event to draw colored rectangles on menu items. Figure 5: A menu featuring colored rectangles as items.
There''''s just one catch. If you''''re using Delphi 5, you must set the menu''''s AutoHotkeys property to maManual. If you leave it as the default, maAutomatic, Delphi will add an ampersand character (&) to the caption, which will break this code. Another solution is to remove the ampersand with the StripHotKey function. 比较流行的做法是,如果你用的是Delphi 5,你应设置菜单的AutoHotkeys属性为maManual。如果你不这样做,而让缺省值maAutomatic留着,Delphi会自动添加一个&号给标题,这将破坏这些代码。另一个解决办法是用StripHotKey函数来移去&号。 Another way to use the OnDrawItem and OnMeasureItem events is to write text vertically on a menu (as shown in Figure 7). To do this, you must create a rotated font. This is only possible using the Windows API function CreateFont or CreateLogFont (see the "Rotated Text" tip later in this article). Then you must draw it in the OnDrawItem event handler. This event is fired every time a menu item is drawn, so if a menu has 20 items, it will be drawn 20 times. To make it faster, the vertical text will be drawn only when the menu item is selected (since there''''s is only one menu item selected at a time). Figure 6 shows how this is implemented with code, and Figure 7 shows the run-time result. OnDrawItem和OnMeasureItem事件的另一个用途是用来在菜单侧旁写垂直的文字(例如在Figure 7显示的那样)。为了做到这样,你必须创建一个旋转的字体。唯一办法是用Windows API的CreateFont或者CreateLogFont函数(稍后看本文中的“旋转的文字”技巧)。于是你必须在OnDrawItem事件中重画它。这个事件在菜单项被拉出时执行,所以如果一个菜单有20项,那么它将被执行20次。为了使它快些,这垂直的文字可以在菜单项被选择时才重画一次(虽然每次只有一个菜单项被选择)。Figure 6显示的是代码如何执行,而Figure 7显示的是运行结果。
procedure TForm1.VerticalDrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect; Selected: Boolean); var lf : TLogFont; OldFont : HFont; clFore, clBack : LongInt; Rectang : TRect; dwCheck : LongInt; MenuHeight : Integer; begin dwCheck := GetSystemMetrics(SM_CXMENUCHECK); // This will be done once, when the item is selected. 当菜单项被选中时,这将被执行 if Selected then begin // Create a rotated font. 创建一个旋转的字体 FillChar(lf, SizeOf(lf), 0); lf.lfHeight := -14; lf.lfEscapement := 900; lf.lfOrientation := 900; lf.lfWeight := Fw_Bold; StrPCopy(lf.lfFaceName, ''''Arial''''); // Select this font to draw. 选取这个字体来画 OldFont := SelectObject(ACanvas.Handle, & [1] [2] [3] 下一页 没有相关教程
|