首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何识别自动排列列表中列表项的rect?

如何识别自动排列列表中列表项的rect?
EN

Stack Overflow用户
提问于 2012-06-04 08:30:42
回答 2查看 332关注 0票数 0

我正在构建一个自定义列表控件,类似于列表视图,但更轻便。它的每个项目都有属性ItemWidthItemHeight,并且这些项目都在TOwnedCollection中。每件物品的大小都是一样的。我还有MarginsItemSpacing的属性,用于指定放置每个项目的间隔距离。

问题是,当涉及到计算每个项目的位置以使其最适合当前控件空间时。该控件只有垂直滚动,没有水平滚动。因此,当一个项目不能放在列表中时,我需要识别它,并将其带到下一行。

为了使这变得更复杂,我还必须能够识别给定的点是否在项目的矩形区域内,以便处理鼠标事件。因此,为了解决这个问题,我决定在每个项目GetRect上放置一个函数,该函数将在控件上返回该项目的Rect区域。但是我该如何让这个函数计算这个值呢?

此函数的两个主要实现将位于控件的Paint中:

代码语言:javascript
复制
for X := 0 to FItems.Count - 1 do begin
  Canvas.Rectangle(FItems[X].GetRect);
end;

当识别一个点是否在该项目的区域中时:

代码语言:javascript
复制
for X := 0 to FItems.Count - 1 do begin
  R:= FItems[X].GetRect;
  Result := (P.X > R.Left) and (P.X < R.Right) and (P.Y > R.Top) and (P.Y < R.Bottom);
end;
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-06-04 09:12:00

知道网格中任何单元格的位置并不需要计算之前所有单元格的位置。这就是网格的伟大之处。每个单元格都有一个可预测的位置。

首先,您需要知道单行中可以水平排列多少个单元格。使用the first answer中的值,该值由以下等式给出:

代码语言:javascript
复制
CellsPerRow := (CW - ML - MR + SH) div (IW + SH);

它取客户端的总宽度,减去边距,再除以单个单元格的有效宽度,这是通过将项目宽度与项目间间距相加得出的。每行的一个单元格没有间距(因为它靠近控件的边缘),所以我们假装工作区实际上比SH像素宽。

现在我们知道一行中有多少个项目,我们可以计算任何项目属于哪个(从零开始的)行:

代码语言:javascript
复制
ItemRow := Item.Index div CellsPerRow;

行(列)中(从零开始)位置也很容易计算:

代码语言:javascript
复制
ItemColumn := Item.Index mod CellsPerRow;

现在我们可以计算单元格的位置了:

代码语言:javascript
复制
LP := ML + ItemColumn * (IW + SH);
TP := MT + ItemRow * (IH + SV);

综合起来,我们得到了这样的结果:

代码语言:javascript
复制
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变为负值,事情可能也会看起来很奇怪。你需要为你的控件设置一个特定的最小宽度。

票数 4
EN

Stack Overflow用户

发布于 2012-06-04 08:30:42

我已经分解了这个过程来演示如何计算这样的位置:

代码语言:javascript
复制
function TMyListItem.GetRect: TRect;
var
  I: Integer;   //Iterator
  LP: Integer;  //Left position
  TP: Integer;  //Top position
  CW: Integer;  //Client width
  CH: Integer;  //Client height
  IW: Integer;  //Item width
  IH: Integer;  //Item height
  SV: Integer;  //Vertical spacing
  SH: Integer;  //Horizontal spacing
  ML: Integer;  //Margin left
  MT: Integer;  //Margin top
  MR: Integer;  //Margin right
  MB: Integer;  //Margin bottom
  R: TRect;     //Temp rect
begin //'Owner' = function which returns the control
  //Initialize some temporary variables...
  CW:= Owner.ClientWidth;
  CH:= Owner.ClientHeight;
  IW:= Owner.ItemWidth;
  IH:= Owner.ItemHeight;
  SV:= Owner.SpacingVert;
  SH:= Owner.SpacingHorz;
  ML:= Owner.Margins.Left;
  MT:= Owner.Margins.Top;
  MR:= Owner.Margins.Right;
  MB:= Owner.Margins.Bottom;
  LP:= ML;  //Default left position to left margin
  TP:= MT;  //Default top position to top margin
  for I := 0 to Collection.Count - 1 do begin
    R:= Rect(LP, TP, LP + IW, TP + IH);
    if Self.Index = I then begin
      Result:= R;
      Break;
    end else begin
      //Calculate next position
      LP:= LP + IW + SV;    //move left position by item width + vertical spacing
      if (LP + IW + MR) >= CW then begin //Does item fit?
        LP:= ML;            //reset left position
        TP:= TP + IH + SH;  //drop down top position to next line
      end;
    end;
  end;
end;

以下是它产生的结果的示例:

应该有一个性能更好的替代方案。此过程正在进行循环计算,因此包含数百个项目的列表可能会显示较慢的结果。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10875116

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档