知道网格中任何单元格的位置不需要计算所有先前单元格的位置。这就是网格的伟大之处。每个单元格都有一个可预测的位置。
首先,您需要知道在一行中可以水平排列多少个单元格。使用第一个答案中的值,由以下等式给出:
CellsPerRow := (CW - ML - MR + SH) div (IW + SH);
这需要总客户端宽度,减去边距,然后除以单个单元格的有效宽度,通过将项目宽度与项目间距相加得到。每行的一个单元格没有间距(因为它紧靠控件的边缘),所以我们假设客户区实际上比SH
像素更宽。
现在我们知道一行可以容纳多少项目,我们可以计算任何项目属于哪一行(从零开始):
ItemRow := Item.Index div CellsPerRow;
该行(列)中的(从零开始的)位置也很容易计算:
ItemColumn := Item.Index mod CellsPerRow;
现在我们可以计算单元格的位置:
LP := ML + ItemColumn * (IW + SH);
TP := MT + ItemRow * (IH + SV);
把它们放在一起,我们得到这个:
function TMyListItemGrid.GetCellsPerRow: Integer;
begin
Result := (ClientWidth - Margins.Left - Margins.Right + SpacingHorz) div (ItemWidth + SpacingHorz);
end;
function TMyListItem.GetRect: TRect;
var
Row, Col: Integer;
EffectiveWidth, EffectiveHeight: Integer;
begin
EffectiveWidth := Owner.ItemWidth + Owner.SpacingHorz;
EffectiveHeight := Owner.ItemHeight + Owner.SpacingVert;
Row := Index div Owner.CellsPerRow;
Result.Top := Owner.Margins.Top + Row * EffectiveHeight;
Result.Bottom := Result.Top + Owner.ItemHeight;
Col := Index mod Owner.CellsPerRow;
Result.Left := Owner.Margins.Left + Col * EffectiveWidth;
Result.Right := Result.Left + Owner.ItemWidth;
end;
小心不要让控件变得太窄,或者让边距变得太宽。如果发生这种情况,则该CellsPerRow
属性可能会变为零,这将导致所有GetRect
调用出现异常。CellsPerRow
如果变成负数,事情也可能看起来很奇怪。您需要为控件强制执行某个最小宽度。