Class Repeater
Builds one copy of an item template per item in ItemsSource, and lets the player pick one.
public class Repeater : InteractiveControl, IUpdateable, IEquatable<IUpdateable>, IDrawable, IIdentifiable<Guid>, IEquatable<IDrawable>, IDisposable, IFocusScope
- Inheritance
-
Repeater
- Implements
- Inherited Members
Remarks
An InteractiveControl that *owns* a StackLayout rather than being one, because a control can only have one base and rows need both: input (to select a row) and real layout (to arrange it). Composing gets the arrange/measure behaviour from the same code every other stack uses, without a parallel implementation to keep in step.
Keeping the rows out of the view's own control tree is deliberate, not a side effect.
Generated rows share the template's control ids, so a tree walk for
GetControl("name") would find an arbitrary one of N. That used to cost the rows
their input as well, since a view collected its focusable controls once, at initialise,
and rows arrive after that - so only row selection and activation worked, and a button
inside a template was inert. IFocusScope is the way back in: the rows stay
out of the tree that resolves ids and view-level bindings, and the view asks this control
separately for the controls inside them.
Overflow is handled by scrolling whole rows in and out of view, not by clipping: nothing below IRenderEncoderScope exposes a scissor rect, so a partially drawn row would spill outside the list. Same trade-off, and the same reasoning, as the ComboBox dropdown.
Constructors
Repeater(ILogger, View, string)
Creates an empty list in view.
public Repeater(ILogger logger, View view, string id = null)
Parameters
Properties
ActivateCommand
Runs when a row is double-clicked, or Enter is pressed with a row selected.
public ICommand ActivateCommand { get; set; }
Property Value
CanFocus
Whether the list takes focus: true when it has an ActivateCommand or a binding to SelectedItem. A list that only shows its items is passed over by Tab.
public override bool CanFocus { get; }
Property Value
CanScroll
True while there are more rows than currently fit.
public bool CanScroll { get; }
Property Value
FocusColor
Outline around the selected row, or the whole list when nothing is selected, while the list has focus. Without it a focused list looks exactly like an unfocused one, and a keyboard user cannot tell the arrow keys will move it.
public Color? FocusColor { get; set; }
Property Value
HoverColor
Fill drawn behind the row under the pointer.
public Color? HoverColor { get; set; }
Property Value
ItemSpacing
The gap between rows, in pixels.
public float ItemSpacing { get; set; }
Property Value
Items
The item of every row, in order, as a new list. Rows that are not shown are included.
public IReadOnlyList<object> Items { get; }
Property Value
ItemsSource
The collection to repeat over. Assigning a different collection rebuilds every row; re-assigning the same instance does nothing. Call Refresh() after mutating a collection in place, since there is no collection-change notification anywhere in this UI layer to hook.
public IEnumerable ItemsSource { get; set; }
Property Value
Remarks
The reference check is load-bearing, not an optimisation. A view refreshes every binding whenever the view model raises any property change, so selecting a row - which writes the selection back to the view model - re-assigned this property in the same breath. Rebuilding here threw away the rows, cleared the selection that had just been made, and published that empty selection straight back over the one the click set. The visible result was a list that could not be selected at all.
MaxExtent
Largest the list may grow along its orientation, in pixels. 0 lets it size to its contents.
public float MaxExtent { get; set; }
Property Value
Remarks
A declared Size on the same axis wins, and forces exactly that extent. The difference matters: a fixed height leaves dead space below the last row whenever there are fewer rows than fit, which is the usual case for a directory that happens to be quiet. A cap grows with the content and only starts scrolling once it is actually exceeded.
Orientation
Direction rows are laid out in.
public Orientation Orientation { get; set; }
Property Value
Padding
The space kept clear inside the list, around the rows.
public Spacing Padding { get; set; }
Property Value
RequiresRawKeyboard
True, so that the arrow keys, Home, End, PageUp, PageDown and Enter reach the list while it has focus.
public override bool RequiresRawKeyboard { get; }
Property Value
ScrollIndex
Index of the first row shown, for a list scrolled past its start.
public int ScrollIndex { get; }
Property Value
ScrollIndicatorColor
Thumb shown when there are more rows than fit.
public Color? ScrollIndicatorColor { get; set; }
Property Value
SelectedIndex
The index of the selected row, or -1 for none. An index outside the rows counts as none. Setting it scrolls the row into view and raises SelectionChanged.
public int SelectedIndex { get; set; }
Property Value
SelectedItem
The selected row's item, or null. Settable so a two-way "{Binding SelectedServer}" round-trips: the view model pushing a value in selects the matching row.
public object SelectedItem { get; set; }
Property Value
SelectionColor
Fill drawn behind the selected row.
public Color? SelectionColor { get; set; }
Property Value
SelectionMarkerColor
A marker down the selected row's leading edge.
public Color? SelectionMarkerColor { get; set; }
Property Value
SelectionMarkerThickness
The width of the marker down the selected row's leading edge, in pixels. 2 by default.
public int SelectionMarkerThickness { get; set; }
Property Value
Stretch
Take the size the parent layout gives this list instead of sizing to the rows: rows are laid out as wide as it, and scroll within its length.
public bool Stretch { get; set; }
Property Value
Remarks
Off by default because it only works where the parent does decide the size - the fill of a dock layout. A stack sizes a list from the list's own answer, so a stretched list in one would never grow past its first size.
SuppressesDefaultButton
True while a row is selected and there is an ActivateCommand, so that Enter activates the row and the view's default button does not also run.
public override bool SuppressesDefaultButton { get; }
Property Value
VisibleItemCount
How many rows currently fit, and so are laid out and drawn.
public int VisibleItemCount { get; }
Property Value
Methods
Dispose()
Disposes every row and the list's panel, then the list.
public override void Dispose()
Draw(IGameTime)
Draws the list's fill and border, then its visible rows, the selection, the hover highlight, the focus outline and the scroll indicator.
public override void Draw(IGameTime gameTime)
Parameters
gameTimeIGameTime
ItemAt(Vector2)
The item of the shown row at point, in UI space, or null.
public object ItemAt(Vector2 point)
Parameters
pointVector2
Returns
LoadContent()
Loads the content of each row.
public override void LoadContent()
Refresh()
Rebuilds every row from the current ItemsSource - for a collection whose contents changed without the collection itself being replaced.
public void Refresh()
Update(IGameTime)
Lays the rows out again when the list moves or changes width, then updates the rows.
public override void Update(IGameTime gameTime)
Parameters
gameTimeIGameTime
Events
RowBuilt
Raised for each row as it is built, with a way to reach the controls inside it.
public event EventHandler<RepeaterRowEventArgs> RowBuilt
Event Type
Remarks
For what a row cannot say as a binding -- a sprite-sheet cell, a generated texture -- since rows are kept out of the view's control tree and every row shares the template's ids. Fires on every rebuild, so a handler runs again for rows that are logically the same item; it must be safe to call twice and must not assume it is seeing a row for the first time.
SelectionChanged
Raised when SelectedIndex changes, including when the selection is cleared.
public event EventHandler SelectionChanged