一步一步自绘listbox实现html中的伸缩列表导航栏功能
在html中伸缩导航栏, 用的应该是比较常见的控件,一般放置软件左侧, 诸如下面图片中的形式



前面写过自绘listbox实现音乐播放器界面, 这次参照上次的理论和代码改造为伸缩导航栏
代码和库已经封装完毕, 需要下载的请看
楼下 2 楼代码
如果代码有更新 , 会继续盖楼更新, 请持续关注
2021-08-29更新:
增加选中状态的持续保持功能.
2021-8-8 22:11再次更新:
将oncommand改为onSelChange , 避免在鼠标点击其他界面后产生焦点问题.
将去除焦点框改为0x10/*_ODS_FOCUS*/.
2021-8-7 20:20 增加默认展开与否设置, 增加了颜色设置, 删除焦点框, 优化了下代码.
简单的封装了个库, 下面是库和调用示例:

exListbox.aardio 库代码:
import fonts.fontAwesome;
class exListbox{
ctor( winform , MenuList , itemHeight,bgcolor=0xa0a0a0,headColor=0x302D29,subColor=0x3F3C38,textColor=0xD1D1D1,selColor=0x6E6B67 ){
winform.bgcolor = bgcolor;
this.seltext = null;
winform.onMeasureItem = function(measureItem){
measureItem.itemHeight = itemHeight;
}
winform.onDrawItem = function(drawItem){
..gdi.selectBrush(
function(hdc,pen,brush){
var rc = drawItem.rcItem;
var alltext = winform.getItemText(drawItem.itemID + 1);
var textTab = ..string.split(alltext,"<|>");
var text = textTab[2];
var font = ::LOGFONT(weight=400;color=textColor);
var indexAll = textTab[1];
var head = ..string.split(indexAll,"<.>");
if(head[[2]]!=null){
if (drawItem.itemState & 1/*_ODS_SELECTED*/ or alltext == this.seltext) {
..gdi.fillRect(hdc,selColor,rc);
}else {
..gdi.fillRect(hdc,subColor,rc);
}
}else {
..gdi.fillRect(hdc,headColor,rc);
}
if(head[[2]]!=null){
..gdi.textOut(hdc,font,text,rc.left+60, rc.top+10);
var subico = ..string.unescape(textTab[3]);
var font = ::LOGFONT(color=textColor;h=-18;name='FontAwesome');
..gdi.textOut(hdc,font,subico,rc.left+30, rc.top+10);
}else {
..gdi.textOut(hdc,font,text,rc.left+40, rc.top+10);
var subico = ..string.unescape(textTab[3]);
var font = ::LOGFONT(color=textColor;h=-18;name='FontAwesome');
..gdi.textOut(hdc,font,subico,rc.left+10, rc.top+10);
var ICO = '\uF0D7';
var font = ::LOGFONT(color=textColor;h=-18;name='FontAwesome');
var index = tonumber(head[1]);
if (MenuList[index].展开==true) {
ICO = '\uF0D8';
}
..gdi.textOut(hdc,font,ICO,rc.right-30, rc.top+10);
}
if (drawItem.itemState & 0x10/*_ODS_FOCUS*/) {
//去掉焦点框
::DrawFocusRect(hdc,rc);
}
},drawItem.hDC,0xa0a0a0/*背景色*/,0xa0a0a0/*画笔色*/)
}
for(k,v in MenuList){
if(v.展开){
for(j=1;#v;1){
winform.add(v[j]);
}
}else {
winform.add(v[1]);
}
}
winform.onSelChange = function(){
var selindex = winform.selIndex;
if(selindex){
var name = winform.getItemText(selindex);
var textTab = ..string.split(name,"<|>");
var indexAll = textTab[1];
var head = ..string.split(indexAll,"<.>");
var index = tonumber(head[1]);
var subindex = tonumber(head[2]);
var count = #MenuList[index]-1;
if(head[[2]]==null){
if( MenuList[index].展开 ){
for(j=count;1;-1){
winform.delete(selindex+j);
}
MenuList[index].展开 = false;
}else {
for(j=1;count;1){
winform.add(MenuList[index][1+j],selindex+j);
}
MenuList[index].展开 = true;
}
}else {
//设置更新区,并更新
var lastIndex = winform.find(this.seltext);
this.seltext = winform.selText;
if(lastIndex){
winform.invalidate(winform.getItemRect(lastIndex));
winform.update();
}
if(this.onClick) this.onClick(index,subindex,textTab[2]);
}
}
}
};
}使用的时候, 放到lib文件夹里, 然后调用 ,
注意: 手动去开启listbox的自绘属性
mainform.aardio
import win.ui;
/*DSG{{*/
mainForm = win.form(text="aardio工程15";right=959;bottom=591)
mainForm.add(
button={cls="button";text="Button";left=424;top=224;right=872;bottom=310;z=2};
edit={cls="edit";text="Edit";left=403;top=348;right=751;bottom=414;edge=1;multiline=1;z=3};
listbox={cls="listbox";left=0;top=0;right=163;bottom=592;items={};ownerDraw=1;z=1}
)
/*}}*/
import console
console.open()
var MenuList = {
{
"1|应用管理|\uF013";
"1.1|应用_demo1|\uF014";
"1.2|应用_demo2|\uF015";
"1.3|应用_demo3|\uF016";
"1.4|应用_demo4|\uF017";
展开 = false;
},
{
"2|协议管理|\uF0E8";
"2.1|协议_demo1|\uF0E9";
"2.2|协议_demo2|\uF0EA";
"2.3|协议_demo3|\uF0EB";
展开 = true;
},
{
"3|日志管理|\uF039";
"3.1|日志_demo1|\uF040";
"3.2|日志_demo2|\uF041";
"3.3|日志_demo3|\uF042";
"3.4|日志_demo4|\uF043";
展开 = false;
}
{
"4|协议管理2|\uF0E8";
"4.1|协议2_demo1|\uF0E9";
"4.2|协议2_demo2|\uF0EA";
"4.3|协议2_demo3|\uF0EB";
展开 = false;
},
{
"5|日志管理2|\uF039";
"5.1|日志2_demo1|\uF040";
"5.2|日志2_demo2|\uF041";
"5.3|日志2_demo3|\uF042";
"5.4|日志2_demo4|\uF043";
展开 = false;
}
}
import exListbox;
var exx = exListbox(mainForm.listbox,MenuList,30,0x000000,0x272E2D,0x663202,0xFFD859,0x3E8427);
exx.onClick = function(index,subindex,text){
console.log(index,subindex,text)
}
mainForm.show();
return win.loopMessage();工程下载:
希望用上的人能回复下, 给个支持, 截个图分享下配色方案.
如果有人喜欢每次强制最多展开一项, 其他项需要自动折叠, 那么可以将库中的onSelChange()改为下面的代码
winform.onSelChange = function(){
var selindex = winform.selIndex;
if(selindex){
var name = winform.getItemText(selindex);
var textTab = ..string.split(name,"<|>");
var indexAll = textTab[1];
var head = ..string.split(indexAll,"<.>");
var index = tonumber(head[1]);
var subindex = tonumber(head[2]);
var count = #MenuList[index]-1;
if(head[[2]]==null){
if( MenuList[index].展开 ){
for(j=count;1;-1){
winform.delete(selindex+j);
}
MenuList[index].展开 = false;
}else {
var zindex = 0;
for(k,v in MenuList){
zindex++;
if( MenuList[k].展开 ){
var subCount = #MenuList[k]-1;
for(j=subCount;1;-1){
winform.delete(zindex+j);
}
MenuList[k].展开 = false;
}
}
for(j=1;count;1){
winform.add(MenuList[index][1+j],index+j);
}
MenuList[index].展开 = true;
}
}else {
if(this.onClick) this.onClick(index,subindex,textTab[2]);
}
}
}20210829重大更新:
增加选中状态的持续保持功能.
这次更新后, 当你选中某项后, 不会因为点击了展开或者折叠功能从而导致选中状态丢失的情况发生. 无论是折叠还是展开, 都不会覆盖掉被选中的状态, 如下图所示:

