public override void Layout(ViewLayoutContext context)
{
Debug.Assert(context != null);
// Validate incoming reference
if (context == null) throw new ArgumentNullException("context");
// We take on all the available display area
ClientRectangle = context.DisplayRectangle;
// Ensure that the correct number of children are created
SyncChildren();
// Is there anything to layout?
if (Count > 0)
{
// Reduce the client area by the requested padding before the internal children
Rectangle displayRect = CommonHelper.ApplyPadding(Orientation.Horizontal, ClientRectangle, _gallery.Padding);
// Get size of the first child, assume all others are same size
_itemSize = this[0].GetPreferredSize(context);
// Number of items that can be placed on a single line
_lineItems = Math.Max(1, displayRect.Width / _itemSize.Width);
// Number of lines needed to show all the items
_layoutLines = Math.Max(1, (Count + _lineItems - 1) / _lineItems);
// Number of display lines that can be shown at a time
_displayLines = Math.Max(1, Math.Min(_layoutLines, displayRect.Height / _itemSize.Height));
// Index of last line that can be the top line
_endLine = _layoutLines - _displayLines;
// Update topline and offset to reflect any outstanding bring into view request
ProcessBringIntoView();
// Limit check the top line is within the valid range
_topLine = Math.Max(0, Math.Min(_topLine, _endLine));
// Update the enabled state of the buttons
_buttonUp.Enabled = _gallery.Enabled && CanPrevLine;
_buttonDown.Enabled = _gallery.Enabled && CanNextLine;
_buttonContext.Enabled = _gallery.Enabled && (Count > 0);
// Calculate position of first item as the left edge but starting downwards
// and equal amount of the spare space after drawing the display lines.
Point nextPoint = displayRect.Location;
nextPoint.Y += (displayRect.Height - (_displayLines * _itemSize.Height)) / 2;
// Stating item is from the top line and last item is number of display items onwards
int start = (_topLine * _lineItems);
int end = start + (_displayLines * _lineItems);
// Do we need to handle scroll offsetting?
int offset = _offset;
if (offset != 0)
{
if (offset < 0)
{
// How many extra full lines needed by the scrolling
int extraLines = _topLine - _beginLine;
// Limit check the number of previous lines to show
if (_topLine - extraLines < 0)
extraLines = _topLine;
// Move start to ensure that the previous lines are visible
start -= (extraLines * _lineItems);
// Adjust offset to reflect change in start
offset += (extraLines * _itemSize.Height);
}
else
{
// How many extra full lines needed by the scrolling
int extraLines = _beginLine - _topLine;
// Move start to ensure that the previous lines are visible
end += (extraLines * _lineItems);
// Limit check the end item to stop it overflowing number of items
if (end > Count)
end = Count;
}
}
// Add scrolling offset
nextPoint.Y -= offset;
// Position all children on single line from left to right
for (int i = 0; i < Count; i++)
{
ViewBase childItem = this[i];
// Should this item be visible
if ((i < start) || (i >= end))
childItem.Visible = false;
else
{
childItem.Visible = true;
// Find rectangle for the child
context.DisplayRectangle = new Rectangle(nextPoint, _itemSize);
// Layout the child
childItem.Layout(context);
// Move across to next position
nextPoint.X += _itemSize.Width;
// If there is not enough room for another item on this line
if ((nextPoint.X + _itemSize.Width) > displayRect.Right)
{
// Move down to next line
nextPoint.X = displayRect.X;
nextPoint.Y += _itemSize.Height;
}
}
}
}
else
{
// No children means no items and so need for enabled buttons
_buttonUp.Enabled = false;
_buttonDown.Enabled = false;
_buttonContext.Enabled = false;
}
// Put back the original display value now we have finished
context.DisplayRectangle = ClientRectangle;
}