用C#和VB.NET实现VS.NET或Office XP风格的菜单(二)
2024-07-10 13:00:26
供稿:网友
用c#和vb.net实现vs.net或office xp风格的菜单
小气的神 2001.08.18
2.“owner-drawn menus”技术
这个例子是vb.net语法的.我去掉了和menu无关的class,原因是错误太多,你会遇到类库和命名空间的移植性的问题:
最多的是beta1 system.winforms 和beta 2 的system.windows.froms的命名空间问题;
然后是beta1中的bitand 、bitor等等bitxxx的函数在beta2中已去掉了bit又和vb中一样了(据说beta1的这项改动遭到了总多vb fans的投诉,说不能把vb也c#化,bit是什么东东),这样你需要把这类函数改掉;
然后是nameobjectcollectionbase从原来的system.collections中删除了,beta2放在system.collections.specialized 中,真的有些昏倒,开始我还以为beta2中删除了这个类。
最后是一些overrides和 overloads的问题,具体的看vs.net或framework sdk beta 2编译时的提示就可以了,这方面ms做得不错,task list中告诉你具体得建议,照做就是了。
具体一点你可以在framework sdk beta 2安装目录的doc目录中找到这两个文件,这是从beta1移植到beta2上不错的指导文件:apichangesbeta1tobeta2.htm 和change list - beta1 to beta2.doc 特别是这个doc文件洋洋洒洒90多页,但很有帮助。
希望你还能在排除所有的错误之后保持清醒,找到最核心有用的代码,来分析。主要是cactionmenu.vb,焦点在onmeasureitem和ondrawitem这两个函数或说事件处理程序上。onmeasureitem主要是处理menuitem的itemheight和itemwidth的,从它传的measureitemeventargs参数数就知道。ondrawitem主要是如何画菜单的问题。关键字overrides表明我们要在子类中重新定义menuitem中的这两个方法。
从56行到58行是onmeasureitem函数:
protected overrides sub onmeasureitem(byval e as system.windows.forms.measureitemeventargs)
if me.action.caption = "-" then
e.itemheight = 5
else
e.itemheight = 20
end if
dim fs as fontstyle
if me.defaultitem = true then fs = fs or fontstyle.bold
dim fnt as new font("tahoma", 8, fs)
dim sf as sizef = e.graphics.measurestring(me.action.caption, fnt)
fnt.dispose()
e.itemwidth = cint(sf.width) + 20
end sub
measureitemeventargs提供4个属性graphis、index、itemheight和itemwidth。me相当于c#或java的this关键字。fnt.dispose()中dispose是一个很有意思的函数调用,在以往的windows编程中象字体、画笔等许多资源都希望快使用快释放,这个语句是用来控制gc(garbage collection)的,意思是我已使用完了这个设备或资源,gc你可以收回了。
从70到146行是有关onitemdraw函数的:
protected overrides sub ondrawitem(byval e as system.windows.forms.drawitemeventargs)
' colors, fonts
dim clrbgicon, clrbgtext, clrtext as color, fs as fontstyle, fnt as font
dim b as solidbrush, p as pen
dim fenabled as boolean = not ctype(e.state and drawitemstate.disabled, boolean)
dim fselected as boolean = ctype(e.state and drawitemstate.selected, boolean)
dim fdefault as boolean = ctype(e.state and drawitemstate.default, boolean)
dim fbreak as boolean = (me.action.caption = "-")
if fenabled and fselected and not fbreak then
clrbgicon = color.silver
clrbgtext = color.white
clrtext = color.blue
fs = fs or fontstyle.underline
else
clrbgicon = color.gray
clrbgtext = color.silver
clrtext = color.black
end if
if not fenabled then
clrtext = color.white
end if
if fdefault then
fs = fs or fontstyle.bold
end if
fnt = new font("tahoma", 8, fs)
' total background (partly to remain for icon)
b = new solidbrush(clrbgicon)
e.graphics.fillregion(b, new [region](e.bounds))
b.dispose()
' icon?
if not me.action.actionlist is nothing then
dim il as imagelist = me.action.actionlist.imagelist
if not il is nothing then
dim index as integer = me.action.image
if index > -1 and index < il.images.count then
dim rect as rectangle = e.bounds
with rect
.x += 2
.y += 2
.width = 16
.height = 16
end with
e.graphics.drawimage(il.images.item(index), rect)
end if
end if
end if
' text background
dim rf as rectanglef
with rf
.x = 18
.y = e.bounds.y
.width = e.bounds.width - .x
.height = e.bounds.height
end with
b = new solidbrush(clrbgtext)
e.graphics.fillregion(b, new [region](rf))
b.dispose()
' text/line
rf.y += 3 : rf.height -= 3
if not fbreak then
b = new solidbrush(clrtext)
e.graphics.drawstring(me.action.caption, fnt, b, rf)
fnt.dispose()
b.dispose()
else
p = new pen(color.black)
rf.y -= 1
e.graphics.drawline(p, rf.x, rf.y, rf.right, rf.y)
p.dispose()
end if
' border
if fenabled and fselected and not fbreak then
p = new pen(color.black)
e.graphics.drawrectangle(p, e.bounds)
p.dispose()
end if
end sub
drawitemeventargs参数给了你和菜单相关的所有环境和信息,它包括6个属性:bounds、font、forecolor、graphics、index、states。如果你以前用过windows下的gdi函数,那一定很熟悉这些函数,不是很复杂只需要你一点点算术知识和美术观点就可以了,如果你是第一次那么在纸上画几个矩形块就可以了理解和做的很好,比起以前tc下的菜单编程容易得多。主要是作者是如何把icon画在菜单上的,然后是根据不同的states表现一下菜单的forecolor, bounds就是菜单项最前面的表示选中等等的小方块。
好了第二部分涉及到了大部分技术细节了,这里你需要关注的是,如何画出来,下一部分我们来看如何画的好看些,象vs.net或office xp那样子。