I’m building a custom list control, similar to a list view but lighter. It has properties ItemWidth and ItemHeight for each of its items, and the items are in a TOwnedCollection. Each item is the same size. I also have properties for Margins and ItemSpacing to specify how far apart to position each item.
The problem is when it comes to calculating each item’s position to best fit them in the current control space. The control has only vertical scrolling, and no horizontal. Therefore, I need to recognize when an item cannot fit in the list and bring it to the next line.
To make this even trickier, I have to also be able to identify if a given point is within an item’s rect area, for handling mouse events. So to solve this, I decided to put a function on each item GetRect which will return that item’s Rect area on the control. But how do I make this function calculate this?
The two main implementations of this function will be in the Paint of the control:
for X := 0 to FItems.Count - 1 do begin
Canvas.Rectangle(FItems[X].GetRect);
end;
And when identifying if a point is in this item’s area:
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;
Knowing the position of any cell of a grid does not require calculating all previous cells’ locations. That’s the great thing about a grid. Each cell has a predictable location.
To start, you need to know how many cells can be arranged horizontally in a single row. Using the values from the first answer, that’s given by this equation:
That takes the total client width, subtracts the margins, and divides by the effective width of a single cell, given by adding the item width with the inter-item spacing. One cell of each row doesn’t have spacing (because it abuts an edge of the control), so we pretend that the client area is actually wider by
SHpixels.Now that we know how many items fit in a row, we can calculate which (zero-based) row any item belongs in:
The (zero-based) position within that row (the column) is also easy to calculate:
Now we can calculate the position of the cell:
Bringing it all together, we get this:
Be careful about letting the control get too narrow, or letting the margins get too wide. If that happens, then the
CellsPerRowproperty could become zero, and that will cause exceptions from all theGetRectcalls. Things will probably also look strange ifCellsPerRowbecomes negative. You’ll want to enforce a certain minimum width for your control.