前言
** Delphi被称为第四代编程语言,它具有简单、高效、功能强大的特点。和VC相比,Delphi更简单、更易于掌握,而在功能上却丝毫不逊色;和VB相比,Delphi则功能更强大、更实用。可以说Delphi同时兼备了VC功能强大和VB简单易学的特点。它一直是程序员至爱的编程工具。 。下的特性:基于窗体和面向对象的方法,高速的编译器,强大的数据库支持,与Windows编程紧密结合,强大而成熟的组件技术。但最重要的还是Object Pascal语言,它才是一切的根本。Object Pascal语言是在Pascal语言的基础上发展起来的,简单易学。Delphi提供了各种开发工具,包括集成环境、图像编辑(Image Editor),以及各种开发数据库的应用程序,如DesktopDataBase Expert等。除此之外,还允许用户挂接其它的应用程序开发工具,如Borland公司的资源编辑器(Resourse Workshop)。在Delphi众多的优势当中,它在数据库方面的特长显得尤为突出:适应于多种数据库结构,从客户机/服务机模式到多层数据结构模式;高效率的数据库管理系统和新一代更先进的数据库引擎;最新的数据分析手段和提供大量的企业组件。“真正的程序员用c,聪明的程序员用Delphi”,这句话是对Delphi最经典、最实在的描述。
在中国大陆,又由于某某公司商业策略的考量,Delphi的市占率越来越少。现在大部分的Delphi用户,多为旧版本培养出来的老客户,而后来的Delphi用户则是逐渐减少。之所以会有这种现象,除了微软与Java两大阵营的竞争效应,以及Delphi本身就缺乏中文教材的因素之外,在程序设计的正规教育体系中,此开发工具亦非主流学科。在这样的环境下,如果Delphi得不到强大的软件公司作为后台来重点开发它,就很难再次崛起。
(先声明一下,本文针对的是一些有一定java或c开发能力的猿,如果是小白也可以参考。。)
一、 开发工具
1.使用的delphi开发工具是XE2,因为比较老的开发工具,考虑到兼容性, 使用windows Server操作系统,无奈就装了个虚拟机,在上面的安装XE2我就不说了,自己慢慢百度吧,我也是直接在虚拟机上运行安装有XE2的虚拟系统盘。
2.有项目就直接打开.dproj结尾的项目文件;开发主要用到.pas文件(单元代码源文件),和.ini文件(配置文件);这两个文件基本搞定基本开发功能。下面一图介绍该开发工具常用到的地方;如还有其他需要用到的功能请自行研究。
常用几个快捷键:
F12:当前主窗口的图形窗口与代码之间的切换,
F11:快速打开对象编辑器,对象就是当前窗口的对象
F7: debug的单步进入执行
F8 :debug的单步跳过执行
Ctrl+F7 :debug时弹出查找变量框,只要复制变量名就可以显示对应的值
Shift+Alt+F11:可以在Structure到中找到的name在图形界面的哪个位置快速匹配
在主窗口的图形界面上,单击某个对象就可以在对象编辑器处查看该对象的所有属性和绑定的事件;双击可以自动定位到该对象的chang()事件当中去。
二、单元模块主要说明
1.这跟java或C一样也有类似主函数main方法一样,程式的入口从主单元文件开始。
单元文件下有两个东西:一个interface(类似接口声明)和implementation(类似接口的实现方法)
2. Interface接口部分
interface接口部分只是申明一些函数方法或过程、变量、常量、属性等等,都是公开的,属于全局变量;
使用uses可以引用其他单元进来使用;申明的函数本身没有实现部分,需要由继承它的类来实现函数,并且可以被不同的类继承,实现的方法也可以不相同;这部分我们不用管太多,用到就直接声明就可以了。
3.implementation实现部分
在实现部分,主要包括有uses(引入其他单元),procedure(单元过程),function(单元方法);procedure和function的区别就是:procedure为一个过程没有返回值,相当于有输入没输出;function为一个方法函数有返回值,有输入有输出;使用uses可以引入其他单元,在implementation里面可以使用不同的procedure或function去实现接口里面的函数或过程;刚才在上面也说了,接口部分的函数或过程只是声明,可以由不同的类继承并实现,而且可以有不同的实现方法。
三、具体使用函数或过程开发语法快速说明
1.procedure过程
1.1 在interface接口部分声明一个实现接口的类,其最终继承于TInterfacedObject(TForm还有继承).
TFrmTest = class(TForm)
MainMenu1: TMainMenu;
itemQuit: TMenuItem;
Option1: TMenuItem;
ChangePassword1: TMenuItem
procedure N5Click(Sender: TObject);
private//私有函数过程 变量
p_RuleName:String;
carton_Cap:Integer;
function checkerror(var strErrorDesc: string;var TempTxtFile :TextFile): boolean;
procedure Login(ModuleName:PChar);
procedure ChangePassword(UserName:String);
public//公有函数过程
procedure appendExtraParameters(tsParameterName, tsParameterValue:TStringList);
function PrinLabel(tsParameterName,tsParameterValue:TStringList):Boolean;
1.2写实现接口的过程
结构是这样的:TFrmTest为interface部分声明的继承其他类的接口成员变量;后Login为其接口方法,参数为PChar:指向一个以Null 结尾的每个字符为一个字节的字符串的指针 (可以看成是一个字符串);
里面的结构主要包括:
var:变量的声明;格式:变量名:类型。
赋值语句: 变量名:=值。
**begin…end: ** 过程的开始和结束;类似oracle存储过程的begin…end
**if 条件true/false the begin…end: ** 条件控制语句;跟其他语言的开发无差,多了点东西而已。
**for 条件 do begin…end : ** 一样是控制语句。
**while 条件 do begin…end: ** 也是控制语句。上面都是常用的控制语句,很简单。
最主要的是要知道程式都是自上而下执行的,中间可以条用其他单元的函数或过程;有没有类似C语言里面的程式。。
procedure TFrmTest.Login(ModuleName:PChar);
var
LibHandle:THandle;
GetFunctionModule:TGetFunctionModule;
LoginInfoTemp:FunctionModule;
i:Integer;
begin
LibHandle:=LoadLibrary('Login_SFIS.dll');
if LibHandle=0 then
raise EDLLLoadError.create('Unable to load dll');
@GetFunctionModule:=GetProcAddress(LibHandle,'GetFunctionModule');
if not (@GetFunctionModule=nil) then
begin
LoginInfoTemp:=GetFunctionModule(ModuleName);
SetLength(LoginInfo,0);
for i:=0 to 50 do
begin
if LoginInfoTemp[i]<>'' then
begin
SetLength(LoginInfo,i+1);
LoginInfo[i]:=Trim(LoginInfoTemp[i]);
end
else
begin
Break;
end;
end;
end
else
begin
raiseLastwin32error;
end
FreeLibrary(LibHandle);
end;
2.function函数
function跟procedure差不多,就是function里面多了个叫result的返回值而已;其他的相差不大。简单的很。。
function getQATypeOfModel(p_ModelName:String):String;
var
sqlStr:String;
begin
sqlStr := ' Select A_TYPE From C_TEST_DESC_T where TEST_NAME=:TEST_NAME and rownum=1 ';
result := '';
with DataFQC do
begin
qryPublic.Close;
qryPublic.SQL.Clear;
qryPublic.SQL.Add(sqlStr);
qryPublic.ParamByName('TEST_NAME ').AsString:=p_ModelName;
qryPublic.Prepare;
qryPublic.Open;
if not qryPublic.Eof then
Begin
result := qryPublic.FieldByName('A_TYPE ').AsString;
END;
qryPublic.Close;
End;
end;
四,图形界面结合代码快速开发
1.图形界面与代码的对应
按快捷键F12可以快速在图形界面和源代码之间的切换,或者直接点击Code和Design实现切换;
在图形界面UI方面,如果想要布局开发界面,则可以在Tool Palette工具板(可以直接按ctrl+alt+p弹出)上找到自己想要的图形,也可以直接搜索;不如布局一个button按钮,直接搜索btn,然后直接拖拉图形到主窗口就可以直接显示该窗口了,点击该窗口可以设置属性名、布局大小参数和绑定事件等等;跟android studio开发安卓图形界面差不多。在图形界面设置好属性和事件后,按F12后可以查看对应的代码,特别是事件,因为事件主要的作用是跟其他图形元素以及前后端数据进行交互;事件之间的相互交互通信是实现动态数据的关键。
2.几种常用的图形开发
2.1 Button按钮控件
主要有TButton、TBitBtn、TSpeedButton等;最常用的是TBitBtn跟TSpeedButton.
TBitBtn是TButton的派生类。增加的属性有Glyph、Kind、Layout、Margin、NumGlyphs、Spacing、Style。这些属性都影响按钮中的图象显示。其中,Kind属性还影响按钮的行为。例如,当Form1中的BitBtn1的Kind设置为bkClose时,按下该按钮将自动调用Form1.Close。因此,除了增加图象显示的特性之外,增加按钮行为也是TBitBtn于TButton的重要区别。
TSpeedButton是TGraphicControl的派生类,不具有TWinControl的重要特性:控制焦点。也就是说,你无法通过Tab键将焦点移动到一个TSpeedButton上,更不能试图按下空格键或者回车键来利用Windows标准输入方式“按”此类按钮。这就是TSpeedButton与TButton(包括TBitBtn)的重要区别。另外,利用TSpeedButton可以轻松制作透明按钮(Flat属性配合Transparent属性),这对实现比较酷的另类应用来说很有意义。
反正我主要使用到的是TSpeedButton,因为按钮的外观对我并没有多大用处;实用才是王道,所以选择实用性比较强大的TSpeedButton;
拖拉生成一个button之后,单击就可以在Object Inspector窗口查看该按钮的是什么类型的button,以及属性和事件;拖拉图形可以设定其大小,也可以在对象属性器里面直接设定;在里面可以设置字体,图形大小,是否可视,对象名称name等常用;注意在开发过程中要熟悉记得每个对象名称的name;因为对象name是唯一的,在开发中可以直接通过对象的name操作其对象的其他属性,相当于一个id这样;例如
spbRefresh.font.color:=clgreen;//设置该按钮字体的颜色为绿色;
对于事件,Button按钮的事件也挺多的,下面Onxxxx开头的都是该Button所具有的事件;其他的那些鼠标上下左右事件我就不说了;主要说一下点击事件OnClick,最常用;双击OnClick事件名,进入显示的当前类窗体的点击事件函数;例如:
procedure TFrm1.spbSendClick(Sender: TObject);
begin
//过程代码
end;
解析:Procedure是过程,TFrm1是窗体类的某个窗体,spbSend是你的按钮控件的名称,spbSendClick就是按钮的单击事件,(Sender:Tobject)就是发送消息到对象,这里默认为本窗体。Sender的类型是Tobject,是Tobject的派生类。每一个事件处理里面至少都有一个Sender参数;Sender的含义就是代表调用TFrm1.spbSendClick这个过程的控件. 由于Sender是TObject,所以任何object都可以赋给Sender.当你点击spbSend时,会产生一个spbSendClick事件,系统会把spbSend传递给spbSendClick过程作为参数,也就是所说的Sender.
2.2编辑框TEdit控件
常用的TEdit 组件主要用于数据的输入和显示和编辑等操作;这个用的比较多,利用好了数据可以实现动态关联并相互变化的效果;其对象的属性也是差不多,既然用的比较多,就拿几个比较重要的来说说:
**AutoSelect:**获取组件焦点。该属性只能在单行文本组件使用。值为True为选中。false则不选中。
**BorderStyle:**设置编辑框控件的外观效果。当值为bsSingle,为一个单线边框。为bsNone则无边框
**CanUndo :**该属性可以确定用户修改文本后可以undo方法回退。
**CharCase:**设置编辑框控件文本的大小写。
MaxLength: 设置文本的最大长度,为0表示长度没有限制。
**PasswordChar:**用来显示字符通常用(*)符号来设置密码
**ReadOnly:**设置只读,不能修改组件的文本内容
**SelStart:**设置文本的起始位置,如果为0,则指向第一个文本。
**SelLength:**设置文本的最大长度
组件的事件:
最常用的事件:
Onchange(编辑框组件被改变时触发该事件),例如验证文本的合法性:
procedure TForm1.Edit1Change(Sender: TObject);
var
str:string;
begin
str:='';
str:=Edit1.Text;
if Length(str)>0 then
if not(str[Length(str)]in ['0'..'9',#8]) then
begin
Application.MessageBox('请输入数字(','提示',MB_OKCANCEL+MB_ICONINFORMATION);
Edit1.Text:=LeftStr(Str,Length(Str)-1);
end;
Edit1.SelStart:=Length(Edit1.text);
end;
OnEnter(当组件接受输入焦点时产生该事件,当窗口组件为激活状态时,可使事件处理执行指定的处理),例如:
procedure TForm1.Edit1Enter(Sender: TObject);
begin
if Sender is TEdit then//当框为TEdit类型时被焦点选中时触发
(Sender as TEdit).Color := $00C8FFFF//改变显示的颜色
else if Sender is TComboBox then//当框为TComboBox类型时触发
(Sender as TComboBox).Color := $00C8FFFF;//改变其颜色
end;
OnExit(当焦点离开时触发的事件),例如:
procedure TForm1.Edit1Exit(Sender: TObject);
begin
if Sender is TEdit then//同上
(Sender as TEdit).Color := clWhite
else if Sender is TComboBox then
(Sender as TComboBox).Color := clWhite;
end;
OnKeyPress(按下键盘上的按键时触发的事件),例如:限制输入的数据;注意一下,回车键是#13
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if not (Key in ['a'..'z','A'..'Z',#8]) then
begin
Application.MessageBox('只能输入英文字符!', '提示', MB_OKCANCEL + MB_ICONINFORMATION);
Key := #0;
end;
end;
OnKeyDown(按下键盘上的按键触发该事件,该事件的key是一个整数,是由键盘的虚拟键值而定。)
例如:显示键盘上对应按下的键值:
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Edit2.Text := IntToStr(Key);
if Shift = [ssShift] then
Edit1.Text := 'Shift'
else if Shift = [ssAlt] then
Edit1.Text := 'Alt'
else if Shift = [ssCtrl] then
Edit1.Text := 'Ctrl'
else
Edit1.Text := '';
end;
还有OnClick(点击时触发的事件)等等;想用这些事件的话,直接在事件对应的右边自定义自己的事件名,双击一下就可以进去编辑自己的事件动作了,简单明了。
2.3下拉选框TComboBox组件
属性就不多说了,跟上面都是类似的;事件也不多说了,自己参考上面的事件,想用哪一些事件触发需要自己考量,比如你是想要点击的时候就触发事件还是值改变时触发事件,还是先点击后改变这种顺序触发事件也是可以的,就相当于嵌套一样,运用得当就得心应手。
2.4菜单栏TPopupMenu组件
该组件的属性跟事件比较少,主要是用来挂多菜单显示的,在里面你就可以设置多个子菜单项,然后在菜单项里面设置自己的事件等等;也比较简单。
菜单组件属性,事件一般不用,事件放在菜单项里面:
子菜单项的事件(跟上面的事件也是一样的,参考上面的说明):
五、组件之间的数据交互
重点的东西来了;图形做的再好看,再炫酷,不能显示用户想要的数据,看不到用户想要的结果;那只能凉凉。。。。不做表面功夫,注重内在数据交互才是目的。
**1.动态改变其他有关联的对象的值。**这里所说的对象就是在当前窗体里面显示放置的一些控件;如Button控件对象,编辑框控件对象,下拉框控件对象等等;在每个对象的事件中都可以通过其他对象name来改变其属性值。也可以利用其他对象的属性值作为判断检查条件等等。
procedure TFrm1.EditDataKeyPress(Sender: TObject; var Key: Char);//TFrm1窗体类的EditData控件对象的OnKeyPress事件
var //var开始定义局部变量,格式-->变量名[,变量名]:变量类型
sqlstr,route,group,groupLast:string;//注意,注释的还有使用:{注释内容}
JITBarcodeObject:ArrayBarcode;
LotNo:string;
begin //事件函数开始标志
if trim(editLength.Text)='0' then //editLength为另一个控件对象,trim(editLength.Text)获取对象的文本值
begin //trim()方法去前后的空格
ShowMessage(Values[7]); //调用方法,显示弹框
editLength.SetFocus;//调用该对象的属性方法,聚焦
Exit; //exit终止程序,停止本窗口执行。
End;
if key=#13 then //如果按了Enter键,#13是回车键的编码
Begin
LblErrorDesc.Caption:=''; //LblErrorDesc为某个区域对象,LblErrorDesc.Caption设置其显示标题
if IP1.Checked=true then // IP1为某个菜单项对象, IP1.Check检查该菜单项是否被勾选,true=被勾选
begin
//省略的代码
end;
end;
End;
2.访问后台数据库
一般我们都是先创建一个专门与数据库打交道的窗体单元,
unit UnitData1;
interface
uses
Windows,SysUtils, Classes, DB, DBTables;//使用到的其他单元,主要是数据库相关的DB、DBTables
type
TDataDB = class(TDataModule)//这个接口类最终继承TPersistent, IInterface, IInterfaceComponentReference类
db: TDatabase;//DB数据库
QryServerDateTime: TQuery;//DB查询,直接调用QryServerDateTime就可以执行sql进行语句查询了
QryPublic:TQuery;
PRONo: TStoredProc;//DB存储过程,直接调用PRONo就可以执行你自己的存储过程了
tblLine: TTable;//Db数据库表
var
DataDB: TDataDB;//声明一个接口类的变量DataDB,一般在其他单元通过该变量访问数据单元
implementation
Procedure TDataDB.SetsystemDateTime;//实现类方法,其中使用的QryServerDateTime做数据库查询
var
ServerDateTime: TDateTime;//声明变量
ServerSystemTime: TSystemTime;
begin
qryServerDateTime.Close;//先关闭链接池查询通道
qryServerDateTime.Prepare;//开始预编译语句
qryServerDateTime.Open;//打开查询通道
//封装参数Sysdate赋值给ServerDateTime
ServerDateTime := qryServerDateTime.FieldByName('Sysdate').AsDateTime;
DateTimeToSystemTime(ServerDateTime, ServerSystemTime);//调用过程,将查询到的时间推送给系统
SetLocalTime(ServerSystemTime);//同时设置本地时间同步
qryServerDateTime.Close;//最后又关闭连接池查询通道
end;
end.
在其他单元窗体调用数据库单元,执行数据库查询。
if IP1.Checked=true then //当IP1菜单项被勾选时执行下面
with DataDB.QryPublic do //调用数据单元DataDB的QryPublic做数据查询动作
begin
Close;//关闭数据连接
sql.Clear;//清除sql语句
//再add添加sql语句,变量值使用‘:变量名’来代替
sql.Add('SELECT TEST_NO FROM R_TEST_SN_T WHERE test_sn=:tset_sn_i');
parambyname('test_sn_i').AsString:= Trim(EditData.Text); //给sql的变量名赋值,赋值为其他对象的框值
Prepare; //sql预编译
open; //执行sql
if not eof then //判断返回结果集是否为空
begin
test_No:=fieldbyname('TEST_NO').AsString; //如果结果集不为空,则获取TEST_NO字段的值
close; //关闭流
if Lot_No=ComboLot.Text then //判断当前数据库返回的结果是否等于ComboLot对象的text文本值;
begin
ShowMessage('The shipping_sn have checked! please next shipping_sn again!');
EditData.Clear;//清空对象EditData对象的内容
Exit;//终止程序,跳出窗口
end;
end;
end
六,如果想退出后重新打开还能保留上一次的数据的话,需要写配置文件(.ini文件),当重启程序的时候就都配置文件,而配置文件里面可以保存着上一次窗体单元中所有对象的属性值;在运行程序的时候,在窗体的入口直接先读取配置文件里面的内容,有则取,无则略;取完值之后,如果当前控件对象还有触发事件则会自动触发事件去后台查数据库或只执行某个函数过程等等;等关闭的时候就把当前的值写到配置文件当中。
1.读取.ini配置文件
function GetSpecialFolderDir:string;//方法GetSpecialFolderDir用于创建文件流,参数为文件的绝对路径
var
pidl:pItemIDList;
buffer:array [ 0..255 ] of char ;
begin
SHGetSpecialFolderLocation( application.Handle , CSIDL_PERSONAL, pidl);
SHGetPathFromIDList(pidl, buffer);
result:=strpas(buffer)+'\MES\';
if not DirectoryExists(result) then
Createdir(result);
end;
//调用GetSpecialFolderDir方法创建文件流并读取Test.ini配置文件;iniTest为TiniFile类型的全局变量,TIniFile接口类
iniTest:=TIniFile.Create(GetSpecialFolderDir+'Test.ini');
//ReadingString(1,2,3);第一个参数:配置文件名;第二个参数key值,即[key]=[value]的左边key值;第三个参数为默认值
iniFQC.ReadString('Test','Check-SN','')='1'//读取Test配置文件的Check-SN对象的属性值;
2.写.ini配置文件
写的话跟读差不多,直接将readstring该为writestring就ok了;然后第三个参数就换成你想要的写的值了。
还有说一下,这货不区分大小写的,包括上面的也一样。
iniFQC:=TIniFile.Create(GetSpecialFolderDir+'FQC.ini');
iniFQC.WriteString('Test','Shift',CombShif.Text);
iniFQC.WriteString('Test','STATION',LblStation.Caption);
iniFQC.WriteString('Test','GROUP',Group_Name);
最后,如果想要弄个国际化的话,需要配置几个版本的配置文件;然后运行程序的时候根据选择的语言来读取对应的配置文件;这,就是国际化!