上图中,窗口是一个普通的VB Form,内置了一个WebBrowser控件,窗口加载的时候,它Navigate到一个本地Html页面,当我们在TestMe 链接的右面输入学号,点TestMe 链接后,即可在下面的表单中显示该学号所对应的学生的信息。如何实现的呢?我们来看Html页面里的代码,看点TestMe 到底发生了什么。 原来它调用了在Html页面中定义的Js脚本函数Test ,其定义如下: <script language="JavaScript"> var host; //COM 宿主 function Test() { var ost; //存储从COM宿主获得的学生实例,对应的是一个Student类,该类的定义 //在COM宿主中,成员有Name,Age,Phone三个属性字段 var sno; //学号 host=window.external; //获得COM 宿主 sno= document.forms[0].elements[0].value; ost =host.GetStudent(sno); //同COM进行交互,获得Student实例 document.forms[1].elements[0].value =ost.Name; document.forms[1].elements[1].value =ost.Age; document.forms[1].elements[2].value =ost.Phone; } </script> 这个例子非常简单,但是已经把整个工作地过程都讲述完毕了。事实上这个COM宿主,我们可以把它看作是对JavaScript语言的扩充,增加一些我们想要的定义,我们可以把它做得非常强大而健壮,包括业务逻辑、数据访问、网络通信、部分界面表示等。 这样做的好处有四: 1、成本节约。熟练的网页设计师在西安的工资待遇大概1000-1500,程序员却不止这个价格。 2、实现业务逻辑与界面的分离。程序发布和维护升级成本将更低。 3、做出的界面更美观。 4、人员招聘将不再难。招聘一名VB6程序员要比招聘一名网页设计师难度要大得多。三、关于VB6下使用.NETFrameWork的简易实现: 作为一名程序员,我们该对所使用平台的优劣势有充分的认识,java的兴起,跟它有一套成熟而庞大的类库有直接的关系,.NetFrameWork 到目前发展到3.0版本,几乎您所有想得到的应用,都可以找到对应的支撑,从而大大简化您的开发工作。而VB6在这方面是很弱的。为VB6提供.NETFrameWork的简易实现,有这两个方面的作用: 1、方便您的产品未来向.NET迁移。 2、类库提供的类本身可节省您的代码,提高开发效率。 示例: Dim oAl As New ArrayList Call oAl.Add(“A”) Call oAl.Add(“B”) Call Console.WriteValue(oAl.Count) '再如: Dim oSB As New StringBuilder Set oSB = oSB.Append("a").Append("b").Append("c") ‘//或 Call oSB.Append("a").Append("b").Append("c") Call Console.WriteValue(oSB.ToString) '//输出 abc 以上代码不管是在我们的体系下,还是在.NET下,都是可以运行的。 对于 String 类,我们做了特殊的处理,因为在VB6下,我们不能将类直接命名为String。所以类名以DyString的形式出现,该类实现了.NET3.0中String类的所有接口。使用方法大体一致,这里要讲一下去年代码生成器开发过程中的一段小插曲,最初没有DyString类,而代码生成器本身是解析既有脚本然后生成目标代码,需要进行大量的字符串处理,用VB自身VBA.Strings下的函数已经远远不能满足需要,所以干脆花了一天半时间写了.NET 下String,Char两个类在Vb6下的实现,其结果是大大节省了生成器的开发时间,缩短了开发周期。DyString的类浏览如下图: 图片(请于此处下载原文 http://99ef.com/我的技术概要.doc ) 示例如下: Dim s As dyString Dim i As Integer Dim en As dyCharEnumerator '//枚举子 Dim c As dyChar '//Char Set s = NewdyString("abc") Set s = s.Concat("header ", " backer") Call Console.WriteValue(s) '//输出 header abc backer i = dyString.Compare(s, "header abc backeR", vbTextCompare) Call Console.WriteValue(i) '//输出 0 i = dyString.Compare(s, "header abc backeR", vbBinaryCompare) Call Console.WriteValue(i) '//输出 1 Set en = s.GetEnumerator '//获得枚举子 Do While en.MoveNext Set c = en.Current Call Console.WriteValue(c.ToUpper.ToString) '//输出每个字符的大写形式 Loop 这样的类还有很多,就不一一介绍了。
四、关于UI 贴个图先,这是我为上一家物流企业设计的触摸屏程序UI运行效果截屏。 图片(请于此处下载原文 http://99ef.com/我的技术概要.doc ) 千万不要被它的外观所迷惑,上面看到的窗口、小键盘、按钮、CheckBox、Label、Frame等,均不是由您所想象中的窗口和控件组成。整个界面是一个PictureBox内置在一个窗口中,您所看到的一切,均由API绘制在这个PictureBox上,背后是一套类在处理显示工作和响应各类事件,就这个界面的构建及运行控制所写的代码,大概500行,大量核心代码是在这个类库中。类库浏览图如下: 图片(请于此处下载原文 http://99ef.com/我的技术概要.doc ) 再贴一个运行时的设计效果,小锁代表当前选中的对象处在是锁定状态,这可不是在VB6窗口设计器中(小锁模仿自Vs.Net2005 IDE): 图片(请于此处下载原文 http://99ef.com/我的技术概要.doc ) 下面一个套餐的设计界面,是我自己的项目中的一个运行时示例截屏。 图片(请于此处下载原文 http://99ef.com/我的技术概要.doc ) 左边是设计区,支持移动对象、Resize对象、多选、Ctrl连选、鼠标拖拽区域圈选、鼠标拖放等,右边是属性窗口,属性窗口模仿了.NET2005属性窗口的大部分功能,包括数据验证等。套餐所包含的商品,均是以拖拽的方式放到左边的设计区,用户所要做的工作就是在右边的属性窗口中进行属性设置。 举这个例子,是想说明一个理念,即UI以可视化(Visual Style)的方式同用户交互,这方面,IDE的窗口设计器是走在前面的。管理类软件的开发工作大都是数据库开发,界面展现中,有大量的数据增删改的操作,遍布在不同菜单下,不同的窗口中,试想,如果我们IDE的窗口设计器,也有那么多的增删改按钮和菜单,每个控件也有其对应的业务窗口进行新增、修改和查看,那么,我们的界面仅仅是添加一个TextBox和一个CommandButton,我们就要分别打开一个新增业务窗口,进行编辑后保存,修改的时候,也是如此,我们将面对多少个业务窗口呢?系统又需要设计出多少个针对增删改的业务窗口呢?IDE的设计理念,值得我们做管理类软件的人去学习。 自从有了可视化(Visual)编程后,我们发现很难灵活地去展现界面,因为我们的创作灵感被窗口和上面遍布的控件给禁锢住了,这也就是为什么游戏软件,很少是以拖拽控件的方式完成的原因,这也是VC中MFC大量的可视化组件都是以类而非控件的形式存在的原因。 通过这套类库,您可以以代码的方式,灵活构建您的界面,没那么多的窗口和控件,也就没那么多的句柄和hDc,耗费的计算机资源将大大减少,灵活性大大增加。 专业的UI设计,数据验证和录入提示是必不可少的,为此,我们去年专门做了一个专业的Validation库,可胜任80%的常见验证工作和录入提示工作。想想我们正常的验证代码是不是都放在保存按钮Click事件下或是控件的Validate事件中呢?您所常见的验证代码是不是都象下面的这样一段呢: Private Sub cmsSave_Click() If VBA.Len(txtName.Text) = 0 Then MsgBox "姓名不能为空,请输入姓名!" Call txtName.SetFocus txtName.SelStart = 0 txtName.SelLength = VBA.Len(txtName.Text) End If
If VBA.Len(txtName.Text) > 4 Then MsgBox "姓名长度不能超过4位,请重新输入姓名!" Call txtName.SetFocus txtName.SelStart = 0 txtName.SelLength = VBA.Len(txtName.Text) End If 。 。 。 End Sub 这里只有数据验证的部分,没有录入时的提示功能,已经显得很冗长了,另外,Call txtName.SetFocus 这行代码,我们还没考虑txtName的Enable属性为False的情况下会发生的运行时错误,如果考虑上的话,代码会更加冗长。看看用了 DyValidation类库后,将会是什么样子的呢? Option Explicit Dim m_fv As IValidate ‘//对当前表单进行验证的对象 Private Sub Form_Load() Dim oCVs As ElementValidations ‘//验证任务集合 Dim oCV As ElementValidation ‘//某一个单一验证任务 Set oCVs = NewElementValidations Set oCV = NewElementValidation("姓名", txtName, _ NewTip("姓名不能为空"), _ dyNValidateNotNull) ‘//生成对 txtName 进行验证的任务 ‘// NewTip("姓名不能为空") 是获得焦点后的录入时提示 ‘// dyNValidateNotNull 代表对 txtName 进行数据的非空验证。 Call oCV.Validates.AddLenValidate(dyRLMustLower, 0, 4) ‘//验证的规则中增加长度验证,长度必须小于等于4 Call oCV.Validates.AddForbiddenValidate("!@#$%^&*-") ‘//禁忌录入,输入的内容里不能包含!@#$%^&*-等特殊字符 Call oCVs.Addex(oCV) Set m_fv = NewFormValidation(, , , oCVs) End Sub Private Sub cmdValidate_Click() If m_fv.Validate(Nothing) Then MsgBox "通过" End Sub 上述代码,有一个特点,就是猛一看,感觉有很多废的代码,这种模式就是这么个特点,再小,你也得把框架搭起来,但是搭起框架后,几乎是一个验证规则一行代码就可以搞定,以下是运行效果截屏,获得焦点时提示: 图片(请于此处下载原文 http://99ef.com/我的技术概要.doc ) 超过限定长度(4位)时点Validate按钮时所进行的提示. 图片(请于此处下载原文 http://99ef.com/我的技术概要.doc ) 当然,DyValidation库远不止这么多,它还支持函数指针,支持.NET下所炫耀的匿名委托(Delegator),从而可以将个性化验证的代码放到任意您想放的地方。
五、关于通用报表组件 在去年的创业中,我把报表展现,放在一个很重要的位置。用了一个半月的时间和精力,集中投入到此方面的研发工作,旨在为用户提供简单的二次数据加工及格式化输出打印预览的灵活性。浏览的系统报表可保存,保存后的文件有极大的压缩比,方便互联网传输,曾经测过一个6万条数据的报表,保存后文件仅占256k空间。同时提供权限管理,保存后的报表文件,可指定用户输入指定密码后方可进入查看。组件本身支持多文档,用户可根据自己的需要,打开系统的若干张报表(如销售日志、系统日志),保存后,以后打开该文件,会自动打开这几张报表,相当于定制了专属于他自己的报表方案。 不啰嗦了,贴图先: 图片(请于此处下载原文 http://99ef.com/我的技术概要.doc) 由于该主题全是图片,所以请下载原文来读。 整体运行效果 权限管理 打印设置 打印预览 查找和筛选:查找同Excel,但是Excel不提供筛选功能。 查找、筛选、替换、全部替换:同Excel 设计表:瞧最右面一列式新增的一列。提供字符型、数值型、时间型和逻辑型四种数据类型参与运算和展现。 重命名表 只读表:让表变成只读,左上角加上锁的图标。 编辑表说明:针对每张报表,可以编辑其说明。 添加新的一列:可选择数据类型,表名和标题可以不一样。并且标题可以重复。 列重命名:对列提供重命名的操作。这里一定要注意,列名是唯一并且不能重复的,而标题可以重复,可以是任意字符。 更改列数据类型。 列只读:将列设为只读后,列首将出现小锁图标 冻结列:被冻结的列,滚动条滚动时,这两列不参与滚动。 筛选方案的制定:定义的筛选方案可以被管理起来,同时在数据->筛选当前表->执行筛选方案下会出现以他们命名的子菜单,如上图中的 A 和 B。 排序:如果选中一行,那么是对整表选中的列进行排序,如果选中行数大于1行,那么直对选中区域进行排序。 多列排序:同Excel 分类汇总:参考Excel,但是外观和效果,比它的要好,使用起来要灵活一些。 简易计算:对选中的区域进行对应的运算。 格式:是针对选中的部分所进行的外观格式方面的设置。包括设置字体、字体大小、是否粗、斜、下划线等,居左、居中、居右、单元格背景色、字体颜色、标题栏颜色、网格线类型、标题栏字体颜色、标题栏字体、行间隔颜色等。 其他:左移和右移,针对当前表或当前列。进行位置上的向左或向右移动。 上移和下移,针对选中的行。进行位置上的向上或向下移动。 同时表格提供编辑的功能,比如键入内容,复制、剪切、粘贴、清除等。 报表组件初步具备了一个多文档表格软件的雏形,在开发过程中,感觉最难的部分,是在撤销和重做上,最关键是所有的操作,都支持了撤销和重做,如汇总等。汇总的操作,Excel是不提供撤销和重做的,我在开发过程中,彻底理解了为什么Excel作为微软的产品却不提供汇总的撤销和重做功能,因为太复杂了,为了建立撤销和重做的机制,最起码一半的研发投入放在了上面。六、关于部署:纯绿色化+0配置 1、也谈纯绿色化: 想想JAVA ,jar包部署在那里就可以运行了,不用安装,不用注册,一直都想在COM下也实现类似的应用。但是,我们没有注意到这两者有本质区别,jar包本身就是扩展名为 jar的压缩文件,其打开的方式为javaw.exe,就象Doc文档的打开方式是MsWord一样。我们从来没听说 Doc文件要打开,还需要对其进行一番安装和注册,那么,java编译出的东东不需要安装和注册也就不难理解了。 但是Java的类加载器,的确是一个很不错的概念。如果在COM下模拟类加载器,一个Dll,不注册,也就可以应用。 我们提供的模型大致如下: 每一个Library都有一个类名称为Lib,它扮演这个类库中所有Public型类构造器的角色,比如DySchoolMgrLib库的Lib类,有NewStudent等函数,来进行Student类实例的构造。然后,我们通过一个类ClassLoader,它只有一个成员函数其原型为 Function LoadClass(ByVal sDllFile As String, ByVal sClassName As String) As Object sDllFile 类库文件地址 sClassName 要加载的类的名称 使用示例代码如下: Dim oLib As Object Dim oStudent As Object Set oLib = ClassLoader.LoadClass("C:\Windows\System32\DySchoolMgrLib.dll", "Lib") ‘//加载DySchoolMgrLib 的 Lib 类 Set oStudent = oLib.NewStudent("{a}", "MyName", 15) ‘//New 出Student实例 按照这个思路,主程序,仅仅起网络通信、类加载器、在线升级的作用,业务逻辑层、数据访问层、界面表示层我们都封装到Dll中,从而实现了真正意义上的纯绿色化,软件再也不需要安装,部署即是文件拷贝传播的过程。2、也谈0配置: 我们在网吧里玩CS反恐精英,谁建了个服务器,从来没听说过其他的机器上还需要进行配置,才能进入游戏来玩,这是一个典型的0配置案例。
其实我们看到的CS架构的软件,大都不是真正意义上的CS软件,因为这个S在现实中一般都指向的是SQLServer后台(Or Oracle?),包括用友的U8和金蝶的K3。这样,我们软件的部署工作,需要在各客户端进行大量的配置,比如有一个界面或是在一个文件里(XML or Ini)配置SQLServer后台的IP地址,SQLServer SA 的密码等,客户端越多,部署的工作就越麻烦。这样做,还有一个非常不好的地方,就是数据库后台必须是暴露的,局域网访问没关系,但是广域网访问,SQLServer后台直接暴露出来,还是有失安全考量的,恰恰糟糕的是,我所见的CS架构软件的广域网应用,大都是这个方案(关于如何解决这个问题,请参考关于串行化和网络通信部分)。 说了这么多,我们发现问题完全出在S的缺失上,在这样的情况下,我们必须有自己的 S,由S来访问SQLServer后台,C来访问S,那么对SQLServer的配置只需要在S上配置就OK了,局域网下C端如何在0配置的情形下访问到S呢?答案很简单,UDP协议。具体实现,可以参考MSDN关于UDP的说明文档。七、关于串行化和网络通信。 试想,一只猫的实例可以从一台计算机爬到另一台计算机,那该多么神奇呀,利用上述UI库定义的显示组件(比如一个全新样式的按钮或CheckBox),可以从服务端下载到客户端并显示,是多么激动人心呀。针对前者,您也许会想,这很简单,A端初始化成一个猫实例,将其各属性值由分割符号(比如管道符|)进行串接而成字符串,由Winsock.SendData 到B端,B端初始化一个猫实例,然后将字符串进行分割分别对猫实例的各属性进行赋值,其实我在做初级程序员的时候,一直也都是这么做的。那么针对后者呢?这样的处理办法,类的数量是1,可能是可行的,但是类多了呢?将变得非常不可行。事实上,很多软件都是这样开发的,甚至很多的软件,代码里都没有类。今天我给您一个耳目一新的处理办法:DyServer加对象字节流串行化。 Dim oStudent As Object Dim sRequrest As String sRequrest = "http://192.168.1.255/GetStudent?No=001&Name=周辰垣" Set oStudent = RemoteGetter.GetObj(sRequrest) 目标是IP地址为192.168.1.255的DyServer,GetStudent 代表要获得对象类型为 Student,”?No=001&Name=周辰垣”,熟悉BS的人,该会清楚,意思为获得 No为001同时姓名为周辰垣的学生实例。如此这般,一个Student实例,就被从服务器上给请求过来了。
而在192.168.1.255主机上上,处理过程大抵如此: Dim oCds As DyADODB.ConditionClause Dim oStudent As Object Set oCds = NewConditionClause Call oCds.Append(Student.NewCondition(dyPropsStNo, dyEqual, "001")) Call oCds.Append(Student.NewCondition(dyPropsStName, dyEqual, "周辰垣")) Set oStudent =Student.GetInstanceFor(oCds) ‘//获取学生实例,上面有过这个示例代码,就不细细描述了。 Call Response.Write(oStudent) ‘//写入 Response 中,也就被发送到了客户端。[code=VB] [/code] 熟悉Asp或是Jsp的人也许会注意到这里的Response,其实这里的Response就是它在Vb6下的实现,底层用到的是对象的字节流串行化。幸运的是,很多Vb下用到的对象比如 StdPicture、Recordset、Collection等都可以用这样的原理,进行传递。例如Recordset: Dim oRs As ADODB.Recordset Dim sSQL As String sSQL="SELECT * FROM Student WHERE No ='001' AND Name ='周辰垣'" Set oRs = gCn.Execute(sSQL) Call Response.Write(oRs) '//写入 Response 中,也就被发送到了客户端。 通过这个例子,您会发现,客户端,是不需要直接连接SQLServer后台的,一样可以进行数据访问。 这样的模型,也许会让您感觉它仅仅适应于是Client 访问 Server的状况,如果Client访问Client呢?又该如何呢?如果您的概念里没有 Client ,在逻辑上,所有的都是DyServer,不就解决了客户端彼此访问的问题了么?事实上我就是这么干的。 这样,通信问题,对象与字节流串行化与反串行化,其具体细节,您都不需要考虑,一样可以建立复杂的通信模型,完成复杂的通信任务。结束语: 好久没写文字了,有些笔生,但幸好这些都是我耳熟能详、烂记于心的内容,所以洋洋洒洒一万余言,还是让我给折腾出来了,其中有精华,也有糟粕,希望您可以批判地去看它,不好的地方,请多指教,多提建议,好的地方,请鼓励我再接再厉,不管合作与否,希望我写的东西,能给您的开发,带来一些启发。如果您也有好的东西,也可与我分享,互相学习,共同提高。
就界面表达来说,我一直很欣赏Html的方式,文本、图片、影音、动画等,都可以自由表现,Html配合JavaScript做出的特效,哪怕在DreamWeaver里实现起来再简单,但是您在VB或是.NET下的WinForm上去做同样的事情都是很难的,这也是BS架构为什么能大行其道的原因之一。但是,Html+JavaScript所做的网页,如何同WinForm去进行交互,一直以来都是一个难题。 如果我们解决了这个问题,业务逻辑层、数据访问层用代码生成器生成,界面表示层我们用 Dreamweaver 来实现,配合Ajax中最流行的免费框架ExtJs(ExtJs到底有多酷多炫,参照下图),所用的人员为初级网页设计师,开发出的界面表示层如果发生变动,仅仅需要修改Html页面,而整个软件不需要重新编译发布,那该多好。这一切,对您来说,都是可以实现的。不多说,看ExtJs的炫图:
图片(请于此处下载原文 http://99ef.com/我的技术概要.doc )
ExtJs到底有多酷多炫,请搜索Google。下面我举个简单的COM+Html+JavaScript例子。
图片(请于此处下载原文 http://99ef.com/我的技术概要.doc )
上图中,窗口是一个普通的VB Form,内置了一个WebBrowser控件,窗口加载的时候,它Navigate到一个本地Html页面,当我们在TestMe 链接的右面输入学号,点TestMe 链接后,即可在下面的表单中显示该学号所对应的学生的信息。如何实现的呢?我们来看Html页面里的代码,看点TestMe 到底发生了什么。 原来它调用了在Html页面中定义的Js脚本函数Test ,其定义如下: <script language="JavaScript">
var host; //COM 宿主
function Test()
{
var ost; //存储从COM宿主获得的学生实例,对应的是一个Student类,该类的定义
//在COM宿主中,成员有Name,Age,Phone三个属性字段
var sno; //学号 host=window.external; //获得COM 宿主
sno= document.forms[0].elements[0].value;
ost =host.GetStudent(sno); //同COM进行交互,获得Student实例
document.forms[1].elements[0].value =ost.Name;
document.forms[1].elements[1].value =ost.Age;
document.forms[1].elements[2].value =ost.Phone;
}
</script> 这个例子非常简单,但是已经把整个工作地过程都讲述完毕了。事实上这个COM宿主,我们可以把它看作是对JavaScript语言的扩充,增加一些我们想要的定义,我们可以把它做得非常强大而健壮,包括业务逻辑、数据访问、网络通信、部分界面表示等。 这样做的好处有四:
1、成本节约。熟练的网页设计师在西安的工资待遇大概1000-1500,程序员却不止这个价格。
2、实现业务逻辑与界面的分离。程序发布和维护升级成本将更低。
3、做出的界面更美观。
4、人员招聘将不再难。招聘一名VB6程序员要比招聘一名网页设计师难度要大得多。三、关于VB6下使用.NETFrameWork的简易实现:
作为一名程序员,我们该对所使用平台的优劣势有充分的认识,java的兴起,跟它有一套成熟而庞大的类库有直接的关系,.NetFrameWork 到目前发展到3.0版本,几乎您所有想得到的应用,都可以找到对应的支撑,从而大大简化您的开发工作。而VB6在这方面是很弱的。为VB6提供.NETFrameWork的简易实现,有这两个方面的作用:
1、方便您的产品未来向.NET迁移。
2、类库提供的类本身可节省您的代码,提高开发效率。
示例:
Dim oAl As New ArrayList
Call oAl.Add(“A”)
Call oAl.Add(“B”)
Call Console.WriteValue(oAl.Count) '再如:
Dim oSB As New StringBuilder
Set oSB = oSB.Append("a").Append("b").Append("c")
‘//或
Call oSB.Append("a").Append("b").Append("c")
Call Console.WriteValue(oSB.ToString) '//输出 abc
以上代码不管是在我们的体系下,还是在.NET下,都是可以运行的。 对于 String 类,我们做了特殊的处理,因为在VB6下,我们不能将类直接命名为String。所以类名以DyString的形式出现,该类实现了.NET3.0中String类的所有接口。使用方法大体一致,这里要讲一下去年代码生成器开发过程中的一段小插曲,最初没有DyString类,而代码生成器本身是解析既有脚本然后生成目标代码,需要进行大量的字符串处理,用VB自身VBA.Strings下的函数已经远远不能满足需要,所以干脆花了一天半时间写了.NET 下String,Char两个类在Vb6下的实现,其结果是大大节省了生成器的开发时间,缩短了开发周期。DyString的类浏览如下图:
图片(请于此处下载原文 http://99ef.com/我的技术概要.doc )
示例如下: Dim s As dyString
Dim i As Integer
Dim en As dyCharEnumerator '//枚举子
Dim c As dyChar '//Char Set s = NewdyString("abc")
Set s = s.Concat("header ", " backer")
Call Console.WriteValue(s) '//输出 header abc backer
i = dyString.Compare(s, "header abc backeR", vbTextCompare)
Call Console.WriteValue(i) '//输出 0
i = dyString.Compare(s, "header abc backeR", vbBinaryCompare)
Call Console.WriteValue(i) '//输出 1 Set en = s.GetEnumerator '//获得枚举子
Do While en.MoveNext
Set c = en.Current
Call Console.WriteValue(c.ToUpper.ToString) '//输出每个字符的大写形式
Loop 这样的类还有很多,就不一一介绍了。
四、关于UI
贴个图先,这是我为上一家物流企业设计的触摸屏程序UI运行效果截屏。
图片(请于此处下载原文 http://99ef.com/我的技术概要.doc )
千万不要被它的外观所迷惑,上面看到的窗口、小键盘、按钮、CheckBox、Label、Frame等,均不是由您所想象中的窗口和控件组成。整个界面是一个PictureBox内置在一个窗口中,您所看到的一切,均由API绘制在这个PictureBox上,背后是一套类在处理显示工作和响应各类事件,就这个界面的构建及运行控制所写的代码,大概500行,大量核心代码是在这个类库中。类库浏览图如下:
图片(请于此处下载原文 http://99ef.com/我的技术概要.doc ) 再贴一个运行时的设计效果,小锁代表当前选中的对象处在是锁定状态,这可不是在VB6窗口设计器中(小锁模仿自Vs.Net2005 IDE):
图片(请于此处下载原文 http://99ef.com/我的技术概要.doc ) 下面一个套餐的设计界面,是我自己的项目中的一个运行时示例截屏。
图片(请于此处下载原文 http://99ef.com/我的技术概要.doc ) 左边是设计区,支持移动对象、Resize对象、多选、Ctrl连选、鼠标拖拽区域圈选、鼠标拖放等,右边是属性窗口,属性窗口模仿了.NET2005属性窗口的大部分功能,包括数据验证等。套餐所包含的商品,均是以拖拽的方式放到左边的设计区,用户所要做的工作就是在右边的属性窗口中进行属性设置。 举这个例子,是想说明一个理念,即UI以可视化(Visual Style)的方式同用户交互,这方面,IDE的窗口设计器是走在前面的。管理类软件的开发工作大都是数据库开发,界面展现中,有大量的数据增删改的操作,遍布在不同菜单下,不同的窗口中,试想,如果我们IDE的窗口设计器,也有那么多的增删改按钮和菜单,每个控件也有其对应的业务窗口进行新增、修改和查看,那么,我们的界面仅仅是添加一个TextBox和一个CommandButton,我们就要分别打开一个新增业务窗口,进行编辑后保存,修改的时候,也是如此,我们将面对多少个业务窗口呢?系统又需要设计出多少个针对增删改的业务窗口呢?IDE的设计理念,值得我们做管理类软件的人去学习。 自从有了可视化(Visual)编程后,我们发现很难灵活地去展现界面,因为我们的创作灵感被窗口和上面遍布的控件给禁锢住了,这也就是为什么游戏软件,很少是以拖拽控件的方式完成的原因,这也是VC中MFC大量的可视化组件都是以类而非控件的形式存在的原因。 通过这套类库,您可以以代码的方式,灵活构建您的界面,没那么多的窗口和控件,也就没那么多的句柄和hDc,耗费的计算机资源将大大减少,灵活性大大增加。 专业的UI设计,数据验证和录入提示是必不可少的,为此,我们去年专门做了一个专业的Validation库,可胜任80%的常见验证工作和录入提示工作。想想我们正常的验证代码是不是都放在保存按钮Click事件下或是控件的Validate事件中呢?您所常见的验证代码是不是都象下面的这样一段呢:
Private Sub cmsSave_Click()
If VBA.Len(txtName.Text) = 0 Then
MsgBox "姓名不能为空,请输入姓名!"
Call txtName.SetFocus
txtName.SelStart = 0
txtName.SelLength = VBA.Len(txtName.Text)
End If
If VBA.Len(txtName.Text) > 4 Then
MsgBox "姓名长度不能超过4位,请重新输入姓名!"
Call txtName.SetFocus
txtName.SelStart = 0
txtName.SelLength = VBA.Len(txtName.Text)
End If
。
。
。
End Sub
这里只有数据验证的部分,没有录入时的提示功能,已经显得很冗长了,另外,Call txtName.SetFocus 这行代码,我们还没考虑txtName的Enable属性为False的情况下会发生的运行时错误,如果考虑上的话,代码会更加冗长。看看用了 DyValidation类库后,将会是什么样子的呢? Option Explicit
Dim m_fv As IValidate ‘//对当前表单进行验证的对象
Private Sub Form_Load()
Dim oCVs As ElementValidations ‘//验证任务集合
Dim oCV As ElementValidation ‘//某一个单一验证任务
Set oCVs = NewElementValidations
Set oCV = NewElementValidation("姓名", txtName, _
NewTip("姓名不能为空"), _
dyNValidateNotNull)
‘//生成对 txtName 进行验证的任务
‘// NewTip("姓名不能为空") 是获得焦点后的录入时提示
‘// dyNValidateNotNull 代表对 txtName 进行数据的非空验证。
Call oCV.Validates.AddLenValidate(dyRLMustLower, 0, 4)
‘//验证的规则中增加长度验证,长度必须小于等于4
Call oCV.Validates.AddForbiddenValidate("!@#$%^&*-")
‘//禁忌录入,输入的内容里不能包含!@#$%^&*-等特殊字符
Call oCVs.Addex(oCV)
Set m_fv = NewFormValidation(, , , oCVs)
End Sub
Private Sub cmdValidate_Click()
If m_fv.Validate(Nothing) Then MsgBox "通过"
End Sub
上述代码,有一个特点,就是猛一看,感觉有很多废的代码,这种模式就是这么个特点,再小,你也得把框架搭起来,但是搭起框架后,几乎是一个验证规则一行代码就可以搞定,以下是运行效果截屏,获得焦点时提示:
图片(请于此处下载原文 http://99ef.com/我的技术概要.doc ) 超过限定长度(4位)时点Validate按钮时所进行的提示.
图片(请于此处下载原文 http://99ef.com/我的技术概要.doc ) 当然,DyValidation库远不止这么多,它还支持函数指针,支持.NET下所炫耀的匿名委托(Delegator),从而可以将个性化验证的代码放到任意您想放的地方。
在去年的创业中,我把报表展现,放在一个很重要的位置。用了一个半月的时间和精力,集中投入到此方面的研发工作,旨在为用户提供简单的二次数据加工及格式化输出打印预览的灵活性。浏览的系统报表可保存,保存后的文件有极大的压缩比,方便互联网传输,曾经测过一个6万条数据的报表,保存后文件仅占256k空间。同时提供权限管理,保存后的报表文件,可指定用户输入指定密码后方可进入查看。组件本身支持多文档,用户可根据自己的需要,打开系统的若干张报表(如销售日志、系统日志),保存后,以后打开该文件,会自动打开这几张报表,相当于定制了专属于他自己的报表方案。 不啰嗦了,贴图先:
图片(请于此处下载原文 http://99ef.com/我的技术概要.doc)
由于该主题全是图片,所以请下载原文来读。 整体运行效果
权限管理
打印设置
打印预览
查找和筛选:查找同Excel,但是Excel不提供筛选功能。
查找、筛选、替换、全部替换:同Excel
设计表:瞧最右面一列式新增的一列。提供字符型、数值型、时间型和逻辑型四种数据类型参与运算和展现。
重命名表
只读表:让表变成只读,左上角加上锁的图标。
编辑表说明:针对每张报表,可以编辑其说明。
添加新的一列:可选择数据类型,表名和标题可以不一样。并且标题可以重复。
列重命名:对列提供重命名的操作。这里一定要注意,列名是唯一并且不能重复的,而标题可以重复,可以是任意字符。
更改列数据类型。
列只读:将列设为只读后,列首将出现小锁图标
冻结列:被冻结的列,滚动条滚动时,这两列不参与滚动。
筛选方案的制定:定义的筛选方案可以被管理起来,同时在数据->筛选当前表->执行筛选方案下会出现以他们命名的子菜单,如上图中的 A 和 B。
排序:如果选中一行,那么是对整表选中的列进行排序,如果选中行数大于1行,那么直对选中区域进行排序。
多列排序:同Excel
分类汇总:参考Excel,但是外观和效果,比它的要好,使用起来要灵活一些。
简易计算:对选中的区域进行对应的运算。
格式:是针对选中的部分所进行的外观格式方面的设置。包括设置字体、字体大小、是否粗、斜、下划线等,居左、居中、居右、单元格背景色、字体颜色、标题栏颜色、网格线类型、标题栏字体颜色、标题栏字体、行间隔颜色等。
其他:左移和右移,针对当前表或当前列。进行位置上的向左或向右移动。
上移和下移,针对选中的行。进行位置上的向上或向下移动。
同时表格提供编辑的功能,比如键入内容,复制、剪切、粘贴、清除等。 报表组件初步具备了一个多文档表格软件的雏形,在开发过程中,感觉最难的部分,是在撤销和重做上,最关键是所有的操作,都支持了撤销和重做,如汇总等。汇总的操作,Excel是不提供撤销和重做的,我在开发过程中,彻底理解了为什么Excel作为微软的产品却不提供汇总的撤销和重做功能,因为太复杂了,为了建立撤销和重做的机制,最起码一半的研发投入放在了上面。六、关于部署:纯绿色化+0配置
1、也谈纯绿色化:
想想JAVA ,jar包部署在那里就可以运行了,不用安装,不用注册,一直都想在COM下也实现类似的应用。但是,我们没有注意到这两者有本质区别,jar包本身就是扩展名为 jar的压缩文件,其打开的方式为javaw.exe,就象Doc文档的打开方式是MsWord一样。我们从来没听说 Doc文件要打开,还需要对其进行一番安装和注册,那么,java编译出的东东不需要安装和注册也就不难理解了。 但是Java的类加载器,的确是一个很不错的概念。如果在COM下模拟类加载器,一个Dll,不注册,也就可以应用。 我们提供的模型大致如下: 每一个Library都有一个类名称为Lib,它扮演这个类库中所有Public型类构造器的角色,比如DySchoolMgrLib库的Lib类,有NewStudent等函数,来进行Student类实例的构造。然后,我们通过一个类ClassLoader,它只有一个成员函数其原型为
Function LoadClass(ByVal sDllFile As String, ByVal sClassName As String) As Object
sDllFile 类库文件地址
sClassName 要加载的类的名称
使用示例代码如下: Dim oLib As Object
Dim oStudent As Object
Set oLib = ClassLoader.LoadClass("C:\Windows\System32\DySchoolMgrLib.dll", "Lib")
‘//加载DySchoolMgrLib 的 Lib 类
Set oStudent = oLib.NewStudent("{a}", "MyName", 15)
‘//New 出Student实例
按照这个思路,主程序,仅仅起网络通信、类加载器、在线升级的作用,业务逻辑层、数据访问层、界面表示层我们都封装到Dll中,从而实现了真正意义上的纯绿色化,软件再也不需要安装,部署即是文件拷贝传播的过程。2、也谈0配置:
我们在网吧里玩CS反恐精英,谁建了个服务器,从来没听说过其他的机器上还需要进行配置,才能进入游戏来玩,这是一个典型的0配置案例。
其实我们看到的CS架构的软件,大都不是真正意义上的CS软件,因为这个S在现实中一般都指向的是SQLServer后台(Or Oracle?),包括用友的U8和金蝶的K3。这样,我们软件的部署工作,需要在各客户端进行大量的配置,比如有一个界面或是在一个文件里(XML or Ini)配置SQLServer后台的IP地址,SQLServer SA 的密码等,客户端越多,部署的工作就越麻烦。这样做,还有一个非常不好的地方,就是数据库后台必须是暴露的,局域网访问没关系,但是广域网访问,SQLServer后台直接暴露出来,还是有失安全考量的,恰恰糟糕的是,我所见的CS架构软件的广域网应用,大都是这个方案(关于如何解决这个问题,请参考关于串行化和网络通信部分)。 说了这么多,我们发现问题完全出在S的缺失上,在这样的情况下,我们必须有自己的 S,由S来访问SQLServer后台,C来访问S,那么对SQLServer的配置只需要在S上配置就OK了,局域网下C端如何在0配置的情形下访问到S呢?答案很简单,UDP协议。具体实现,可以参考MSDN关于UDP的说明文档。七、关于串行化和网络通信。
试想,一只猫的实例可以从一台计算机爬到另一台计算机,那该多么神奇呀,利用上述UI库定义的显示组件(比如一个全新样式的按钮或CheckBox),可以从服务端下载到客户端并显示,是多么激动人心呀。针对前者,您也许会想,这很简单,A端初始化成一个猫实例,将其各属性值由分割符号(比如管道符|)进行串接而成字符串,由Winsock.SendData 到B端,B端初始化一个猫实例,然后将字符串进行分割分别对猫实例的各属性进行赋值,其实我在做初级程序员的时候,一直也都是这么做的。那么针对后者呢?这样的处理办法,类的数量是1,可能是可行的,但是类多了呢?将变得非常不可行。事实上,很多软件都是这样开发的,甚至很多的软件,代码里都没有类。今天我给您一个耳目一新的处理办法:DyServer加对象字节流串行化。 Dim oStudent As Object
Dim sRequrest As String
sRequrest = "http://192.168.1.255/GetStudent?No=001&Name=周辰垣"
Set oStudent = RemoteGetter.GetObj(sRequrest)
目标是IP地址为192.168.1.255的DyServer,GetStudent 代表要获得对象类型为 Student,”?No=001&Name=周辰垣”,熟悉BS的人,该会清楚,意思为获得 No为001同时姓名为周辰垣的学生实例。如此这般,一个Student实例,就被从服务器上给请求过来了。
而在192.168.1.255主机上上,处理过程大抵如此: Dim oCds As DyADODB.ConditionClause
Dim oStudent As Object
Set oCds = NewConditionClause
Call oCds.Append(Student.NewCondition(dyPropsStNo, dyEqual, "001"))
Call oCds.Append(Student.NewCondition(dyPropsStName, dyEqual, "周辰垣"))
Set oStudent =Student.GetInstanceFor(oCds)
‘//获取学生实例,上面有过这个示例代码,就不细细描述了。
Call Response.Write(oStudent)
‘//写入 Response 中,也就被发送到了客户端。[code=VB]
[/code] 熟悉Asp或是Jsp的人也许会注意到这里的Response,其实这里的Response就是它在Vb6下的实现,底层用到的是对象的字节流串行化。幸运的是,很多Vb下用到的对象比如 StdPicture、Recordset、Collection等都可以用这样的原理,进行传递。例如Recordset: Dim oRs As ADODB.Recordset
Dim sSQL As String
sSQL="SELECT * FROM Student WHERE No ='001' AND Name ='周辰垣'"
Set oRs = gCn.Execute(sSQL)
Call Response.Write(oRs)
'//写入 Response 中,也就被发送到了客户端。
通过这个例子,您会发现,客户端,是不需要直接连接SQLServer后台的,一样可以进行数据访问。 这样的模型,也许会让您感觉它仅仅适应于是Client 访问 Server的状况,如果Client访问Client呢?又该如何呢?如果您的概念里没有 Client ,在逻辑上,所有的都是DyServer,不就解决了客户端彼此访问的问题了么?事实上我就是这么干的。 这样,通信问题,对象与字节流串行化与反串行化,其具体细节,您都不需要考虑,一样可以建立复杂的通信模型,完成复杂的通信任务。结束语:
好久没写文字了,有些笔生,但幸好这些都是我耳熟能详、烂记于心的内容,所以洋洋洒洒一万余言,还是让我给折腾出来了,其中有精华,也有糟粕,希望您可以批判地去看它,不好的地方,请多指教,多提建议,好的地方,请鼓励我再接再厉,不管合作与否,希望我写的东西,能给您的开发,带来一些启发。如果您也有好的东西,也可与我分享,互相学习,共同提高。
有机会可以一起探讨呵呵。
不知为啥,怎么都转不到net中去
哪位大牛给俺指引方向啊