加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Delphi组件编辑器

发布时间:2020-12-15 09:13:05 所属栏目:大数据 来源:网络整理
导读:看到Dev中的cxGrid组件的编辑器很强大,于是很想探究一下,跟踪cxGrid的代码比较麻烦,但原理大概知道一二.首先来研究一下设计器双击cxGrid弹出一个编辑窗体,选择窗体中的一个内容后,属性编辑器中的内容也随着变化.有了这个特性,对于控件中的一些复杂成员(继承

看到Dev中的cxGrid组件的编辑器很强大,于是很想探究一下,跟踪cxGrid的代码比较麻烦,但原理大概知道一二.首先来研究一下设计器双击cxGrid弹出一个编辑窗体,选择窗体中的一个内容后,属性编辑器中的内容也随着变化.有了这个特性,对于控件中的一些复杂成员(继承于TPersistent类),如果需要对其进行设置,可以简化为在属性编辑器中进行.cxGrid的代码过于复杂,看看同样效果的标准控件TDataSet控件的字段编辑器的实现吧.

? 在Delphi的源码中找到DBReg单元,可以看到其注册的组件编辑器代码:

RegisterComponentEditor(TDataset,TDataSetEditor);//TDataSetEditor注册为TDataSet的组件编辑器,响应鼠标双击或右键事件

procedure TDataSetEditor.ExecuteVerb(Index: Integer);//双击或右键菜单触发
begin
? if Index = 0 then
??? ShowFieldsEditor(Designer,TDataSet(Component),GetDSDesignerClass);
end;

继续跟踪ShowFieldsEditor函数:

procedure ShowFieldsEditor(Designer: IDesigner; ADataset: TDataset;
? DesignerClass: TDSDesignerClass);
var
? FieldsEditor: TFieldsEditor;
? vShared: Boolean;
begin
? FieldsEditor := CreateFieldsEditor(Designer,ADataSet,DesignerClass,vShared); //创建字段编辑器窗体
? if FieldsEditor <> nil then
??? FieldsEditor.Show;//显示
end;

这个函数核心代码是对CreateFieldsEditor的调用,创建出字段编辑器窗体.跟踪进去看到:

function CreateFieldsEditor(Designer: IDesigner; ADataset: TDataset;
? DesignerClass: TDSDesignerClass; var Shared: Boolean): TFieldsEditor;
begin
? Shared := True;
? if ADataset.Designer <> nil then
? begin
??? Result := (ADataset.Designer as TDSDesigner).FFieldsEditor;
? end
? else
? begin
??? Result := TFieldsEditor.Create(Application);
??? Result.DSDesignerClass := DesignerClass;
??? Result.Designer := Designer;
??? {Result.Form := Designer.Form;}
??? Result.Dataset := ADataset;
??? Shared := False;
? end;
end;

TFieldsEditor类是继承于TDesignWindow的编辑器类,TForm是其基类.F12键弹出设计界面,在FieldListBox控件的OnClick事件中,可见到如下代码:

procedure TFieldsEditor.AListBoxClick(Sender: TObject);
begin
? UpdateSelection;
end;

procedure TFieldsEditor.UpdateSelection;
var
? I: Integer;
? Field: TField;
? ComponentList: IDesignerSelections;
begin
? if Active then
? begin
??? ComponentList := TDesignerSelections.Create;
??? try
????? with GetActiveListBox do
??????? for I := 0 to Items.Count - 1 do
????????? if Selected[I] then
????????? begin
??????????? Field := TField(Items.Objects[I]){Dataset.FindField(Items[I])};
??????????? if Field <> nil then ComponentList.Add(Field);
????????? end;
????? if ComponentList.Count = 0 then ComponentList.Add(Dataset);
??? except
????? raise;
??? end;
??? Designer.SetSelections(ComponentList);
? end;
end;

看到目标了,最终实现属性编辑器进行切换效果的代码是Designer.SetSelections(ComponentList);我们只需要根据设计界面选择的内容,准备好需要在属性编辑器中显示的对象,添加到TDesignerSelections对象中,调用Designer.SetSelections即可.

另外跟踪一下属性编辑器的编辑框工作原理。? Delphi的属性编辑器可以自动根据属性来调整编辑框的类别,如需要输入字符串的情况就生成一个Edit编辑框,而需要设置Boolean型的就生成CheckBox编辑框,对于集合类型的可以展开依次进行设置。这种方式比较灵活,而且运行我们定制控件的属性编辑器,指定编辑方式。? Delphi没有公开源码,可以跟踪EControl的TInspectorList控件源码,其最终也是调用Delphi控件注册的PropertyEditor来模拟Delphi中操作方式。使用Spy++查看EControl的TInspectorList和Delphi的组件属性编辑器,其原理都是一样的,绘制出所有组件的属性图,左边是属性名称,右边是属性的值,从上到下依次绘制出所有的组件属性。根据鼠标点击的位置获取到鼠标下方属性的索引,从而得到对应的属性,并将属性编辑框移到对应的位置。根据控件的属性所注册的编辑器类型生成属性编辑框的外观,如文本输入框、下拉选择框、复选框等等。? 打开EControl的edcPropEdit单元,查看UpdateEditState函数,可看到如下代码:??? atr := GetAttributes;??? if F_ReadOnly then EditStyle := ieSimple???? else if (paValueList in atr) then EditStyle := iePickList????? else if (paDialog in atr) then EditStyle := ieEllipsis?????? else EditStyle := ieSimple;? 看到属性的GetAttributes;方法调用了,这是我们下注册属性编辑器的时候指定属性编辑方式的,可以指定弹出对话框、下拉列表等等方式。这里根据注册的编辑方式修改了属性编辑框的外观,如果是paValueList则EditStyle设置为iePickList,属性编辑框的外观生成一个向下的黑色箭头;EditStyle为ieEllipsis的则在编辑框的后面绘制一个带...的按钮。? 请见ecExtEdit的PaintBtnGlyph函数:procedure TCustomEditEx.PaintBtnGlyph(Canvas: TCanvas; Rect: TRect);const? LeftOffs = 3;var? Flags: Integer;? W,G,I: Integer;? DC: HDC;begin? DC := Canvas.Handle;? Flags := 0;? case FEditStyle of?? iePickList:??? begin????? if FActiveList = nil then Flags := DFCS_INACTIVE????? else if FPressed then Flags := DFCS_FLAT or DFCS_PUSHED;????? DrawFrameControl(DC,Rect,DFC_SCROLL,Flags or DFCS_SCROLLCOMBOBOX);? //绘制DFCS_SCROLLCOMBOBOX风格的向下箭头??? end;?? ieEllipsis:??? begin????? W := 2;????? G := (FButtonWidth - LeftOffs * 2 - 3 * W) div 2;????? if G <= 0 then G := 1;????? if G > 3 then G := 3;????? Flags := Rect.Left + (FButtonWidth - 3 * W - 2 * G) div 2 + Ord(FPressed);????? I := Rect.Top + (ClientHeight - W) div 2 {+ Ord(FPressed)};????? PatBlt(DC,Flags,I,W,BLACKNESS);????? PatBlt(DC,Flags + G + W,Flags + 2 * G + 2 * W,BLACKNESS);??? end;? end;end;? 对于下拉框风格的属性编辑器来说,下拉框的值是如何填充的呢?打开edcPropEdit单元,查看DropDown函数:procedure TCustomPropertyEdit.DropDown;{$IFDEF EC_VCL9_UP}var wp: IWideProperty10;{$ENDIF}begin? if FPropertyEditor <> nil then?? begin??? PickList.Items.Clear;??? {$IFDEF EC_VCL9_UP}??? FWListItems.Clear;??? TWideStringList(FWListItems).Sorted := PickList.Sorted;??? if Supports(FPropertyEditor,IWideProperty10,wp) then????? wp.GetValues(GetWStr)??? else??? {$ENDIF}????? FPropertyEditor.GetValues(GetStr);?? end;? inherited;end;? 核心代码是FPropertyEditor.GetValues(GetStr); 其中的GetStr是一个函数名称,定义为:procedure TCustomPropertyEdit.GetStr(const S: string);begin? PickList.Items.Add(S);end;? 由属性编辑器在获取到值后自动调用GetStr将获取到的内容填充到PickList列表中.而属性编辑器是在UpdateEditor函数中进行设置的,鼠标在点击一个属性的时候会调用UpdateEditor函数。--------------------- 作者:henreash 来源:CSDN 原文:https://blog.csdn.net/henreash/article/details/7371897 版权声明:本文为博主原创文章,转载请附上博文链接!

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读