来源:www.cncfan.com | 2006-1-20 | (有4374人读过)
本文介绍了如何创建XML dataset,然后通过XML文件里的元素对XML“表”动态地创建相应控件。 本文介绍了如何创建XML dataset,然后通过XML文件里的元素对XML“表”动态地创建相应控件。文末所附的工程(Project)文件,使用户可以创建简单的(非关系型)数据库,这个数据库用XML文件存储。随后加载这个XML文件,经解析之后,可以在用户自行定义的“数据库”输入界面上动态生成控件。
大家都习惯使用Delphi来编程,实在太容易了。一桩困难的任务可以变得简单得多,伤透脑筋的结构设计可以轻易完成。感谢Delphi提供的这种灵活实用的工具,和优雅的界面风格。
Delphi 6问世,提供了新的组件,使开发者能够创建和管理XML文件。例如,在Interner组里提供了XMLDocument组件,在Data Access组里提供了XMLTransform, XMLTransformProvider, and XMLTransformClient组件集。
另外,在主菜单的File | New | Other | New下,你能找到XML数据绑定向导(XML Data Binding Wizard)。如果还不够,你可以使用XML映象器(Tools | XML Mapper or Programs | Borland Delphi 6 | XML Mapper)。尽管Delphi已经提供了如此之多的捷径来产生和使用XML文件,Borland公司还是向我们提供了“免动脑筋”的解决方案 - 使用“即试即用”的TClientDataSet。
从TClientDataSet到XML 用TClientDataSet创建和加载XML文件是很容易的,调用SaveToFile和LoadToFile函数即可,只要对函数的第二个参数设置相应的XML类型。实际上,有三个参数类型,一个是遗留的dfBinary二进制类型,另外是dfXMLUTF8和UTF8-based XML类型。
例如,可以这样存储文件(代码中的cds是TClientDataSet,FCurrentDataBaseName是文件名): cds.SaveToFile(Format('%s.xml', [FCurrentDataBaseName]), dfXMLUTF8); 这样你就得到了XML文件。
动态创建相应的控件 当然,在调用SaveToFile之前,还得创建数据库。这也不难,如下例。假设有两个TListBox控件,一个存放域名(lstbxFields),另一个存放相应的数据类型(lstbxDataTypes),动态创建控件的过程如下:
{----------------------------------------------------} procedure TfEZDBBrowserAddEditDelete.Cre8Controls; const { DYNAMIC_CONTROLS_TOP:第一个控件与顶部的距离} DYNAMIC_CONTROLS_TOP = 24; DYNAMIC_CONTROLS_LEFT = 12; { 为简短起见,省略其它常量} var i: Integer; iCurrentRow, iCurrentTop, iControlsOnThisRow, iCurrentWidthOfControls: Integer; MemoOnThisRow: Boolean;
procedure Cre8StringLabeledEdit(ACaption: String); begin with TLabeledEdit.Create(Self) do begin Parent := scrlbx; Name := Format('lbledt%s', [ACaption]); { HelpContext用于映射控件和表格索引(grid index 3 = (control with HelpContext 3), 等等)} HelpContext := HELP_CONTEXT_OFFSET+i; Hint := ACaption; EditLabel.Caption := ACaption; EditLabel.Font.Style := EditLabel.Font.Style+[fsBold]; Text := ''; { 扩展宽度,使可输入长串} Width := LABELED_EDIT_STRING_WIDTH; grdcds.Columns[i].Width := GRID_COLUMN_ALPHA_WIDTH; { 如果当前行宽不合适,去下一行} if (iCurrentWidthOfControls + LABELED_EDIT_STRING_WIDTH + (SPACE_BETWEEN_DYNAMICALLY_CREATED_CONTROLS * iControlsOnThisRow)) > MAX_WIDTH_OF_CONTROLS then begin inc(iCurrentRow); iControlsOnThisRow := 1; if MemoOnThisRow then Top := (SPACE_BETWEEN_DYNAMICALLY_CRE8ED_ROWS * iCurrentRow)+MEMO_HEIGHT else Top := iCurrentTop+SPACE_BETWEEN_DYNAMICALLY_CRE8ED_ROWS; iCurrentTop := Top; MemoOnThisRow := False; Left := DYNAMIC_CONTROLS_LEFT; iCurrentWidthOfControls := LABELED_EDIT_STRING_WIDTH; end else begin Top := iCurrentTop; if iCurrentWidthOfControls = 0 then begin Left := DYNAMIC_CONTROLS_LEFT; if iCurrentRow=0 then FFirstDynamicControl := Name; end else Left := iCurrentWidthOfControls + (SPACE_BETWEEN_DYNAMICALLY_CREATED_CONTROLS * iControlsOnThisRow) + DYNAMIC_CONTROLS_LEFT; inc(iControlsOnThisRow); iCurrentWidthOfControls := iCurrentWidthOfControls + LABELED_EDIT_STRING_WIDTH; end; OnEnter := DynamicControlEnter; OnExit := DynamicControlExit; Tag := LABELED_EDIT_STRING_TAG; end; end;
{ 创建Memos有一个特别问题:Memo很长时,要随时保留回车键("crlf")位置,用于调整屏幕顶端位置。}
procedure Cre8Memo(ACaption: String); var mmoLeft, mmoTop: Integer; begin with TMemo.Create(Self) do begin Parent := scrlbx; Name := Format('mmo%s', [ACaption]); HelpContext := HELP_CONTEXT_OFFSET+i; Hint := ACaption; Text := ''; Scrollbars := ssVertical; { 如果当前行宽不合适,去下一行} if (iCurrentWidthOfControls + MEMO_WIDTH + (SPACE_BETWEEN_DYNAMICALLY_CREATED_CONTROLS * iControlsOnThisRow)) > MAX_WIDTH_OF_CONTROLS then begin inc(iCurrentRow); iControlsOnThisRow := 1; if MemoOnThisRow then Top := iCurrentTop + SPACE_BETWEEN_DYNAMICALLY_CRE8ED_ROWS + MEMO_HEIGHT else Top := iCurrentTop + SPACE_BETWEEN_DYNAMICALLY_CRE8ED_ROWS; iCurrentTop := Top; Left := DYNAMIC_CONTROLS_LEFT; iCurrentWidthOfControls := MEMO_WIDTH; end else begin Top := iCurrentTop; if iCurrentWidthOfControls = 0 then begin Left := DYNAMIC_CONTROLS_LEFT; if iCurrentRow=0 then FFirstDynamicControl := Name; end else Left := iCurrentWidthOfControls + (SPACE_BETWEEN_DYNAMICALLY_CREATED_CONTROLS * iControlsOnThisRow) + DYNAMIC_CONTROLS_LEFT; inc(iControlsOnThisRow); iCurrentWidthOfControls := iCurrentWidthOfControls + MEMO_WIDTH; end; { 现在可以在行例设置memo了} MemoOnThisRow := True; mmoLeft := Left; mmoTop := Top; OnEnter := DynamicControlEnter; OnExit := DynamicControlExit; Tag := MEMO_MEMO_TAG; end; { 在上面设置标签} with TLabel.Create(Self) do begin Parent := scrlbx; { 也许可以不用Name... } Name := Format('lbl%s', [ACaption]); Caption := ACaption; Font.Style := Font.Style+[fsBold]; Left := mmoLeft; Top := mmoTop-LBL_VERTICAL_DELTA; end; end;
procedure Cre8MoneyLabeledEdit(ACaption: String); {为简短起见,这里省略代码,可到附件下载}
procedure Cre8IntegerLabeledEdit(ACaption: String); {为简短起见,这里省略代码,可到附件下载}
procedure Cre8FloatLabeledEdit(ACaption: String); {为简短起见,这里省略代码,可到附件下载}
procedure Cre8ComboBox(ACaption: String); {为简短起见,这里省略代码,可到附件下载} begin { 初始化变量} iCurrentRow := 0; iCurrentWidthOfControls := 0; iControlsOnThisRow := 0; iCurrentTop := DYNAMIC_CONTROLS_TOP; MemoOnThisRow := False; dm.cds.Active := True; { Do not want to do this in the DM's Create, as the CDS must be active first } dm.cds.LogChanges := False; for i := 0 to Pred(dm.CDS.FieldList.Count) do begin with dm.CDS.Fields do begin if (Fields[i] is TStringField) then Cre8StringLabeledEdit(Fields[i].FieldName) else if (Fields[i] is TMemoField) then Cre8Memo(Fields[i].FieldName) else if (Fields[i] is TCurrencyField) then Cre8MoneyLabeledEdit(Fields[i].FieldName) else if (Fields[i] is TIntegerField) then Cre8IntegerLabeledEdit(Fields[i].FieldName) else if (Fields[i] is TFloatField) then Cre8FloatLabeledEdit(Fields[i].FieldName) else if (Fields[i] is TDateField) then Cre8DateTimePicker(Fields[i].FieldName) else if (Fields[i] is TBooleanField) then Cre8ComboBox(Fields[i].FieldName); end; end; Popul8DynamicControls; end;
|