众所周知,在我们日常写一些小软件的时候,我们总不可避免的需要适配有任何一位用户可能会调整窗口的大小来达到适合自己观看的角度。
那么我们的Delphi小程序,应该怎么样才能达到自适应角度的问题呢?众所又周知,Delphi贴心的给了我们一个控件的Anchor属性。这个属性可以达到适配用户与窗口之间的联系。大概功能就是在说:窗口控件将会随着窗口的大小改变而改变。
但是,细心的网友很快就会发现,这个功能完全不能适合我们广大的朋友需求。此时此刻,就必须得有大侠上阵,为我们解答疑惑了!!
工欲善其事,必先利其器,我们得先知道我们的不完善的功能是什么,才能随之对应的给出解决方案,正如同你将代码托管到某个开源网址中,而很少有人给你提出issue,你排除bug的份额也就会小了很多。是吧!
不完善的功能如下显示,请看图:
仔细看,这里是传统意义上的窗口,左边的Anchor属性里面只有akLeft和akTop,意思就是说:【目前这个memo框的位置应该是对齐我们放置控件时的左方和上方】。大家可以看看我们这幅图,我拉扯了原有窗体的大小,此时,窗体的右侧和下侧被我拉长,但是控件却始终保持在我们窗体的左上方,它的Top和Left值是永远不变的。
此时,如果我们修改一下Anchor属性,会发生什么事呢?请接着看下图
这里,我们将Memo框的Anchor属性改成了akRight和akBottom。意思是右侧和底部。此时细心的网友就会发现了,这个Memo框距离窗体右侧和下侧的位置与我们放置这个控件的位置是一样的诶!
此时,我们就会发现问题所在了。具体Anchor属性该怎么玩,这里就不多说了,大家可以自己去尝试一下,将别的改成True,总之,我这里只多说一句嘴,Anchor属性并不能很好的让我们开发者拥有更多的体验感。
那么,我现在需要实现的功能是什么呢?大家请看下图【下图是我已经实现好的,马上就会和大家讲到原理是什么了。】 请看这幅图,我已经将此功能写好了,我们会惊讶的发现,所有的控件成比例的放大缩小,所有控件与窗体边缘的距离也与窗体长宽成比例放大缩小。
这个时候,我们就会发现了神奇的问题了,这个功能在原版的Delphi中是没有的,必须要我们自己去实验一次,才会有这个功能。让我们来看看这个奇妙的功能原理是什么吧!
首先,创建窗体,在上面放上你自己喜欢的所有控件。然后双击窗体【记住,一定是双击窗体!我们要进入窗体的Create事件中添加一些原始量。】
此时,大家就会进入到代码编辑界面。然后,我们在implementation代码块的上方,有一个var,我们在var下面新建几个成员变量,变量名随意,当然我这里也挺随意的。
var
Form1: TForm1;
cch: Integer = 0; //初始的窗体高度
ccw: Integer = 0; //初始的窗体宽度
chlst: array of Integer; //初始的控件高度数组
cwlst: array of Integer; //初始的控件宽度数组
cplst: array of Integer; //初始的控件距离窗口上端的距离数组
cllst: array of Integer; //初始的控件距离窗口左端的距离数组
carr: array of TControl; //存放所有窗体控件数组
然后,大家会发现,我们的【存放所有窗体控件数组】类型居然是TControl的,这个类以前从来没有见过诶,怎么回事?别着急,晚点我就会和大家说到这个了。
然后,在我们的FormCreate中,写上一串方法,首先自然是初始化我们的窗体高度和宽度啦,大致如下:
procedure TForm1.FormCreate(Sender: TObject);
begin
cch := self.Height; //初始化窗口宽高
ccw := self.Width;
for var I := 0 to ControlCount - 1 do begin
SetLength(chlst, I + 1); //给初始控件的高度数组设置大小。
chlst[I] := Controls[I].Height; //给初始控件的高度数组设置值。以下均同
SetLength(cwlst, I + 1); //宽度
cwlst[I] := Controls[I].Width;
SetLength(cplst, I + 1); //距离窗口上端的距离
cplst[I] := Controls[I].Top;
SetLength(cllst, I + 1); //距离窗口左端的距离
cllst[I] := Controls[I].Left;
SetLength(carr, I + 1); //最后一步,给存放所有窗体控件数组添加元素
carr[I] := Controls[I];
end;
end;
首先,定义一个for循环,遍历窗体上的所有控件。然后,就是设置数组大小,设置值,以及最后给存放所有控件数组添加了一个元素。
请注意吼,这里的遍历窗口所有控件,我用的是ControlCount,这个是Form1自带的一个属性值,大家可以注意一下,然后Controls是一个来自TControl的一个值,可以直接键入Top、Left等。
总的来说,这一步都挺简单的。然后,让我们回到窗体布局界面。
我们可以发现,我们在窗体的Event事件里面,找到OnResize属性,然后,进入事件中。
这个事件的意思是:【当窗体的大小被改变时触发,也就是当窗体最大化、最小化、拖动窗体边缘的时候,会触发这个事件。】
我们需要使控件自适应窗体大小,因此这个事件是必不可少的。我们进入事件之后,敲出如下代码:
procedure TForm1.FormResize(Sender: TObject);
begin
var ch := self.Height; //定义两个变量,用于获取窗体大小改变后的高度和宽度值。
var cw := self.Width;
for var I := 0 to Length(carr) - 1 do begin
var avgh := round(chlst[I] * (ch / cch)); //获取平均改动值,用了【控件原高度x(窗体高度 / 窗体原高度)】公式。
var avgw := round(cwlst[I] * (cw / ccw));
var avgp := round(cplst[I] * (ch / cch));
var avgl := round(cllst[I] * (cw / ccw));
carr[I].Height := avgh; //应用平均改动值
carr[I].Width := avgw;
carr[I].Top := avgp;
carr[I].Left := avgl;
end;
end;
众所周知吼,所有的控件如果被别的数组所引用,那么数组改变了属性,控件也随之改变,因为控件永远都是按照引用传递值的,所以不必担心改了数组值而控件值没改的情况。但是局部变量则不行。用一个for循环的意思,是为了让窗体上所有的空间全部自适应。
看了我们刚刚的代码了吗?好了的,现在请各位看到我们最初始的问题:【TControl是什么意思。】其实啊,TControl这个的意思非常简单,众所又双叒叕周知,所有的控件都有一个属于自己的类,例如按钮则隶属于TButton类,文本框则隶属于TEdit类,标签框则隶属于TLabel类。那么这些类都有一个共同的特点,那就是:全部都继承自TControl类。
此时,我们就可以说,Delphi里绝大部分的控件类都继承自TControl类。不排除有些控件不在里面的。例如TTimer这样之类的没有实际控件显示的类。但当大家看到刚刚的Form1的Control就已经大概猜到了这个意思了吧。。
好的,此时此刻,大家就会发现,无论我们在窗体上放上多少能够被可视化的控件,现在均都可以实现自适应了。
感谢收看,下次再见咯咯!!