关于DELPHI的布局
以下文章载自:【巴蛮子的破新茅屋】http://blog.csdn.net/bamanzi/archive/2006/02/11/596962.aspx
Windows编程的习惯是直接指定控件的座标和大小,而UNIX下面的习惯是在容器里面堆放,用不同的layout manager来控制布局(java,wx等也是这种思想)。这也是为什么Windows程序的对话框一般都不让改变大小(因为这意味着要自己写很多代码逐步调整子控件的位置),而UNIX下一般都可以。Delphi程序原来有Align属性来控制控件对那边靠拢。但如果想要一个控件固定离左边、右边有多远,得用D6开始提供Anchors属性。
所以DELPHI提供3种布局属性:Align, Anchor,和 Constraint。基本能满足布局设计的需要。
这样还是不够的,比如有七八个按钮顺排或者表格式布局,这在D2006里面可以用新增的TFlowPanel和TGridPanel来实现。
TFlowPanel和TGridPanel的使用方法:
http://edn.embarcadero.com/article/33421
呵呵,没找到中文说明的,只能看看E文了。里面的DEMO无法DOWN。
其中有一个事情要注意:任何一个控件被放到这两个布局上后,无法调整位置,DELPHI提供了一种方法,就是选择要调整的控件,在属性窗体的最下面会出现和布局有关的属性,进行调整就可以了。TFlowPanel有ControlIndex。TGridPanel有column,row,columnspan,rowspan。
评价
DELPHI提供的GridPanel貌似很好,但是使用起来实在不方便。我自己用GridPanel准备画一个人口信息的输入界面,结果画了一会就觉得烦了,根本没办法用。考虑再三,还是自己写一个。
至少提供如下功能:
1.能够在设计状态用鼠标调整行的行的宽度和列的高度。
2.panel的宽度和高度应该随着里面行和列的高度和宽度的变化而变化。而不应该是像GridPanel,宽度和高度不边,而让行和列的值来适应GridPanel。
3.能方便的增加和删除行和列。
4.放入pane中的控件的宽度要随着所在格子的宽度变化而变化。
开发思路:
1.从TCustomGridPanel继承。
2.在设计状态当鼠标移动到网格线上的时候,Cursor变成可crHSplit和crVSplit,(可以通过继承WMSetCursor来实现)
3.设计状态,当在网按住鼠标并移动的时候,修改对应的格子的大小(可以通过继承WMLButtonDown,WMLButtonUp,和WMMouseMove来实现)
4.当Panel上放入一个控件的时候,自动设置到对应格子的宽度(可以通过继承AlignControls来实现)。
5.增加RowCount和ColumnCount属性,可以通过设置这两个属性来设置行和列的总数。
效果:
可惜,这个GIF无法显示鼠标移动到网格中的时候改变鼠标形状的情况。
DELPHI问题
在写这个panel的时候发现一个问题,可能是DELPHI的BUG
在
ExtCtrls单元第3500行,procedure TCustomGridPanel.AlignControls(AControl: TControl; var Rect: TRect);方法中,代码如下:
AnchorSubset := AControl.Anchors * [akLeft, akRight];
if AnchorSubset = [akLeft] then
NewBounds.Left := CellRect.Left
else if AnchorSubset = [akRight] then
NewBounds.Left := Max(CellRect.Left, CellRect.Right - AControl.Margins.ExplicitWidth)
else
NewBounds.Left := Max(CellRect.Left, CellRect.Left + ((CellRect.Right - CellRect.Left) - AControl.Margins.ControlWidth) div 2);
这个应该是一个明显的BUG,如果Control的Anchors同时设置了akLeft和akRight,则系统就不再处理,也就是前面提到的如果用GridPanel,放到PANEL上的控件无法根据格子的宽度自动调整宽度。
修复方法:增加一个判断就可以:
AnchorSubset := AControl.Anchors * [akLeft, akRight];
//hl add 20090415原来控件没有提供对 Anchors = akLeft, akRight的处理
if AnchorSubset = [akLeft, akRight] then
begin
NewBounds.Left := CellRect.Left;
NewBounds.Right := CellRect.Right;
end
else begin
if AnchorSubset = [akLeft] then
NewBounds.Left := CellRect.Left
else if AnchorSubset = [akRight] then
NewBounds.Left := Max(CellRect.Left, CellRect.Right - AControl.Margins.ExplicitWidth)
else
NewBounds.Left := Max(CellRect.Left, CellRect.Left + ((CellRect.Right - CellRect.Left) - AControl.Margins.ControlWidth) div 2);
NewBounds.Right := NewBounds.Left + Min(CellRect.Right - CellRect.Left, AControl.Margins.ExplicitWidth);
end;