这是一个用c#写的控件,它的原型是outlook 2000里面左侧的快捷方式栏,类似的控件也会在QQ,Visio等常用软件中找到,我为这个控件写了一个dll,并且做了一个demo演示它。
如下图所示:
我把一些主要的控件属性在demo里可以随时更改。它代码量不是很多,dll文件只有36kb。
我没有为这个控件提供额外的面板,但是控件中的每个元素都具有Tag属性。
提供了VisibleGroupChanged, ItemClick等主要事件。在它内部我封装了滚动处理,(按住滚动按钮超过0.5秒后由定时器自动滚动),封装了单选状态时的item互斥性切换。在控件中显示的内容全部是绘制而成,也就是说,它们都是逻辑元素,而非实际的子控件对象。为此,我定义了一个SbComponent类作为所有元素的父类,它主要描述了元素的布局信息,同时也提供了通用的HitTest测试方法以响应鼠标,Items对鼠标提供了悬停反馈,但是GroupHeader没有,因为我觉得它并不是最重要的功能,如果想加还是很简单的。在绘制元素时主要使用了ControlPaint类来实现。
控件的内部组织结构如下图所示:
控件的使用非常简单:我借鉴了treeview,listview控件的封装用法,
可以通过鼠标点击自动完成当前显示组的切换,也可以使用代码来完成:
晚上的时候,我为这个控件加入了对拖放item的功能支持。不过这个功能确实具有一定挑战性,因为涉及到的鼠标处理比较复杂。我们可以看到,鼠标操作的主要特点是:
1.当按下某个控件(例如按钮)时,保持鼠标按下状态,并移开控件,则该控件会弹起,如果此时抬起鼠标,则该控件不会被点击。如果保持鼠标按下并移回控件,控件继续显示成按下状态。即,必须按下和抬起时都捕捉到该控件,点击才有效。估计这是windows设计者考虑到了用户的误操作,允许用户可以此种方法取消点击控件。
2. 当鼠标按下一个item并拖动它到临界区(靠近itemsbox边缘)时,如果item很多,则自动开始滚动。
3. 当鼠标移动到其他GroupHeader时,会切换当前显示的Group。即允许用户将item从一个Group拖动到其他Group中。
上面的挑战性和难点在于,需要根据鼠标的位置计算出需要插入的索引位置,以及要绘制标记插入位置的反馈,并且再位置改变时更新它。具体的细节比较复杂,为此我为sidebar引入了很多成员变量来记录一些必要的信息。也使得mousedown,mouseup,mousemove里面的代码量增加了几乎一倍。。。
不过上面的烦琐细节都被我封装在内部,在外部的使用还是非常简单的。我提供了一个AllowDragItem属性,只需要简单的设置它即可选择是否支持拖放item。同时我考虑为控件增加一个新的事件把它通知给外部。
如下图所示:
我把一些主要的控件属性在demo里可以随时更改。它代码量不是很多,dll文件只有36kb。
我没有为这个控件提供额外的面板,但是控件中的每个元素都具有Tag属性。
提供了VisibleGroupChanged, ItemClick等主要事件。在它内部我封装了滚动处理,(按住滚动按钮超过0.5秒后由定时器自动滚动),封装了单选状态时的item互斥性切换。在控件中显示的内容全部是绘制而成,也就是说,它们都是逻辑元素,而非实际的子控件对象。为此,我定义了一个SbComponent类作为所有元素的父类,它主要描述了元素的布局信息,同时也提供了通用的HitTest测试方法以响应鼠标,Items对鼠标提供了悬停反馈,但是GroupHeader没有,因为我觉得它并不是最重要的功能,如果想加还是很简单的。在绘制元素时主要使用了ControlPaint类来实现。
控件的内部组织结构如下图所示:
控件的使用非常简单:我借鉴了treeview,listview控件的封装用法,
1
SideBar.SideBar sbar
=
new
SideBar.SideBar();
2 // 赋予imagelist
3 sbar.ImageList = this .imageList1;
4 sbar.AddGroup( " 第一组 " );
5 sbar.AddGroup( " 第二组 " );
6 // 添加items,参数列表为:itemtext,imageindex
7 sbar.Groups[ 0 ].Items.Add( " item0 " , 0 );
8 sbar.Groups[ 1 ].Items.Add( " item1 " , 1 );
9 sbar.VisibleGroupChanged += new SbGroupEventHandler(sbar_VisibleGroupChanged);
10 sbar.ItemClick += new SbItemEventHandler(sbar_ItemClick);
2 // 赋予imagelist
3 sbar.ImageList = this .imageList1;
4 sbar.AddGroup( " 第一组 " );
5 sbar.AddGroup( " 第二组 " );
6 // 添加items,参数列表为:itemtext,imageindex
7 sbar.Groups[ 0 ].Items.Add( " item0 " , 0 );
8 sbar.Groups[ 1 ].Items.Add( " item1 " , 1 );
9 sbar.VisibleGroupChanged += new SbGroupEventHandler(sbar_VisibleGroupChanged);
10 sbar.ItemClick += new SbItemEventHandler(sbar_ItemClick);
可以通过鼠标点击自动完成当前显示组的切换,也可以使用代码来完成:
1
//
设置当前显示的组
2 sbar.VisibleGroupIndex = 1 ;
2 sbar.VisibleGroupIndex = 1 ;
晚上的时候,我为这个控件加入了对拖放item的功能支持。不过这个功能确实具有一定挑战性,因为涉及到的鼠标处理比较复杂。我们可以看到,鼠标操作的主要特点是:
1.当按下某个控件(例如按钮)时,保持鼠标按下状态,并移开控件,则该控件会弹起,如果此时抬起鼠标,则该控件不会被点击。如果保持鼠标按下并移回控件,控件继续显示成按下状态。即,必须按下和抬起时都捕捉到该控件,点击才有效。估计这是windows设计者考虑到了用户的误操作,允许用户可以此种方法取消点击控件。
2. 当鼠标按下一个item并拖动它到临界区(靠近itemsbox边缘)时,如果item很多,则自动开始滚动。
3. 当鼠标移动到其他GroupHeader时,会切换当前显示的Group。即允许用户将item从一个Group拖动到其他Group中。
上面的挑战性和难点在于,需要根据鼠标的位置计算出需要插入的索引位置,以及要绘制标记插入位置的反馈,并且再位置改变时更新它。具体的细节比较复杂,为此我为sidebar引入了很多成员变量来记录一些必要的信息。也使得mousedown,mouseup,mousemove里面的代码量增加了几乎一倍。。。
不过上面的烦琐细节都被我封装在内部,在外部的使用还是非常简单的。我提供了一个AllowDragItem属性,只需要简单的设置它即可选择是否支持拖放item。同时我考虑为控件增加一个新的事件把它通知给外部。
1
//
使控件支持鼠标拖放条目
2 Sbar.AllowDragItem = true ;
2 Sbar.AllowDragItem = true ;
你可以在这里下载到这个控件和例子的最新源代码:
http://www.cnblogs.com/Files/hoodlum1980/SideBar20070707.rar
在这个网站上面还有我的其他几个比较小但是有趣的例子源码,也许会在以后详细介绍它们。
——————————————————————————————
小Bug记录:
1.修改了调整字体后布局错误的bug。
2.修改了在demo中的一个疏忽:在显示字体对话框时,错误的将字体初始化为了form1的字体。
3.我把影响外观的一些属性修改后的刷新显示封装在了控件内部。这个并不算什么严重的bug
但会使外部使用时更为简便。例如,对enabled,view(大图标,小图标...),flatstyle等属性的修改,都会在控件内部调用刷新。在外部只要简单的设置它们的值即可。
---------------------
这是我在我的博客园博客开张时的第一篇文章,现在我把博客园博客的一些比较重要的文章转载到这里,算是一个重大的决定,由于网络速度,发布日志以及代码插入窗口,图片插入窗口的性能和相应速度,这些因素决定了我有向csdn搬家的倾向。今晚在博客园上想发表日志,可是乌龟爬一样的相应速度,以及屡次三番的server unavailable让我十分恼火!这是不可忍受的。。。