其实代码并没有多增加两句, 就是增加了个记录上次选中项目的记录功能
修改后库代码如下:
import fonts.fontAwesome;
class exListbox{
ctor( winform , MenuList , itemHeight,bgcolor=0xa0a0a0,headColor=0x302D29,subColor=0x3F3C38,textColor=0xD1D1D1,selColor=0x6E6B67 ){
winform.bgcolor = bgcolor;
this.seltext = null;
winform.onMeasureItem = function(measureItem){
measureItem.itemHeight = itemHeight;
}
winform.onDrawItem = function(drawItem){
..gdi.selectBrush(
function(hdc,pen,brush){
var rc = drawItem.rcItem;
var alltext = winform.getItemText(drawItem.itemID + 1);
var textTab = ..string.split(alltext,"<|>");
var text = textTab[2];
var font = ::LOGFONT(weight=400;color=textColor);
var indexAll = textTab[1];
var head = ..string.split(indexAll,"<.>");
if(head[[2]]!=null){
if (drawItem.itemState & 1/*_ODS_SELECTED*/ or alltext == this.seltext) {
..gdi.fillRect(hdc,selColor,rc);
}else {
..gdi.fillRect(hdc,subColor,rc);
}
}else {
..gdi.fillRect(hdc,headColor,rc);
}
if(head[[2]]!=null){
..gdi.textOut(hdc,font,text,rc.left+40, rc.top+10);
}else {
..gdi.textOut(hdc,font,text,rc.left+30, rc.top+10);
var ICO = '\uF054';
var font = ::LOGFONT(color=0xB8B8B8;h=-14;name='FontAwesome');
var index = tonumber(head[1]);
if (MenuList[index].展开==true) {
ICO = '\uF078';
}
..gdi.textOut(hdc,font,ICO,rc.left+10, rc.top+10);
}
if (drawItem.itemState & 0x10/*_ODS_FOCUS*/) {
//去掉焦点框
::DrawFocusRect(hdc,rc);
}
},drawItem.hDC,0xa0a0a0/*背景色*/,0xa0a0a0/*画笔色*/)
}
for(k,v in MenuList){
if(v.展开){
for(j=1;#v;1){
winform.add(v[j]);
}
}else {
winform.add(v[1]);
}
}
winform.onSelChange = function(){
var selindex = winform.selIndex;
if(selindex){
var name = winform.getItemText(selindex);
var textTab = ..string.split(name,"<|>");
var indexAll = textTab[1];
var head = ..string.split(indexAll,"<.>");
var index = tonumber(head[1]);
var subindex = tonumber(head[2]);
var count = #MenuList[index]-1;
if(head[[2]]==null){
if( MenuList[index].展开 ){
for(j=count;1;-1){
winform.delete(selindex+j);
}
MenuList[index].展开 = false;
}else {
for(j=1;count;1){
winform.add(MenuList[index][1+j],selindex+j);
}
MenuList[index].展开 = true;
}
}else {
//设置更新区,并更新
var lastIndex = winform.find(this.seltext);
this.seltext = winform.selText;
if(lastIndex){
winform.invalidate(winform.getItemRect(lastIndex));
winform.update();
}
if(this.onClick) this.onClick(index,subindex,textTab[2]);
}
}
}
};
}上面演示是针对 返璞归真那个精简版改的, 如果你是用复杂版的, 同样的参考上面代码修改下即可.

