Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 如何在自动排列的列表中识别列表项的rect?_Delphi_Custom Controls_Listitem - Fatal编程技术网

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

Delphi 如何在自动排列的列表中识别列表项的rect?,delphi,custom-controls,listitem,Delphi,Custom Controls,Listitem,我正在构建一个自定义列表控件,类似于列表视图,但更轻。它的每个项目都有属性ItemWidth和ItemHeight,这些项目位于TOwnedCollection中。每个项目都是相同的大小。我还有边距和项目间距的属性来指定每个项目的位置 问题在于,当计算每个项目的位置以使其在当前控制空间中最适合时。控件只有垂直滚动,没有水平滚动。因此,我需要识别某个项目何时无法放入列表,并将其带到下一行 为了使这一点更加棘手,我还必须能够识别给定的点是否在项目的矩形区域内,以便处理鼠标事件。为了解决这个问题,我决

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

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

为了使这一点更加棘手,我还必须能够识别给定的点是否在项目的矩形区域内,以便处理鼠标事件。为了解决这个问题,我决定在每个项
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;
对于X:=0到FItems。计数-1开始
R:=FItems[X].GetRect;
结果:=(P.X>R.Left)和(P.XR.Top)和(P.Y
我将此过程分解,以演示如何计算这些位置:

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;
以下是它生产的产品的示例:


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

我已将此过程分解,以演示如何计算这些位置:

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;
以下是它生产的产品的示例:


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

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

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

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

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

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

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

如果您不再将其视为列表视图控件而开始将其视为网格控件,则它可能有助于您理解内容。如果您不再将其视为列表视图控件而开始将其视为网格控件,则它可能有助于您理解内容控制。你把
SV
SH
搞混了,不是吗?将垂直间距添加到项目的水平位置是没有意义的。@这取决于您如何看待它-我想象的是一条垂直线,将每个项目彼此隔开。一条垂直线穿过水平间距。间距会影响项目的水平位置。它可能是垂直线的间距,但这不会以任何方式影响垂直空间。你把
SV
SH
混在一起了,不是吗?将垂直间距添加到项目的水平位置是没有意义的。@这取决于您如何看待它-我想象的是一条垂直线,将每个项目彼此隔开。一条垂直线穿过水平间距。间距会影响项目的水平位置。它可能是垂直线的间距,但这不会以任何方式影响垂直空间。