0

リスト ビューに似ていますが、軽量なカスタム リスト コントロールを作成しています。プロパティItemWidthItemHeightそのアイテムごとにあり、アイテムはTOwnedCollection. どの商品も同じサイズです。また、各項目をどのくらい離して配置するかを指定するMarginsおよびのプロパティもあります。ItemSpacing

問題は、各項目の位置を計算して、現在の制御空間に最適に収まるようにするときです。コントロールには垂直スクロールのみがあり、水平スクロールはありません。したがって、アイテムがリストに収まらない場合を認識して、次の行に移動する必要があります。

これをさらに難しくするには、マウス イベントを処理するために、特定のポイントが項目の四角形領域内にあるかどうかを識別できる必要もあります。これを解決するために、各項目に、その項目の領域をコントロールにGetRect返す関数を配置することにしました。Rectしかし、この関数にこれを計算させるにはどうすればよいでしょうか?

この関数の 2 つの主な実装は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;
4

2 に答える 2

4

グリッドの任意のセルの位置を知るために、以前のすべてのセルの位置を計算する必要はありません。それがグリッドの素晴らしいところです。各セルには予測可能な位置があります。

まず、1 行に水平方向に配置できるセルの数を知る必要があります。最初の answerの値を使用すると、次の式で与えられます。

CellsPerRow := (CW - ML - MR + SH) div (IW + SH);

これは、クライアントの合計幅を取り、余白を差し引き、アイテム幅とアイテム間の間隔を加算することによって得られる単一セルの有効幅で割ります。各行の 1 つのセルには間隔がありません (コントロールの端に接しているため)。そのため、クライアント領域が実際にはSHピクセル単位で広いと見なします。

行に収まるアイテムの数がわかったので、アイテムが属する (0 から始まる) 行を計算できます。

ItemRow := Item.Index div CellsPerRow;

その行 (列) 内の (0 から始まる) 位置も簡単に計算できます。

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負になると物事もおかしくなるでしょう。コントロールに特定の最小幅を適用する必要があります。

于 2012-06-04T01:12:00.433 に答える
-1

このようなポジションを計算する方法を示すために、この手順を分解しました。

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;

以下は、生成されたもののサンプルです。

ここに画像の説明を入力

より優れたパフォーマンスの代替手段があるはずです。このプロシージャは計算のループを実行しているため、数百のアイテムのリストでは結果が遅くなる場合があります。

于 2012-06-04T00:30:42.463 に答える