【问题标题】:How to identify rect of list item in an auto-arranged list?如何识别自动排列列表中列表项的矩形?
【发布时间】:2012-06-04 00:30:41
【问题描述】:

我正在构建一个自定义列表控件,类似于列表视图但更轻。它的每个项目都有ItemWidthItemHeight 属性,这些项目位于TOwnedCollection 中。每个项目的大小相同。我还有MarginsItemSpacing 的属性来指定每个项目的位置相距多远。

问题在于计算每个项目的位置以使其最适合当前控制空间。该控件只有垂直滚动,没有水平滚动。因此,我需要识别出某个项目何时无法放入列表并将其带到下一行。

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

这个函数的两个主要实现会在控件的Paint中:

for X := 0 to FItems.Count - 1 do begin
  Canvas.Rectangle(FItems[X].GetRect);
end;

当确定一个点是否在这个项目的区域内时:

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;

【问题讨论】:

  • 如果您停止将其视为列表视图控件并开始将其视为网格控件,它可能会帮助您理解事物。

标签: delphi custom-controls listitem


【解决方案1】:

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

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

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 变为负数,事情也可能看起来很奇怪。您需要为控件强制执行某个最小宽度。

【讨论】:

    【解决方案2】:

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

    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;
    

    这是它产生的一个示例:

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

    【讨论】:

    • 你把SVSH 搞混了,不是吗?在项目的水平位置添加垂直间距是没有意义的。
    • @RobKennedy 取决于您如何看待它 - 我在想象一条垂直线,将每个项目彼此隔开。
    • 一条穿过水平间距的垂直线。间距影响项目的水平位置。它可能是垂直线所在的间距,但这不会以任何方式影响垂直空间。
    猜你喜欢
    • 2014-06-11
    • 2011-12-16
    • 2019-06-01
    • 2013-07-23
    • 2011-11-16
    • 1970-01-01
    • 1970-01-01
    • 2021-06-05
    • 2016-02-02
    相关资源
    最近更新 更多