首页 > 编程 > .NET > 正文

用C#和VB.NET实现VS.NET或Office XP风格的菜单(三)

2024-07-10 13:00:25
字体:
来源:转载
供稿:网友
用c#和vb.net实现vs.net或office xp风格的菜单

小气的神 2001.08.18

3. “menuitemstyle”接口和vs.net风格的菜单项



这个project又将切换到c#语言。我是这样想的:先针对普通菜单、office200风格、vs.net风格三种情况定义一个统一的接口(interface),其中包括画icon(drawicon)、画分割条(drawseparator)、画菜单背景(drawbackground)、写菜单项的文字(drawmenutext)等功能;普通、office2000和vs.net根据各自不同的情况实现这个接口的drawxxx的功能。然后从menuitem继承一个子类,象第二部分讲的那样overrides 菜单项的两个函数:onmeasureitem和ondrawitem,根据不同的风格调用上面实现的接口中的drawxxx函数就可以了。最后我把这部分都分隔出来放在一个.cs文件中,单独编译成一个vsnet.menu.dll,你只用using vsnet.menu ; 然后就可以象在第一部分那样象使用普通的menuitem那样来用了,demo源代码中你还可以看到我定义了iconmenuitem的类,它有一个方法:menuitemcreator(vsnet.menu.iconmenustyle stype , string stext , bitmap bmp , system.eventhandler eh)可以完成生成需要的menuitem。本来我想用资源文件或将图片icon等资源放在一个专门的文件中,然后由这个类来负责从资源文件或外部的类中获得资源createmenuitem。但是是第一版,你会看到例程中我仍然用原始的new bitmap()的方式直接从硬盘拿资源。当我看到它show出来时,先是很开心,然后发现还有许多要改进,想想其实做一个专业的菜单也需要花许多心思。

好吧让我们看一下有关vs.net风格菜单项这部分主要的实现代码:



public class vsnetstyle : menuitemstyledrawer

{

static color bgcolor = color.fromargb(246, 246, 246);

static color ibgcolor = color.fromargb(202, 202, 202);

static color sbcolor = color.fromargb(173, 173, 209);

static color sbbcolor = color.fromargb( 0, 0, 128);



static int textstart = 20;



public void drawcheckmark(graphics g, rectangle bounds, bool selected)

{

controlpaint.drawmenuglyph(g, new rectangle(bounds.x + 2, bounds.y + 2, 14, 14), menuglyph.checkmark);

}



public void drawicon(graphics g, image icon, rectangle bounds, bool selected, bool enabled, bool ischecked)

{

if (enabled)

{

if (selected)

{

controlpaint.drawimagedisabled(g, icon, bounds.left + 2, bounds.top + 2, color.black);

g.drawimage(icon, bounds.left + 1, bounds.top + 1);

}

else

{

g.drawimage(icon, bounds.left + 2, bounds.top + 2);

}

}

else

controlpaint.drawimagedisabled(g, icon, bounds.left + 2, bounds.top + 2, systemcolors.highlighttext);

}



public void drawseparator(graphics g, rectangle bounds)

{

int y = bounds.y + bounds.height / 2;

g.drawline(new pen(systemcolors.controldark), bounds.x + systeminformation.smalliconsize.width + 7, y, bounds.x + bounds.width - 2, y);

}



public void drawbackground(graphics g, rectangle bounds, drawitemstate state, bool toplevel, bool hasicon)

{

bool selected = (state & drawitemstate.selected) > 0;



if (selected || ((state & drawitemstate.hotlight) > 0))

{

if (toplevel && selected)

{ // draw toplevel, selected menuitem

g.fillrectangle(new solidbrush(ibgcolor), bounds);

controlpaint.drawborder3d(g, bounds.left, bounds.top, bounds.width, bounds.height, border3dstyle.flat, border3dside.top | border3dside.left | border3dside.right);

}

else

{ // draw menuitem, selected or toplevel, hotlighted

g.fillrectangle(new solidbrush(sbcolor), bounds);

g.drawrectangle(new pen(sbbcolor), bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);

}

}

else

{

if (!toplevel)

{ // draw menuitem, unselected

g.fillrectangle(new solidbrush(ibgcolor), bounds);

bounds.x += systeminformation.smalliconsize.width + 5;

bounds.width -= systeminformation.smalliconsize.width + 5;

g.fillrectangle(new solidbrush(bgcolor), bounds);

}

else

{

// draw toplevel, unselected menuitem

g.fillrectangle(systembrushes.menu, bounds);

}

}

}



public void drawmenutext(graphics g, rectangle bounds, string text, string shortcut, bool enabled, bool toplevel, drawitemstate state)

{

stringformat stringformat = new stringformat();

stringformat.hotkeyprefix = ((state & drawitemstate.noaccelerator) > 0) ? hotkeyprefix.hide : hotkeyprefix.show;

int textwidth = (int)(g.measurestring(text, systeminformation.menufont).width);



int x = toplevel ? bounds.left + (bounds.width - textwidth) / 2: bounds.left + textstart;

int y = bounds.top + 2;

brush brush = null;

if (!enabled)

brush = new solidbrush(color.fromargb(120, systemcolors.menutext));

else

brush = new solidbrush(color.black);

g.drawstring(text, systeminformation.menufont, brush, x, y, stringformat);

g.drawstring(shortcut, systeminformation.menufont, brush, bounds.left + 130, bounds.top + 2, stringformat);

}

}



