Delphi的Owner与Parent可以不一致,而且Owner不是必须存在(一共7个问题) good

 with…do语句的用法

问题1:Owner与Parent不一致:
新建叁个Form,下面放三个Button1,多少个Panel1,然后在Panel1上再放二个Button2,测验结果:
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(button2.Owner.Name); //
突显Form1,凡是拖控件放到Form上的控件,它们的Owner都以Form,可从TControl.ReadState步向深切商讨

ShowMessage(button2.Parent.Name); // 显示Panel1
end;
上行下效代码:
procedure TForm1.Button1Click(Sender: TObject);
var
Button2: TButton;
begin
Button2 := TButton.Create(Nil); 澳门新葡萄京官网首页,//
注意参数是Nil,即Owner为空

Button2.Name := ‘Button2’;
Button2.Left := 100;
Button2.Top := 100;

 

with…do语句用来钦赐三个块中的字段(记录的照旧目的的)、属性和章程所属的记录只怕目的。Delphi初行家只怕早就习于旧贯了之类的代码书写格式:

Button2.Parent := Panel1;
//ShowMessage(button2.Owner.Name卡塔尔; // 注意,不能读取
ShowMessage(button2.Parent.Name);
end;
剖判进度:
constructor TButton.Create(AOwner: TComponent);
inherited Create(AOwner);

