袁创:软件开发中Windows窗体设计概念及原则
袁永福 2018-6-6
■■■■前言
虽然现在都流行BS架构的软件,但CS软件仍然牢牢的占据半壁江山,大量的应用场景仍然是必须采用CS软件。因此大量的开发组织仍然在维护和开发Windows窗体程序。此时Windows窗体设计中一些概念和原则仍然是要贯彻执行的。虽然这些知识浅显易懂,但实践中发现大量的普通程序猿甚至老程序猿都没能系统的掌握这些知识,反复犯着低级错误,限制了开发能力,开发出来的软件用户体验不佳。在此作者袁永福(南京都昌信息科技有限公司创始人)介绍相关的知识,希望能帮助提高读者的开发水平,提升软件品质。
当开发人员在窗体中新增一个控件时,系统会给这个控件设置默认名称,比如对于Label控件,其默认名称可能为lable1、label2等,对于TextBox控件其默认名称可能为textBox1、textBox2等等。
笔者设计了一个“用户信息”窗体,在默认情况下,各个控件默认的名称如下图所示:
这是一种非常糟糕的窗体设计结果。在编写该窗体的C#代码时,由于无法同时看到窗体设计界面,因此很难记下各种控件的名称及其功能。当窗体控件比较多时,此时的程序代码的编写和维护时相当困难的。
为了避免这种不好的情况,开发人员应当养成在窗体上新建控件就立即修改控件名称的习惯,这样能有效的改进软件的用户界面的设计质量,也是提高窗体后台代码质量的基础。
不过对于Label等少数几种控件,由于基本上不参与编程,只是在用户界面上显示一段不可改变的静态文本,因此可以不设置Label控件的名称,但当在少数情况下,当窗体后台代码需要操作Label控件的文本等属性值时,为了提高代码的质量也应当修改Label控件的名称。
比如对于下面这个窗体,各个数据输入控件的名称设置如下。
跟代码变量命名规则类型,窗体控件也有命名规则,业界主流的控件命名规则可能有多种,各种规则之间差别不大,笔者采用的命名规则大致如下:
控件名称一般为表示控件类型的前缀加上表示控件作用的字符组成。常用控件类型和前缀对应关系如下表所示。
控件类型 | 前缀 |
Button 按钮 | btn |
CheckBox 复选框 | chk |
ColumnHeader 视图列表头 | col |
ComboBox 组合框 | cbo |
ContextMenu 快捷菜单 | ctm |
DataGrid 数据网格控件 | dg |
DataGridView 数据网格视图控件 | dgv |
DateTimePicker 时间输入框 | dtp |
DomainUpDown 数值框 | dud |
Form 窗体 | frm |
GroupBox 组合框 | grp |
HscrollBar 水平滚动条 | hsb |
ImageList 图标列表 | img |
Label 文本标签 | lbl |
LinkLabel 带链接的文本标签 | lbl |
ListBox 列表框 | lst |
ListView 视图列表 | lvw |
Menu 菜单 | menu |
MenuItem 菜单项 | menu |
NumericUpDown 数值框 | nud |
Panel 面板 | pnl |
PictureBox 图片框 | pic |
ProgressBar 进度条 | prg |
RadioButton 单选框按钮 | rdo |
Spliter 拆分条 | spl |
StatusBar 状态栏 | stu |
StatusBarPanel 状态栏区域 | pnl |
StatusStrip 状态栏 | stu |
TabControl 分页控件 | tab |
TabPage 分页标签页面 | page |
TextBox 文本框 | txt |
Timer 定时器 | tmr |
ToolBar 工具条 | tbr |
ToolStrip 工具栏 | tsp |
ToolStripButton 工具栏按钮 | btn |
ToolStripComboBox 工具栏下拉组合框 | cbo |
ToolStripDropDownButton 工具栏下拉列表 | btn |
ToolStripDropDownMenu 工具栏菜单项目 | menu |
ToolStripLabel 工具栏静态文本 | lbl |
ToolStripProgressBar 工具栏进度条 | prg |
ToolStripTextBox 工具栏文本框 | txt |
TreeView 树状视图列表 | tvw |
VScrollBar 垂直滚动条 | vsb |
WebBrowser 浏览器控件 | wb |
对于其他不常用的控件类型的前缀读者可以自己琢磨或者请教他人,控件名称前缀只是一个比较小的细节问题,即使有误也无伤大雅。
笔者一般使用英文或者缩写来表示控件的作用,若没有合适的英文则采用汉语全拼音或者干脆用中文汉字,但绝不会用中文汉语拼音首字母,因为汉语拼音首字母很难仅仅从字母上猜出其表示的汉字,而猜测英文缩写相对容易得多。
如下图所示,可能有些人使用汉语拼音首字母来设置控件的名称,比如“姓名”文本框取名“txtXM”,这是一种不好的控件命名方式,因为很多时候是实在是难于从拼音首字母中猜测控件的作用,控件名称的低可读性降低了程序代码的可读性。
因此按照比较合理的命名方式,“姓名”文本框的名称就采用TextBox控件的前缀“txt”和表示姓名的英文“Name”组合而成为“txtName”。
在实践中,很多商业软件不是“一次开发,长期使用”,而是“一次开发,长期修改,长期使用”,因此开发商业软件需要考虑到未来的修改和升级换代,此时程序代码不但要耐用,还需要耐看耐改,程序代码需要具备良好的可读性,而给控件设置合适的名称就是一个基础工作。其实除了控件的名称,其他的诸如数据库表名字段名,窗体名称,程序模块名称等等都需要遵守一定的命名规范。
设计Windows窗体是还需要设置好控件的TabIndex属性值。在程序运行显示窗体时,用户可以按下Tab键来切换输入焦点,而切换顺序就依照各个控件的TabIndex属性值从小到大排序。一般的输入焦点切换顺序应该是按照从左到右,从上到下。因此需要设置左上方位的控件的TabIndex属性值为小,右下方为的控件的TabIndex属性值为大。但实际开发中还是按照具体功能需求来安排输入焦点切换顺序。
在设置各个控件的TabIndex属性值时不应设置为连续的值,比如设置第一个控件的TabIndex属性值为1,第二个控件为2,第三个控件为3,等等等。采用这种设置时,当未来在输入焦点顺序中插入新的控件,则会需要修改大量的控件的TabIndex属性值。
因此笔者建议输入的各个控件的TabIndex属性值不连续,比如有10的间隔,此时可以设置第一个控件的TabIndex属性值为10,第二个控件为20,第三个控件为30,等等等。这样就为未来插入新的控件留下TabIndex顺序的空间。如下图所示:
在这个“用户信息”窗体设计的例子中,当该窗体运行显示后,用户可以按下Tab键从左到右从上到下的切换焦点。
有些控件并不能接受输入焦点,比如Label控件,但仍然需要为这类控件设置合适的TabIndex属性值。因为Label控件虽然不能接受输入焦点,但仍然能接受快捷键,比如在“用户信息”的窗体设计中,笔者设置第一个Label控件的文本为“姓名 (&N):”,则程序运行后显示该窗体时,用户按下“Alt+N”组合键时,Label控件就感应到快捷键并试图设置输入焦点到自己,由于Label控件不能接受输入焦点,因此系统就会将输入焦点自动切换到Label控件的下一个焦点顺序的控件上。也就是TabIndex值为20的文本框上。这样用户就使用快捷键“Alt+U”就能快速切换焦点到“姓名”文本框了。
当用户界面上数据输入域很多时,这种快速切换输入焦点的功能是很人性化的,方便用户使用纯键盘操作来高速输入和修改数据,显得用户界面做的很专业。若没有这种功能,则用户需要频繁的切换鼠标和键盘来输入数据,大大拖累操作速度。
商业程序的用户界面要求比较高,因为使用者可能好几年天天面对相同的用户界面,累计下来会重复进行几万次操作,此时用户界面需要耐看耐用,这需要在很多细节上做好功夫,其中设置各个控件的TabIndex属性值就是一个基础工作,必须花点心思做好。
在VS.NET可以很方便的设置复杂窗体的TabIndex。如下图所示:
当开发者按下图中右上角的“Tab键顺序”按钮后,窗体中每个控件上都显示出TabIndex顺序值。此时用户只需要挨个点击控件就可以设置控件的TabIndex值了。
■■■■Z-Index
在WinForm窗体设计中有一个Z-Index的概念。窗体布局是采用绝对坐标方式的,每一个控件都有Left和Right属性用于确定控件的左上角在窗体中的X-Y坐标。
当窗体有多个控件时,控件之间可能存在相互重叠的现象,这类似图像处理中层的概念。也就是说一个控件单独的占有一个控件布局层,多个控件就会有多个布局层的叠加,而越靠近窗体的控件就越容易被覆盖,于是从窗体开始计算,每个控件布局层都有一个从0开始计算的序号,这个序号就是Z-Index。
从另外一种方式理解,窗体上的控件布局是不仅仅是X-Y坐标系那种二维的,而是三维的,第三维就是Z坐标轴,它是从窗体向用户延伸的,控件是分布在窗体和用户之间。此时可以说用户是俯看用户界面,若不注意是不会感觉到第三维的存在。而且控件在第三维中的坐标值就是Z-Index,这个值是从0开始的,不会重复;Z-Index值大的控件可以覆盖掉值小的控件。
我们还可以从第三种方式看Z-Index值,窗体对象有一个Controls的属性,该属性值可以看做一个控件数组,窗体上所有的控件都在这个数组中。系统创建窗体时,是从这个数组逆向遍历获得控件对象,然后依次放置在窗体上,很显然在数组中靠前的控件可以覆盖掉靠后的控件,于是控件在这个数组中的逆向序号也可以看做Z-Index值。
实际上其他软件开发技术中也有Z-Index属性,比如VB,还有Web开发中才CSS样式也支持Z-Index属性值。
Z-Index与其他控件相关,因此控件本身不会有Z-Index属性,而且根据控件在窗体中的布局动态的计算出来的。
在VS.NET的窗体设计器中,不能直接设置控件的Z-Index值,但可以使用布局工具条上的“置于顶层”和“置于低层”按钮来将控件沿着Z坐标轴置于最大值或最小值,也就是沿着Z坐标轴移动控件,使其最靠近用户或者最靠近窗体。
在窗体设计中,设置控件在Z轴上的先后顺序也是有一些原则的,那就是重要的控件需要置前,不能被不重要的控件覆盖掉。如下图所示
文本标签的Z-Index值大于文本框的,这种用户界面是很不好的,会遮挡文本框中的部分类型,而且也不美观,因此需要调整为文本框靠前,也就是如下图的效果。
如果用户界面排版时被迫发生一些重叠遮盖,也必须保证重要的控件不被遮盖。
一般来说,若设计人员心中已经知道用户界面的设计,在窗体上从左到右、从上到下的依次添加控件,控件的TabIndex和Z-Index值都是不断增加的,此时做好之后大多就无需调整了。但在实际中设计好的窗体经常会添加或删除控件,或者大幅移动控件的位置,此时需要注意调整控件的TabIndex和Z-Index值。
在同一个软件中,甚至同一家企业开发的所有的软件中,对于窗体控件纵向应当采用相同的对齐方式,可以左对齐、居中对齐或者右对齐。不过笔者建议左对齐。
如下图所示,对于“用户信息”窗体,控件设计时采用左对齐时窗体的运行时用户界面如下:
而采用居中对齐时窗体运行时的用户界面如下:
采用右对齐时窗体运行时的用户界面如下
对比这三种情况,应该是控件左对齐排版最好,其次是右对齐,居中对齐最差。因此一般都是采用左对齐方式。
不过不管采用哪种控件对齐方式,一定要记住在同一个软件中所有的用户界面中采用统一的对齐方式,不能一个窗体采用左对齐而另外一个窗体采用右对齐,这是一种相当不专业的表现。
在VS.NET中统一控件的对齐方式是比较简单的,首先将控件按照大致的位置布局好,然后选中所有要参与对齐操作的控件,点击工具条上的对齐功能按钮就可。
下图就是VS.NET提供的对齐工具条。
该工具条上常用的按钮功能有
左对齐 | 移动控件,当前控件不动,使得所有选择的控件的左边缘和当前控件对齐。 |
居中对齐 | 移动控件,使得所有选择的控件的垂直中轴线和当前控件的重合。 |
右对齐 | 移动控件,使得所有选择的控件的右边缘和当前控件的对齐。 |
顶端对齐 | 移动控件,使得所有选择控件控件的顶边缘和当前控件的对齐。 |
中间对齐 | 移动控件,使得所有选择的控件的水平中轴线和当前控件的重合。 |
底端对齐 | 移动控件,使得所有选择的控件的底边缘和当前控件的对齐。 |
使宽度相同 | 设置所有选择的控件的宽度值等于当前控件的值。 |
使高度相同 | 设置所有选择的控件的高度值等于当前控件的值。 |
使大小相同 | 设置所有选择的控件的宽度和高度等于当前控件的值。 |
使水平间距相等 | 适当的水平移动选择的控件,其中最左边的控件和最右边的控件不动,使得各个控件水平方向的间距相等。 |
增加水平间距 | 适当的水平移动选择的控件,使得各个控件水平方向的间距增加一些,各个控件的间距增加量相同。 |
减小水平间距 | 适当的水平移动选择的控件,使得各个控件水平方向的间距减少一些,各个控件的间距减少量相同。 |
移除水平间距 | 适当的水平移动选择的控件,使得各个控件水平方面紧密的靠在一起,之间没有间隙。 |
使垂直间距相等 | 适当的上下移动选择的控件,其中最上面和最下面的控件不动,使得各个控件垂直方向的间距相等。 |
增加垂直间距 | 适当的上下移动选择的控件,使得各个控件垂直方向的间距增加一些。 |
减小垂直间距 | 适当的上下移动选择的控件,使得各个控件垂直方向的间距减小一些。 |
移除垂直间距 | 适当的上下移动控件,使得各个控件垂直方向紧密的靠在一起,之间没有间隙。 |
置于顶层 | 使得选择的控件在Z顺序方向变得靠前,不被其他控件遮盖。 |
置于底层 | 使得选择的控件在Z顺序方向变得靠后,不会覆盖其他控件。 |
例如有几个控件的位置设计如下
此时需要选中这些控件,然后点击对齐工具条上的“中间对齐”和“移除水平间距”按钮即可达到如下图所示的设计效果。
■■■■对话框
Windows窗体大体分为普通窗体和对话框。对话框一般时是弹出方式显示,显示对话框的时候,应用程序的其他任何窗体都冻结了,无法进行任何鼠标键盘操作。只能等待用户明确的关闭对话框后才能进行操作。
普通窗体和对话框的标题栏必须具有明确的设置。如图:
在VS.NET窗体设计器中,新增的窗体默认具有最小化最大化按钮,同时窗体运行时鼠标可以拖拽边框来设置窗体的大小。
对于对话框,如果做任何修改则运行时会出现以下问题:
第一,对话框最小化时冻结。当用户误操作点击了最小化按钮后,对话框被最小化了。此时应用程序虽然完整的显示出来。但被冻结了,无法进行任何操作。此时用户很有可能没意识到正在显示对话框而认为程序死机了,甚至有时候必须手动杀死进程重启软件才能继续工作。这严重影响用户体验。因此所有的对话框必须隐藏最小化按钮,不得例外。
如下图所示,在VS.NET的窗体属性设计器中,需要设置窗体的MinimizeBox属性为False。
第二,对话框改变大小。一个对话框显示如下图所示:
当用户误操作修改了窗体大小,如下图所示:
此时部分输入界面和保存/取消按钮看不见了。此时容易给用户带来很大的困惑。影响用户体验。因此对话框一般来说时不能设置为用户可改变大小的。也就是设置窗体的MaximizeBox属性值为False,FormBorderStyle属性值为FixedDialog。
如果对话框内部处理了窗体大小改变事件来调整控件的排版,则对话框自然是可以改变大小的。
第三,显示在任务栏。窗体默认是显示在任务栏中的。此时建议修改这种默认行为。除了主窗体显示在任务栏中,其他的窗体都不显示在任务栏中。比如这个程序用户界面如下:
为一个主窗体,连续显示了两个对话框。此时从其他程序切换到这个程序时,鼠标光标移动到Windows任务栏点击程序图标时显示效果如下:
此时用户需要在额外的移动鼠标并点击一次才能切换到程序界面中。
而如果对话框没有在任务栏中显示,则鼠标移到Windows任务栏时显示效果如下:
此时用户只需点击一次就能切换到所需的程序了。避免额外操作。
开发者需要设置窗体的ShowInTaskbar属性值为False就能让窗体不在Windows任务栏中显示。
综上,对于对话框,原则上需要手动设置属性如下:
FormBorderStyle=FixedDialog
MaximizeBox=False
MinimizeBox=False
ShowInTaskbar=False。
此外还建议设置StartPosition=CenterScreen。
■■■■窗体的位置
窗体的位置也是比较细节的技术。默认情况下窗体StartPosition属性值是Manual,也就是“随机”的,如下图所示:
当用户点击“初步诊断”文本框,弹出一个对话框让用户选择录入。此时弹出对话框在屏幕中的位置和初步诊断文本框在屏幕中的位置距离遥远,用户体验很不好。
用户首先需要靠眼睛的余光来察觉到对话框的显示,然后移动眼光甚至连带转头的动作,然后用手移动鼠标光标很长一段距离,然后瞄准点击才能操作对话框。这是一个浪费用户精力和时间的过程,积少成多的造成大量用户时间的浪费。
为此好的软件需要尽量避免用户眼光焦点和鼠标光标的大范围的跳跃移动。因此新增的对话框显示的位置可以采用2种方式:
第一是屏幕居中,这种方法很简单,就是设置对话框的StartPosition属性值为CenterScreen。
第二是对话框尽量靠近当前输入焦点。比如:
此时用户无需长距离的移动眼光和鼠标光标就可进行操作,节省时间。
在一些情况下,受限于屏幕工作区域的限制,上图中的对话框无法完整的显示,则可以挪个地方贴近显示,如下图所示:
这就需要对话框在显示的时候进行额外的判断。
■■■■高分屏
现在一些用户开始用上了高分辨率屏幕,简称高分屏,也就是SXGA。一些新型的高端笔记本电脑,比如华为MateBook X Pro之类的都是高分屏。
高分屏能显示更为精细的内容,色彩也更为绚丽逼真。不过也对Windows应用程序提出了一些要求。此时程序计算所得的屏幕坐标和实际值是不一样的。虽然大多数情况下Windows操作系统能自动处理,但在少数情况下还是会容易出现问题,需要在开发中注意。
高分屏对Windows程序另外一个显著的影响是鼠标光标。一般的鼠标光标是程序加载.cur文件来创建的。而一般的.cur文件是采用32*32像素的。这种鼠标光标在高分屏中明显的缩小了,此时应用程序需要额外配置高分屏使用的.cur文件。比如64*64大小的.cur文件。
■■■■案例分析
现有这么一个对话框:
我们对它进行案例分析,该对话框的问题如下:
1.对话框内容整体轮廓和对话框距离太大。应该让控件们整体向左上角移动一点,然后窗体大小调小一些。这样内容和对话框边缘间距就不是很大了。改进美观。
2.文本标签格式不一致。比如存在“卡号:”,还存在“年龄”。有的有分号,有得没有分号,需要统一加上分号。
3.就诊时间是按照年月日的方式显示,没有精确到时分秒。因此应该称之为“就诊日期”。
4.就诊时间和发病时间输入框太长,导致右边出现大段空白。应该缩短日期输入框的宽度。
5.过敏记录文本框左边的文本标签部分的覆盖了文本框的内容。应该调整控件的Z-Index属性。
6.各行控件之间的垂直间隔不相等。比如姓名输入框和住址输入框的垂直间隔,住址和就诊时间之间的垂直间隔,两个间隔不相等。应该调整。
7.按钮应该设置快捷键。比如保存按钮的文本应该设计成“保存(&O)”,取消按钮的文本应该设计成“取消(&C)”。
8.控件区域的左右边缘应该对齐。比如联系电话输入框的右边缘和其他控件没有对齐。此时需要设置该行放置3个文本框,然后将电话输入框靠右移动一些。
9.对话框框最上面的控制栏应该去掉最大化最小化按钮。
■■■■小结
合抱之木,生于毫末;九层之台,起于累土。一个高品质的软件既需要高瞻远瞩的宏观架构,也需要细节处的点点滴滴来的落地实现。Windows窗体设计是个细致活,需要行业内数万名一线程序猿掌握规则,按着套路来完成。仔细调整控件的每一个属性,精心优化每一处用户体验。这样才能做出精品软件让客户满意,给客户带来更大的价值。这些点点滴滴的毫末改进就能帮助实现HIT行业的价值最大化。
Q友评论Q友评论仅代表用户个人观点,不代表Q医疗立场