您好,欢迎访问三七文档
当前位置:首页 > 建筑/环境 > 设计及方案 > 第5章 窗体界面设计
第5章窗体界面设计本章要点:高级的可视化程序设计深入理解事件驱动模型动态生成窗体控件遍历、搜索的方法5.1菜单的使用和动态菜单菜单在每个软件中都会见到,大体来说分为两种:窗体中的主菜单(MainMenu)和单击鼠标右键调出的上下文菜单(ContextMenu)。这两者在WindowsForms中都有现成的控件可用,而且由于其优良的设计,这两者的用法基本相同。一个菜单可以看成是一棵树:它拥有很多个菜单项,而每个菜单项下面又可以有自己的菜单项,这样才得以建立出经常见到的多级菜单,如图5.1就是一个典型的例子。这里【格式】是主菜单中的一个菜单项,其下又有【对齐】、【使大小相同】等菜单项,而在【垂直间距】菜单项下还有菜单项……可以如此不停地重复下去。之所以要在这里费如此多的功夫来讲解这些大家可能已经很熟悉的概念,完全是因为澄清这些概念对于使用WindowsForms有很大的好处。一个MainMenu(或者ContextMenu,两者用起来非常相似)代表一个主菜单(或上下文菜单),它包含了一个MenuItems集合,这个集合中的每一个元素都是一个MenuItem,代表一个菜单项;每一个MenuItem中又有自己的MenuItems集合,包含它自身的子菜单项……这种设计使得处理每一个菜单项的方式几乎完全一致,也可以方便地应用树的算法来对菜单做各种操作。后面将会看到这样的例子。在本案例中,我们将使用“全动态”的方式,在运行时刻交互地生成新菜单项。新菜单项的文字、位置和行为都可以由用户指定。5.1.1动态生成菜单建立一个新的Windows窗体应用程序Case5_1,首先加入一个MainMenu控件。一般来说一个窗体只有一个主菜单,所以可直接把它命名为MainMenu。MainMenu本身是一个不可见控件,它会出现在窗体下方的不可见控件区。但是加入它之后,窗体的上部会出现类似菜单的东西,可以在其中直接输入菜单项的文字。这是VisualStudio.NET提供的新的“菜单编辑器”。5.1.2程序运行按F5键运行程序。在程序启动时,窗体上只有3个不能展开的菜单项,在TreeView中也可以看到这种结构。在文本框中输入一些文字,如“菜单1”;然后在“行为”栏中任意选择一项,如ShowHello,它使菜单项被单击以后弹出“Hello”对话框;最后在“添加到”栏中选择一项,如“菜单2”(注意,这个操作顺序不可违反);单击【添加菜单项】按钮,会看到新菜单项已经被加入到主菜单中,而TreeView中也及时地反映了这种变化。如果单击这个新加的菜单项,将会看到“Hello”对话框。当然,还可以继续添加,不仅是在原有的菜单项下,还可以在新加入的菜单项下,这种过程可以无限地重复。如果添加的时候选择了其他行为,如ShowMenuItemText,单击菜单项时就会弹出它自身的文字。5.1.3算法解释单击【添加菜单项】按钮之后,新菜单项是如何被加入到菜单中的呢?DimcorrespondingMenuItem,newMenuItemAsMenuItemcorrespondingMenuItem=FindMenuItem(treAddTo.SelectedNode)newMenuItem=NewMenuItem(txtNewText.Text)IflstBehavior.SelectedIndex=0ThenAddHandlernewMenuItem.Click,Handlers(lstBehavior.SelectedIndex)EndIfcorrespondingMenuItem.MenuItems.Add(newMenuItem)首先使用FindMenuItem函数找到应该添加的位置,也就是用户在TreeView中选择的节点所对应的菜单项。关于FindMenuItem的实现,后面会讲解。接着用用户输入的文字生成新的菜单项。MenuItem的构造函数有好几个,在这里使用的函数只需要传入菜单项文字。接下来用AddHandler语句,将用户选择的行为与newMenuItem的Click事件关联起来。AddHandler是一个语句,它的作用就是将事件与事件处理程序相关联。这种关联还可以用RemoveHandler语句来去除。注意这两个都不是函数,所以不要加括号。最后,把新的菜单项加入到它该去的位置。正如前面所说,本例中使用了MenuItems集合,这是一个MenuItemCollection类型的集合,它拥有一般集合所拥有的方法,如Add,Remove等。菜单加入以后,用MenuItemToNode过程重新建立TreeView,并反映当前的菜单结构:treAddTo.Nodes.Clear()MenuItemToNode(MainMenu.MenuItems,treAddTo.Nodes)treAddTo.ExpandAll()现在,让我们进入MenuItemToNode过程,看看“菜单树”是怎样被变成TreeView中的树的。ForEachsubItemInmenuItemsDimsubNodeAsNewTreeNode(subItem.Text)IfsubItem.MenuItems.Count0ThenMenuItemToNode(subItem.MenuItems,subNode.Nodes)EndIftargetNodes.Add(subNode)Next很简短,是不是?确实,这里使用的是递归的方法,而递归是用来对付重复性结构的最简洁的办法(尽管效率可能不太高)。首先,对主菜单的MenuItems集合应用MenuItemToNode过程,并且希望把它映射到treAddTo的Nodes集合中去。前面说过菜单包含很多菜单项,而每个菜单项本身又包含很多菜单项。所以任意抽出一个菜单项来,都可以看成是一棵树。于是再对这棵树应用MenuItemToNode过程,而这个过程会自动地在下级菜单中重复,直到遇到了最末级的菜单项为止。当然,对递归的详细分析和描述不是本书的任务,如果你对递归感到难以理解(这是正常的,每一个刚接触到递归的人都会如坠云里雾里),请参考任何一本程序设计语言相关的书籍。回头再来看FindMenuItem的实现,它接受用户选择的树节点作为参数,返回菜单上对应于这个节点的菜单项:DimindexStackAsNewCollections.Stack()DoUntilselectedNodeIsNothingindexStack.Push(selectedNode.Index)selectedNode=selectedNode.ParentLoopDimmenuItemAsMenuItem=MainMenu.MenuItems(indexStack.Pop())DoUntilindexStack.Count=0menuItem=menuItem.MenuItems(indexStack.Pop())LoopReturnmenuItem在这里使用一个堆栈作为辅助,堆栈就是一个类似烧烤用的铁签的东西:想一想,你从最顶上开始吃铁签上的肉,而最先插上去的肉你最后才能吃到。在堆栈中,最先放进去的数据最后出来,关于堆栈的详细信息,你可以参考任何一本数据结构教材。.NET框架提供了很好的堆栈实现,在这里利用了TreeNode的Parent属性,记录从用户选定的节点开始向上追溯到根节点为止,每一个节点在其所在层次中的位置(用Index属性)。把这些位置存入堆栈中,然后再利用这些数据,从菜单的顶层往下追寻,由于树与菜单是“同构”的,这样一定可以达到目的。在这个过程中,TreeNode是自下而上,MenuItem是自上而下,而堆栈则刚好提供了一个“反转”的功能,使自下而上得到的数据可以自上而下用。最后谈一下程序中定义的一个成员数组——Handlers:PrivateHandlers(2)AsEventHandler这是一个EventHandler的数组,而EventHandler是一个委托对象,可以接受事件处理程序,正如Form1_Load中一样:Handlers(0)=AddressOfShowHelloHandlers(1)=AddressOfShowMenuItemTextHandlers(2)=AddressOfShowMenuItemIndex将这3个事件处理过程放入数组,为用户提供选择,就可以让用户指定使用哪一个,从而动态决定菜单项的行为。5.2进度条、跟踪条、工具提示试验了一个相对复杂的程序之后,现在来轻松一下,看看一个简单的Windows窗体程序。它只演示进度条(ProgressBar)、跟踪条(TrackBar)和工具提示(ToolTip)控件的使用,这3个控件虽说并不起眼,但是也有它们能够显身手的场合。例如,媒体播放器中就有跟踪条的身影,IE中大量使用了进度条,而工具提示则是现在商业软件的标准配置。这个程序的窗体上只有一个进度条和一个跟踪条,拖动跟踪条,进度条的显示会与跟踪条保持同步,而这两个控件都配备有工具提示。5.2.1设计一个带有进度条、跟踪条、工具提示的程序5.2.2控件描述事实上,ProgressBar、TrackBar都很像第4章中学习过的ScrollBar,它们的中心属性就是4个:(1)Maximum控件范围的最大值;(2)Minimum控件范围的最小值;(3)Step每次变化的步长(ScrollBar中要复杂一些,因为它有几种步长);(4)Value当前位置。这4个属性均为可读写,了解了这一点,对它们的使用方式就不会有任何疑问了。其次来看看ToolTip控件,值得注意的属性有5个。(1)InitialDelay属性经过多少时间显示工具提示。换句话说,鼠标放在控件上方多久,工具提示才会出现,默认值为500ms。(2)AutoPopDelay属性当指针在包含指定工具提示文本的控件内保持静止时,工具提示保持的时段。也就是说,工具提示多久才会消失,默认值为5000ms。(3)ReshowDelay属性鼠标指针从一个控件移到另一个控件时,后面的“工具提示”窗口出现前必须经过多长时间。(4)AutomaticDelay属性工具提示的自动延迟。它事实上是用来设置前3个值的,一旦设置了AutomaticDelay,则InitialDelay会自动设为等于AutomaticDelay;AutoPopDelay会自动设为等于AutomaticDelay的10倍;而ReshowDelay会自动设为AutomaticDelay的五分之一。当然,也可以手动设置前面3个属性。(5)ShowAlways属性指示“工具提示”窗口在其父控件不活动时是否显示。5.3工具栏和状态栏WindowsForms为.NET框架引入了非常直观的工具栏和状态栏的实现,它们都广泛使用集合的观念来管理子控件(工具栏中的按钮和状态栏中的面板),好处就是:那些可能需要使用多个控件实体(如Delphi就是)来管理的项目现在只需要一个控件了。但是这样的做法也可能使习惯于“工具栏就是按钮的组合”观念的程序员不太习惯,不过一旦领会了它的妙处,你就会由衷地赞叹这种设计。在这个案例中,我们将学习使用工具栏、状态栏和一个以前一直被忽视的控件:图像列表(ImageList),这个控件起到的是图标(或者图片)容器的作用。如果在程序中使用到很多小的图标,那么用它来集中管理这些图标会很方便。5.3.1在程序中使用工具栏和状态栏5.3.2运行工具栏和状态栏程序5.3.3程序点评前面曾经讲过ToolBar的子控件是集中管理的,不仅反映在ToolBarButton被放入集合中,而且也反映在它们的事件是集中处理的。本例中只写了一个ToolBar1_ButtonClick事件,通过传入的ToolBarButtonClickEventAr
本文标题:第5章 窗体界面设计
链接地址:https://www.777doc.com/doc-4005723 .html