一、Delphi中生成控件的两种艺术  
      
    
   —-    1、    Form(表单卡塔尔国设计中生成控件    
    
  
—-    在进展Form设计时,直接在控件工具箱选拔所需控件,再安装其性质与响应事件,这种方法相比较普及。    
    
   —-    2、    程序中动态变化控件    
    
  
—-    临时候,大家需求在程序运维时动态变化控件,那样做有两大亮点:一是能够扩充程序的眼观四路;二是要是生成控件的有个别与程序中间运维结果相关,显明方法一是无力回天的兑现的,务必用程序中动态变化方法。    
    
  
—-    程序中动态变化控件的艺术分为三步,首先,定义生成的控件类型,再用Create函数生成控件,最终对控件的相关属性赋值。以TButton控件为例,步骤如下:    
    
   —-    (1卡塔尔    定义控件类型    
    
       var  
             Button1:TButton;  
      
    
   —-    (2卡塔尔    生成控件    
    
   Button1:=TButton.    Create(self);  
   Button1.Parent:=Self;      
     //平日将其父控件设置为Self,假使不安装Parent的值,  
   则控件不会在荧屏  
   //显示出来  
      
    
  
—-    (3卡塔尔    设置任何性质及定义相关事件响应函数,如Caption,Left,Top,Height,Width,Visible,Enabled,Hint和onClick事件响应函数等。    
    
   二、动态变化控件方法的利用  
      
    
  
—-    在付出生产调解与治本种类中,须要动态变化排产安顿图,以甘特图表示,应用Shape控件来呈现器件的加工境况(每道工序的加工初始时间与结束时间卡塔尔国是极度符合的。应用Chart控件,对加工设备利用率以三维直方图展现,极其直观。现分别将在前后相继中动态生成Shape控件和Chart控件的经过加以注解。    
    
   —-    1、动态生成Shape控件展现排产安顿图(甘特图卡塔尔    
    
           procedure    TCreateMultiCharts.ProcCreateCharts;  
           var  
               i,j,Rows,Columns,RowSpace,ChartsHeight:Integer;    
               ShapeChart:array    of    array    of    TShape;  
           begin  
               Rows:=16;                  //Shape控件数组行数  
               Columns:=8;              //    Shape控件数组列数  
               RowSpace:=20;        //    Shape控件行间隔  
               ChartsHeight:=20;    //    Shape控件中度  
               SetLength(ShapeChart,Rows,Columns);    
     //设置ShapeChart数组大小  
               for    i:=0    to    Rows    do  
                   for    j:=0    to    Columns    do  
                   begin  
               ShapeChart[i][j]:=TShape.Create(self);  
               with    ShapeChart[i,j]    do  
               begin  
                   Parent:=Self;      //此行不可缺乏,  
   不然Shape控件在荧屏显示不出  
           Shape:=stRectangle;    //    Shape控件形状为矩形  
                   Top:=45+i*(RowSpace+ChartsHeight);  
                   Left:=Round(180+Q[i,j].StartTime);    
   //因Q[i,j].StartTime为实数,故需进行四舍五入取整  
                   Width:=Round(Q[i,j].Value)  
                   Height:=ChartsHeight;                  
                   Brush.Color:=RandomColor;    
   //自定义函数,表达附后  
                   Brush.Style:=bsSolid;    //设置填充方式  
                   Enabled:=True;  
               end;  
           end;  
   end;  
      
    
   —-    注:    
    
   —-    (1卡塔尔Q为一记录型二维数组,定义如下:    
    
       type  
             TempData=Record  
             Value:Real;  
             StartTime:Real;  
         end;  
         Q:array    of    array    of    TempData  
      
    
   —-    何况在另一经过已对Q的分占的额数举办赋值。    
    
  
—-    (2卡塔尔为了差距差别的构件,Shape以不一样颜色显示,那时候,调用了函数RandomColor。该函数为:    
    
         function    TCreateMultiCharts.RandomColor;  
   var  
       red,green,blue:byte;  
   begin  
       red:=random(255);  
       green:=random(255);  
       blue:=random(255);  
      
result:=red    or    (green    shl    8)    or    (blue    shl    16);  
   end;  
   —-    2、动态生成Charts控件的Chart塞里es组件,展现设备利用率    
    
   procedure    TFormMultiMachinesBurthen.  
   ShowMachineBurthenCharts;  
   var  
       i:Integer;  
       Burthen:Real;  
       SeriesClass:TChartSeriesClass;    
       NewSeries:array    of    TChartSeries;  
   begin    
       SetLength(NewSeries,CreateMultiCharts.Rows);  
       MachinesBurthenCharts.height:=200;  
       MachinesBurthenCharts.Width:=550;  
       for    i:=0    to    CreateMultiCharts.Rows    do  
       begin  
           SeriesClass:=TBarSeries;      //设置形状为三个维度条形图  
           NewSeries[i]:=SeriesClass.Create(Self);  
           NewSeries[i].ParentChart:=MachinesBurthenCharts;  
           NewSeries[i].Clear;  
           Burthen:=MachineBurthen[i];  
          
Burthen:=Round(Burthen*100卡塔尔(قطر‎/100;      //只取小数点后两位数字  
          
NewSeries[i].add(Burthen,”,NewSeries[i].SeriesColor);  
       end;  
   end;  
      
    
   —-    注:    
    
  
—-    (1)    MachineBurthen[i]为一实型数组,其值为对应器具的利用率,已在另一函数中计算取得;    
    
   —-    (2)    MachinesBurthenCharts为TChart控件,在type段说明。

var

constructor TButtonControl.Create(AOwner: TComponent);
inherited Create(AOwner);

==============================================================================  
   动态创设控件
==============================================================================  
  
function    DynaCreateComponent(Owner:    TComponent;    CompType:    TControlClass;    CompName:    String;    Left,Top,Width,Height:Integer):    TControl;  
   begin  
      
if    (Owner.FindComponent(CompName)<>nil)    and    not(Owner.FindComponent(CompName)    is    TControl)    then  
       begin  
           Result    :=    nil;  
           exit;  
       end;  
      
Result    :=    Owner.FindComponent(CompName)    as    TControl;  
       if    Result=nil    then  
       begin  
           Result    :=    CompType.Create(Owner);  
           with    Result    do  
           begin  
               if    Owner    is    TwinControl    then  
               begin  
                   SetBounds(Left,Top,Width,Height);  
                  
Parent    :=    TwinControl(Owner卡塔尔;{假诺是可视零件,则显得之}  
                  
if    Owner    is    TForm    then    TForm(OwnerState of Qatar.ActiveControl    :=    TWinControl(Result卡塔尔国;{设置窗口大旨}  
               end;  
           end;  
           Result.Name    :=    CompName;  
       end  
       else    {Result<>Nil}  
       if    not(Result    is    CompType)    then  
       begin  
           Result    :=    nil;  
           Exit;  
       end;  
       Result.Visible    :=    True;  
   end;  
   {    对于未知多少的控件组,利用TList  
       var    ControlList:    Tlist;    CreateNum:    integer;  
      
const    CreateClass    :    TControlClass    =    TButton;//能够自由改善TControlClass    =    TEdit或TPanel等。效果等同。  
       var    i:integer;    APoint:    Pointer;  
       ControlList    :=    TList.Create;  
       ControlList.Clear;  
       CreateNum    :=    10;  
       for    i:=1    to    CreateNum    do  
               begin  
                  
APoint    :=    Pointer(DynaCreateComponent(self,CreateClass,’Button_’    +    IntToStr(i),0,i*20+1,60,20));//创建  
                   ControlList.Add(APoint);  
               end;  
       TButton(ControlList.Items[i]).Caption    :=    ‘XXXX’;}

Button: TButton;

constructor TWinControl.Create(AOwner: TComponent);
inherited Create(AOwner卡塔尔; // 应该会调用TObject的NewInstance

动态创制控件收罗的

begin

constructor TControl.Create(AOwner: TComponent);
inherited Create(AOwner卡塔尔; // important 把控件归入到Owner的器皿中

Button := TButton.Create(Self,’,’,’);

procedure TControl.SetParent(AParent: TWinControl);
begin
if AParent = Self then
raise EInvalidOperation.CreateRes(@SControlParentSetToSelf); // 相对不容许父控件是控件本人
AParent.InsertControl(Self卡塔尔(قطر‎; // important7
一流入口,子控件处理与显示都在那地管理。AParent是父控件,Self是子控件,现在是在试行子控件的函数。
end;

Button.Parent := Self;

procedure TWinControl.InsertControl(AControl: TControl);
begin
// 以父控件的身价来进行此函数,AControl是子控件
if AControl is TWinControl then
begin
AControl.Perform(CM_PARENTCTL3DCHANGED, 0, 0卡塔尔(قطر‎; // fixme
独有Win控件才有3D属性?
UpdateControlState; // important7
二级入口,父控件刷新自个儿的情形。它会和煦寻觅最上层的Win控件,并判定是还是不是需求出示。假若急需出示,就调用API递归展现Win子控件,然后展现本身。
end;
end;

Button.Left := 50;

procedure TWinControl.UpdateControlState;
var
Control: TWinControl;
begin
// 以父控件的身价来实施此函数
while Control.Parent <> nil do
begin
Control := Control.Parent;
if not Control.Showing then Exit; //
假使有些祖先控件不显得,那么子控件也就毫无展现了
end;
if (Control is TCustomForm) or (Control.FParentWindow <> 0) then
UpdateShowing; //
突显控件的入口,里面做一层层的业务(检查实验控件本人是或不是供给突显,创建控件等等)
end;

Button.Top := 50;

procedure TWinControl.UpdateShowing;
begin
// 检查评定是不是供给出示
//
假如需求显示,那么先检查评定本人的FHandle是不是为空,为空的话就现场创制(这里会安装当前控件的Parent)
if ShowControl then CreateHandle;
// 递归呈现自身的全数子控件
// 等富有Win子控件展现达成,显示本身
Perform(CM_SHOWINGCHANGED, 0, 0State of Qatar; //
五级入口:新创立的控件,会在这里边调用API真正展现,并且到此截至。
end;

……

procedure TWinControl.CMShowingChanged(var Message: TMessage);
const
ShowFlags: array[Boolean] of Word = (
SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE +
SWP_HIDEWINDOW, // 贰个潜藏,此外都雷同
SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE +
SWP_SHOWWINDOW卡塔尔国; // 四个人展览示,SWP_NOMOVE表示地方不改变 
begin
// important
五级入口:调用API真正展现Windows窗口。屏蔽那句,一切Form的子控件都不呈现
SetWindowPos(FHandle, 0, 0, 0, 0, 0, ShowFlags[FShowing]卡塔尔国; //
API,更动窗口的尺码,地方和Z序。注意最终二个参数,是或不是激活就看它的意义。
end;

end;

中间成立窗口对象的历程如下:
procedure TWinControl.CreateHandle;
begin
CreateWnd;
SetWindowPos; // API 调解岗位
SetProp(FHandle, MakeIntAtom(ControlAtomState of Qatar,THandle(SelfState of Qatar卡塔尔(قطر‎; //
全局记录Handle
end;

每行都写二个Button是不是令你觉获得讨厌。Object
帕斯Carl提供它特有的with…do语句,能够帮您拨冗那一个烦懑。下边包车型客车代码可以写为:

procedure TWinControl.CreateWnd;
begin
CreateParams(Params卡塔尔(قطر‎; //
筹划参数,包蕴Owner的handle,当做当前控件的Parent
Windows.RegisterClass(WindowClassState of Qatar; // 注册窗口类
CreateWindowHandle(Params卡塔尔; // 依照Params申请窗口对象
end;

var

procedure TWinControl.CreateWindowHandle(const Params: TCreateParams);
begin
//
依照早前寻思的Params参数使用API创立窗口。其13个参数都以Params的参数,0表示Menu,WindowClass的十项内容只用到了hInstance一项
// important
注意,这时候才真的设置父窗口。它的父窗口是WndParent,也等于Owner的窗口句柄
// important
控件移到准确的来得地点,正是靠这些X和Y,会移到父控件区域的相对地方(奉行检查实验)。而VC里通常接纳CW_USEDEFAULT
with Params do
FHandle := CreateWindowEx(ExStyle, WinClassName, Caption, Style, X, Y,
Width, Height, WndParent, 0, WindowClass.hInstance, Param);
end;

Button: TButton;


begin

标题2:可利用IsChild实行探测。无论Windows依旧Delphi,本人都不是投机的父窗口

Button := TButton.Create(Self,’,’,’);

procedure TForm1.Button1Click(Sender: TObject);
begin
if IsChild(handle, handleState of Qatar then ShowMessage(‘yes’卡塔尔(قطر‎ else
ShowMessage(‘no’卡塔尔国; // 展现No, 因为Form本身不是上下一心的Child

with Button do {编写翻译器会精晓上面包车型大巴七个属性归于Button}

if IsChild(handle, button1.handle) then ShowMessage(‘yes’) // 显示Yes
else ShowMessage(‘no’);

begin

if IsChild(handle, panel1.handle) then ShowMessage(‘yes’) // 显示Yes
else ShowMessage(‘no’);

Parent := Self;

if IsChild(handle, button2.handle) then ShowMessage(‘yes’) //
展现Yes,通超过实际地衡量开掘,凡是具备子孙关系的控件(不唯有是老爹和儿子关系State of Qatar,IsChild都创建

else ShowMessage(‘no’);
end;

Left := 50;


Top := 50;

标题3:况兼Owner不是必需存在,比方:

……

procedure TForm1.Button1Click(Sender: TObject);
var
Button2: TButton;
begin
Button2 := TButton.Create(Nil); //
这个Nil就是指Owner!

Button2.Left := 100;
Button2.Top := 100;
Button2.Parent := Panel1;

end;

// ShowMessage(button2.Owner.Name卡塔尔; // 运转错误!
ShowMessage(button2.Parent.Name);
end;

end;

最佳玩的是,

with…do中得以分包五个记录也许指标,用逗号隔断。with…do也可以嵌套使用。


主题材料4:Owner的安装比较容易,但Parent到底是哪些设置的呢?

第一就是TWinControl.CreateWnd里安装:
Params.WndParent := TWinControl(Owner).Handle; //
即把Owner的Handle作为父窗口。然则button2的Owner是Form1,所以它的Parent也是Form1

接下来调用:
procedure TWinControl.CreateWindowHandle(const Params: TCreateParams);
begin
//
根据早前思忖的Params参数使用API创制窗口。其十一个参数都以Params的参数,0意味着Menu,WindowClass的十项内容只用到了hInstance一项
// important
注意,这时候才真正设置父窗口。它的父窗口是WndParent,也正是Owner的窗口句柄
// important
控件移到科学的显得地点,就是靠那个X和Y,会移到父控件区域的对立地点(执行检查评定)。而VC里平日选用CW_USEDEFAULT
with Params do
FHandle := CreateWindowEx(ExStyle, WinClassName, Caption, Style, X, Y,
Width, Height, WndParent, 0, WindowClass.hInstance, Param);
end;


标题5:遇到Owner为空的时候,哪天手艺正确安装Parent呢?

回答:没有Owner的时候,即便内部存款和储蓄器对象已经被创造,然则它自个儿的windows句柄并不曾被创制,那时它也无需父控件的句柄(即使有,也不会动用)。它的windows窗口创制要延缓到实施SetParent的时候。当钦定了Parent,那么设置它的父控件句柄也是意料之内的事务。

没有要求父控件的句柄是指(在钦定父控件从前,因为不会试行到CreateWnd函数,即便有Owner也是如此):
procedure TWinControl.CreateWnd;
begin
WndParent := TWinControl(Owner).Handle;
end;

测量试验代码:
procedure TForm1.Button1Click(Sender: TObject);
var
Button2: TButton;
begin
Button2 := TButton.Create(Nil);
Button2.Left := 100;
Button2.Top := 100;
Button2.Parent := Panel1; //
这里调用了SetParent函数,那么它的Delphi属性FParent就有值了,然后就足感到API准备参数了

ShowMessage(button2.Parent.Name);
end;

要害流程如下:
1.
安装Delphi控件的父亲和儿子关系:TControl.SetParent->TWinControl.InsertControl->TWinControl.Insert->TWinControl.UpdateControlState->TWinControl.UpdateShowing

  1. 真正成立控件
    CreateHandle->CreateWnd->CreateParams(Params卡塔尔->CreateWindowHandle(Params卡塔尔国
  2. 最后还要试行TWinControl.CMShowingChanged 举办展示

里面在实施Insert函数的时候,设置了Delphi的FParent属性
procedure TWinControl.Insert(AControl: TControl);
begin
AControl.FParent := Self;
end;

中间在举办CreateParams函数的时候,通过Delphi的FParent属性,得到了父控件的句柄
procedure TWinControl.CreateParams(var Params: TCreateParams);
begin
if Parent <> nil then
Params.WndParent := Parent.GetHandle //
这里得到父控件的句柄,任何时候筹算让API使用!

else
Params.WndParent := FParentWindow; // 那句几时会被执行?
end;


标题6:控件展现之后,到底哪些时候被移到了合情合理的坐标的吧?

回答:这些标题就不得法,其实是创办窗口的时候就放到了不易的x,y地点,今后调治展现的时候,调用SetWindowPos函数,其风格都以SWP_NOSIZE

  • SWP_NOMOVE + SWP_NOZORDER +
    SWP_NOACTIVATE,即大小不改变,地点不改变,Z轴不改变,不激活窗口,只变动展现可能不显示

procedure TWinControl.CreateWindowHandle(const Params: TCreateParams);
begin
//
控件移到准确的体现地方,就是靠这些X和Y,会移到父控件区域的相对地方(实施检查实验)。而VC里日常选取CW_USEDEFAULT
with Params do
FHandle := CreateWindowEx(ExStyle, WinClassName, Caption, Style, X, Y,
Width, Height, WndParent, 0, WindowClass.hInstance, Param);
end;

亟需突显或蒙蔽的时候,调用:
procedure TWinControl.CMShowingChanged(var Message: TMessage);
const
ShowFlags: array[Boolean] of Word = (
SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE +
SWP_HIDEWINDOW, // 三个掩盖,其它都无差异
SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE +
SWP_SHOWWINDOWState of Qatar; // 一个来得,SWP_NOMOVE表示地点不改变
begin
//
五级入口:调用API真正显示Windows窗口。屏蔽那句,一切Form的子控件都不出示
SetWindowPos(FHandle, 0, 0, 0, 0, 0, ShowFlags[FShowing]); //
API,退换窗口的尺寸,地点和Z序。注意倒数参数,是或不是激活就看它的机能
end;


难点7:
既然Owner能够是nil,那么自然也能够是TGraphicControl,何况随着这些图形控件的放出,其子控件(哪怕是Win子控件State of Qatar也会被放飞!!

procedure TForm1.Button1Click(Sender: TObject);
var
Button2: TButton;
begin

Button2 := TButton.Create(Image1);
Button2.Name := ‘Button2’;
Button2.Left := 100;
Button2.Top := 100;

Button2.Parent := Panel1;
ShowMessage(button2.Owner.Name);
ShowMessage(button2.Parent.Name);

image1.Free;
end;


参考:

发表评论

电子邮件地址不会被公开。 必填项已用*标注