非常漂亮的Listbox,正好短视频需要做语音合成。
import win.ui;
/*DSG{{*/
mainForm = win.form(text="auGen";right=682;bottom=352;bgcolor=16777215;border="none";max=false;mode="popup";sysmenu=false;title=false)
mainForm.add(
edit={cls="edit";left=157;top=52;right=682;bottom=327;autohscroll=false;bgcolor=16777215;border=1;font=LOGFONT(h=-16;name='Microsoft YaHei UI');multiline=1;vscroll=1;z=1};
listbox={cls="listbox";left=-2;top=-2;right=157;bottom=383;edge=1;items={};ownerDraw=1;z=3};
notice={cls="static";left=162;top=331;right=682;bottom=351;bgcolor=16777215;font=LOGFONT(name='Microsoft YaHei UI');transparent=1;z=4};
static={cls="static";text="百度语音合成";left=178;top=10;right=366;bottom=34;font=LOGFONT(h=-20;name='Microsoft YaHei UI';weight=700);transparent=1;z=2}
)
/*}}*/
import win.ui.simpleWindow2;
sw = win.ui.simpleWindow2(mainForm);
import exListbox;
menulist = {
{
"1|1-音色选择|\uF2BD",
"1.0|女声|\uF182",
"1.1|男声|\uF183",
"1.3|逍遥|\uF183",
"1.4|丫丫|\uF182",
展开 = true
},
{
"2|2-输入内容|\uF044",
展开 = false
},
{
"3|3-合成音频|\uF028",
"3.1|下载音频|\uF0ED",
展开 = true
},
{
"4|4-查看|\uF1C7",
"4.1|打开目录|\uF07C",
展开 = true
},
}
elb = exListbox(mainForm.listbox, menulist,30,0x2E2E2E,0x3C3C3C,0x2E2E2E,0xFFFFFF,0x666666)
elb.onClick = function(index,subindex,text){
if (index==1) {
mainForm.notice.text = "选择合成" ++ text ++ "语音";
voice = subindex;
} elseif(index==3){
if (#mainForm.edit.text<1) {
mainForm.notice.text = "请输入要合成的语音内容";
return
};
var wmPlay = com.CreateObject("{6BF52A52-394A-11d3-B153-00C04F79FAA6}");
import baidu.speech;
var http = baidu.speech();
/*
支持下面三种写法:
http.setAuth( 这里直接写access_token )
http.setAuth( 这里写一个可以获取access_token的网址 )
http.setAuth( "hkMI2fBTaKHDLolNxkPw7ylQ","tbUgpyqX9MiCE0LKozGFaGRoLS7xZHqs" )
)
*/
http.setAuth( "hkMI2fBTaKHDLolNxkPw7ylQ","tbUgpyqX9MiCE0LKozGFaGRoLS7xZHqs" )
var mp3Url,err = http.text2audio(
tex = mainForm.edit.text;
per = voice;
)
wmPlay.Url = mp3Url;
wmPlay.controls.play();
import inet.httpFile;
import time;
tm = time.now();
tm.format="%Y%m%d%H%M%S";
hf = inet.httpFile(mp3Url, "mp3/"++tostring(tm)++".mp3")
hf.download()
mainForm.notice.text = tostring(tm)++".mp3已下载"
} elseif(index==4){
import process;
process.explore(io.localpath("/mp3"));
}
}
voice = 0;
mainForm.show();
return win.loopMessage();再分享一个小技巧,对于需要实时变更菜单标题的需求,可以用如下代码重新指定MenuList。这样的好处是可以利用图标字体显示选中状态或其他需要实施变更的信息。
关键listbox需要先clear,不然会重复绘制。MenuList可以修改,也可以重新指定新的列表。
import exListbox;
var exx = exListbox(mainForm.listbox,MenuList,30);
exx.onClick = function(index,subindex,text){
if (index==5 and subindex==2) {
MenuList[5][3] = "5.2|日志2|\uF041";
mainForm.listbox.clear()
exx = exListbox(mainForm.listbox,MenuList,30);
}
}回复#10 @jerryxjr1220 :
开源的好处就是可以任意修改代码为自己所用, 这个需求可以利用return返回值来自动修改menuList并刷新列表, 不用listbox.clear
可以这样操作:
1.库里增加个 this.MenuList = MenuList; 用来保存传递进来的表, 下面就可以修改这个表了.
2.this.OnClick() 函数增加return 返回值, 我们在库里接受到返回值, 然后去执行修改操作
修改后的库代码如下:
import fonts.fontAwesome;
class exListbox{
ctor( winform , MenuList , itemHeight,bgcolor=0xa0a0a0,headColor=0x302D29,subColor=0x3F3C38,textColor=0xD1D1D1,selColor=0x6E6B67 ){
winform.bgcolor = bgcolor;
this.MenuList = MenuList;
this.seltext = null;
winform.onMeasureItem = function(measureItem){
measureItem.itemHeight = itemHeight;
}
winform.onDrawItem = function(drawItem){
..gdi.selectBrush(
function(hdc,pen,brush){
var rc = drawItem.rcItem;
var alltext = winform.getItemText(drawItem.itemID + 1);
var textTab = ..string.split(alltext,"<|>");
var text = textTab[2];
var font = ::LOGFONT(weight=400;color=textColor);
var indexAll = textTab[1];
var head = ..string.split(indexAll,"<.>");
if(head[[2]]!=null){
if (drawItem.itemState & 1/*_ODS_SELECTED*/ or alltext == this.seltext) {
..gdi.fillRect(hdc,selColor,rc);
}else {
..gdi.fillRect(hdc,subColor,rc);
}
}else {
..gdi.fillRect(hdc,headColor,rc);
}
if(head[[2]]!=null){
..gdi.textOut(hdc,font,text,rc.left+40, rc.top+10);
}else {
..gdi.textOut(hdc,font,text,rc.left+30, rc.top+10);
var ICO = '\uF054';
var font = ::LOGFONT(color=0xB8B8B8;h=-14;name='FontAwesome');
var index = tonumber(head[1]);
if (this.MenuList[index].展开==true) {
ICO = '\uF078';
}
..gdi.textOut(hdc,font,ICO,rc.left+10, rc.top+10);
}
if (drawItem.itemState & 0x10/*_ODS_FOCUS*/) {
//去掉焦点框
::DrawFocusRect(hdc,rc);
}
},drawItem.hDC,0xa0a0a0/*背景色*/,0xa0a0a0/*画笔色*/)
}
for(k,v in this.MenuList){
if(v.展开){
for(j=1;#v;1){
winform.add(v[j]);
}
}else {
winform.add(v[1]);
}
}
winform.onSelChange = function(){
var selindex = winform.selIndex;
if(selindex){
var name = winform.getItemText(selindex);
var textTab = ..string.split(name,"<|>");
var indexAll = textTab[1];
var head = ..string.split(indexAll,"<.>");
var index = tonumber(head[1]);
var subindex = tonumber(head[2]);
var count = #this.MenuList[index]-1;
if(head[[2]]==null){
if( this.MenuList[index].展开 ){
for(j=count;1;-1){
winform.delete(selindex+j);
}
this.MenuList[index].展开 = false;
}else {
for(j=1;count;1){
winform.add(this.MenuList[index][1+j],selindex+j);
}
this.MenuList[index].展开 = true;
}
}else {
//设置更新区,并更新
var lastIndex = winform.find(this.seltext);
this.seltext = winform.selText;
if(lastIndex){
winform.invalidate(winform.getItemRect(lastIndex));
winform.update();
}
if(this.onClick) {
var retText = this.onClick(index,subindex,textTab[2]);
if(retText != null){
var selindex = winform.selIndex;
this.MenuList[index][subindex+1] = retText;
this.seltext = retText;
winform.add(retText,selindex)
winform.delete(selindex+1)
winform.selIndex = selindex;
}
};
}
}
}
};
}使用的时候
exx.onClick = function(index,subindex,text){
console.log(index,subindex,text)
if (index==5 and subindex==2) {
return "5.2|日志被修改了";
}
}
登录后方可回帖
,感谢分享