menuitemstyledrawer就是那个公用的接口类,无论普通风格、office2000还是vs.net风格都要实现自己方式的接口,这个接口包括drawcheckmark、drawicon、drawmenutext、drawbackground、drawseparator等函数,可以实现菜单项需要的各种函数。完成这部分后可以从menuitem继承一个子类来象第二部分一样处理了。看下面的代码,具体考察一下熟悉的onmeasureitem和ondrawitem:

protected override void onmeasureitem(measureitemeventargs e)

{

base.onmeasureitem(e);



// make shortcut text 省略这部分代码。

if (menustyle != iconmenustyle.standard)

{

if (text == "-")

{

e.itemheight = 8;

e.itemwidth = 4;

return;

}

int textwidth = (int)(e.graphics.measurestring(text + shortcuttext, systeminformation.menufont).width);

e.itemheight = systeminformation.menuheight;

if (parent == parent.getmainmenu())

e.itemwidth = textwidth - 5; // 5 is a magic number

else

e.itemwidth = math.max(160, textwidth + 50);

}

}



iconmenustyle.standard是个enum表明是普通风格、office2000或是vs。net的风格。这部分和我们第二部分看到的没有什么不同。

protected override void ondrawitem(drawitemeventargs e)

{

base.ondrawitem(e);

graphics g = e.graphics;

rectangle bounds = e.bounds;

bool selected = (e.state & drawitemstate.selected) > 0;

bool toplevel = (parent == parent.getmainmenu());

bool hasicon = icon != null;



style.drawbackground(g, bounds, e.state, toplevel, hasicon);

if (hasicon)

style.drawicon(g, icon, bounds, selected, enabled, checked);

else

if (checked)

style.drawcheckmark(g, bounds, selected);



if (text == "-")

{

style.drawseparator(g, bounds);

}

else

{

style.drawmenutext(g, bounds, text, shortcuttext, enabled, toplevel, e.state);

}

}



刚刚我们说的menuitemstyledrawer接口的好处在这里显示出来,整个过程显得简单明了,具体实现得代码不是很多。当这个类完成后,剩下来的就是使用了它了,这部分象第一部分所述,你可以在一个顶级菜单项的子菜单项声明成iconmenu类型的也就是我们实现的继承menuitem的类,简单的代码象下面这样:

private system.windows.forms.menuitem mitems1 ; system.drawing.bitmap bitmap1 = new bitmap( bmppathstr + "open.bmp") ;



mitems1 = imenuitem.menuitemcreator( menustyle , "&open" , bitmap1,

这个mitem1就是一个vs.net风格的菜单项了。具体的可以看附带的project和屏幕截图。



至此我们完成了用vb.net或c#完成一个有vs.net或office xp风格的菜单。三个部分是渐进的,如果你以前进行或实验过第二部分讨论的问题,那么第三部分唯一让人感兴趣的是menuitemstyledrawer接口的思路。对于整个新的.net的编程方式上说,原来的vb用户可能会经历一个痛苦的过程,他们的第一反应是sub class、hook或是终极的api,而接触过c++、mfc、delphi甚至vj++的用户会很容易想到继承,特别时delphi和vj的用户入手应当最快了。想想会开始怀念以前vb的时光,因为对于这样的问题,vb用户总是拿着大锤,直接敲个大洞,然后拿到结果;而c++、mfc、dephi用户则拿着一本说明书,一步一步按指示找到结果;结果可能一样,但两者的方式是截然不同的。好了,这些是题外话了。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表