注意:
此楼为写库过程中使用过的测试代码 , 只是为了演示此库也可以使用漂亮的滚动条, 最新封装库和完整工程代码看楼下楼层.
把plus模拟滚动条加上看看效果, 这里参考https://www.chengxu.xyz/t/372#Post1043
简单的调试下单元格大小适应滚动条 , 似乎效果看着还不错, 哈
import fonts.fontAwesome; //GDI自绘 import win.ui; /*DSG{{*/ var winform = win.form(text="listbox自绘伸缩导航栏";right=715;bottom=449;bgcolor=12639424) winform.add( listbox={cls="listbox";left=0;top=0;right=145;bottom=450;bgcolor=16777215;db=1;dl=1;dt=1;items={};ownerDraw=1;z=1}; static={cls="static";left=147;top=0;right=712;bottom=450;align="center";bgcolor=15780518;center=1;db=1;dl=1;dr=1;dt=1;font=LOGFONT(h=-47);z=2} ) /*}}*/ import win.ui.ctrl.plus; class scrollbarEx{ ctor(ctrl,iWidth = 10,itemHeight = 15){//listbox,滚动条宽度、列表项行高 if(!ctrl[["hwnd"]]) error("参数必须是窗口对象",2); this = ctrl; var rc = this.getRect() rc.bottom += itemHeight; this.modifyStyle(,0x4000000/*_WS_CLIPSIBLINGS*/); var dyPlus = winform.add( plus = {cls="plus";left = rc.right-iWidth;top=rc.top; bottom=rc.bottom;width = iWidth;hide=1;bgcolor=-8750470;border={radius=5};color=14587648;foreTop=50;paddingLeft=1;paddingRight=1;dr=1;dt=1;db=1;z=1} ); sb = dyPlus.plus; ..win.setPos(sb.hwnd,,,,,0); ..win.setPos(this.hwnd,,,,,1); sb.skin(style) var max = this.count * itemHeight; //显示区域高度 = 总项目得高度 var overstep = (max - rc.height()) / itemHeight//溢出表项计数 = 超出窗口显示,额外的项目 var thumb = ..math.floor(rc.height() / max * sb.height); var trackPos = overstep; sb.setTrackbarRange(0,overstep); sb.foreTop = thumb; sb.progressPos = trackPos var step = 1 //..math.floor( (max + rc.height()) / sb.foreTop );//绝对高度的时候计算步进 sb.show(overstep > 0); sb.onPosChanged = function( pos,thumbTrack ){ if(thumbTrack){ trackPos = overstep - pos; //..io.print(trackPos) ::SendMessageInt(this.hwnd, 0x115/*_WM_VSCROLL*/,::MAKELONG(4/*_SB_THUMBPOSITION*/,trackPos), 0); } } this.wndproc = function(hwnd,message,wParam,lParam){ select(message) { case 0x20A/*_WM_MOUSEWHEEL*/ { if( ::HIWORD(wParam) & 0x8000){ if(sb.stepProgress(-step)){::SendMessageInt(this.hwnd, 0x115/*_WM_VSCROLL*/, 1/*_SB_LINEDOWN*/, 0);} }else { ::SendMessageInt(this.hwnd, 0x115/*_WM_VSCROLL*/, 0/*_SB_LINEUP*/, 0); sb.stepProgress(step); } return 0; }case 0x115/*_WM_VSCROLL*/{ //..io.print(::HIWORD(wParam),::LOWORD(wParam)) }case 0x200/*_WM_MOUSEMOVE*/{ if(wParam & 1/*_MK_LBUTTON*/){ var x,y = ..win.getMessagePos(lParam); var topIndex = ::SendMessageInt(this.hwnd,0x18E/*_LB_GETTOPINDEX*/,0,0); /*获取列表框中第一个可见项的索引。最初,索引 0 的项位于列表框的顶部, 但如果列表框内容已滚动,则另一项可能位于顶部。多列列表框中的第一个可见项是左上角项。*/ trackPos = overstep - topIndex; sb.progressPos = trackPos; } }case 0x181/*_LB_INSERTSTRING*/{//LB_ADDFILE、LB_ADDSTRING没有尝试,不知道要不要一起处理。 max = (this.count + 1) * itemHeight; //显示区域高度,在_LB_INSERTSTRING事件中,添加表项在事件之前发生实际表项数目要 + 1 overstep = (max - rc.height()) / itemHeight//溢出表项计数 thumb = ..math.floor(rc.height() / max * sb.height); var topIndex = ::SendMessageInt(this.hwnd,0x18E/*_LB_GETTOPINDEX*/,0,0); trackPos = overstep - topIndex; sb.setTrackbarRange(0,overstep); sb.foreTop = thumb; sb.progressPos = trackPos; sb.show(overstep > 0); }case 0x182/*_LB_DELETESTRING*/{ max = (this.count - 1) * itemHeight; //显示区域高度,在_LB_DELETESTRING事件中,删除表项在事件之后发生,实际表项数目要 - 1 overstep = (max - rc.height()) / itemHeight//溢出表项计数 thumb = ..math.floor(rc.height() / max * sb.height); trackPos = overstep; sb.setTrackbarRange(0,overstep); sb.foreTop = thumb; sb.progressPos = trackPos; sb.show(overstep > 0); }case 0x184/*_LB_RESETCONTENT*/{ sb.show(false); } } } }; } namespace scrollbarEx{ style = { background={ default=0xFFE1E1E1; }; color={ default = 0xBD15A43D; hover = 0xFF15A43D; active = 0xFF15A43D; } } } winform.listbox.onMeasureItem = function(measureItem){ measureItem.itemHeight = 30; } import console console.open() var MenuList = { { groupName = "应用管理"; 二级菜单 = { "应用_demo1"; "应用_demo2"; "应用_demo3"; "应用_demo4"; }, 展开=false; }, { groupName = "协议管理"; 二级菜单 = { "协议_demo1"; "协议_demo2"; "协议_demo3"; }, 展开=false; }, { groupName = "日志管理"; 二级菜单 = { "日志_demo1"; "日志_demo2"; "日志_demo3"; "日志_demo4"; }, 展开=false; }, { groupName = "系统管理"; 二级菜单 = { "系统_demo1"; "系统_demo2"; }, 展开=false; }, }; for(k,v in MenuList){ winform.listbox.add(v.groupName); } winform.listbox.oncommand = function(id,event){ var index = winform.listbox.selIndex;//winform.listbox.hitTest(); if(index){ var name = winform.listbox.getItemText(index); var subClick = true; for(i=1;#MenuList;1){ if(MenuList[i].groupName == name){ var count = #MenuList[i].二级菜单; if(MenuList[i].展开){ for(j=count;1;-1){ winform.listbox.delete(index+j); } MenuList[i].展开 = false; }else { for(j=1;count;1){ winform.listbox.add(MenuList[i].二级菜单[j],index+j); } MenuList[i].展开 = true; } subClick = false; break; } } if( subClick ){ winform.static.text = name; } } } winform.listbox.onDrawItem = function(drawItem){ gdi.selectBrush( function(hdc,pen,brush){ var rc = drawItem.rcItem; gdi.fillRect(hdc,0xFFFFFF,rc); if (drawItem.itemID > 0) { gdi.drawLine(hdc,rc.left, rc.top,rc.right, rc.top); } if (drawItem.itemState & 1/*_ODS_SELECTED*/) { gdi.fillRect(hdc,0xFFF5E2,rc); } //加文字测试 var text = winform.listbox.getItemText(drawItem.itemID + 1); var font = ::LOGFONT(weight=400;color=0x000000); var Findflag = false; for(i=1;#MenuList;1){ if(MenuList[i].groupName == text){ gdi.textOut(hdc,font,text,rc.left+10, rc.top+10); var ICO = '\uF0D7'; var font = ::LOGFONT(color=0xB0B0B0;h=-18;name='FontAwesome'); if (MenuList[i].展开==true) { ICO = '\uF0D8'; } gdi.textOut(hdc,font,ICO,rc.right-30, rc.top+10); Findflag = true; break; } } if( !Findflag ){ gdi.textOut(hdc,font,text,rc.left+30, rc.top+10); } },drawItem.hDC,0xF5FDFF/*背景色*/,0xEEEEEE/*画笔色*/) } scrollbarEx(winform.listbox,5,35); winform.show(); win.loopMessage();