Class SelectingItemsControl
Adds single-item selection on top of ItemsControl - the shared base for Selector (adds a popup on top of this) and WrapGrid/ListBox (always-visible, no popup).
public abstract class SelectingItemsControl : ItemsControl, INotifyPropertyChanging, IBindingTarget, INotifyPropertyChanged, INotifyFocusChanged, IContainerLayout, IVirtualizingScrollInfo
- Inheritance
-
SelectingItemsControl
- Implements
- Derived
- Inherited Members
- Extension Methods
Remarks
Realizes SelectorItems (see CreateContainer(DataTemplate, object)) instead of bare ItemContainers, and keeps each realized item's IsSelected in sync with SelectedIndex - including across a pool-and-reuse cycle, since a selected item's container can be de-realized and rebuilt later by virtualization.
Properties
ContainerIndices
Gets the reverse lookup from a realized SelectorItem back to the item index it's currently
showing, so Container_Tapped(object, EventArgs) can turn a tap on the container into a
SelectedIndex assignment. Kept in sync by AttachContainer(ItemContainer, int)/
DetachContainer(ItemContainer). Exposed to derived controls so
Selector - whose own AttachContainer/DetachContainer override never calls
this base implementation - can reuse the same map for its own (differently-triggered) tap handling
instead of keeping a second, identically-shaped duplicate.
protected Dictionary<SelectorItem, int> ContainerIndices { get; }
Property Value
SelectedIndex
Gets or sets the currently selected item's index, or -1 for no selection.
[Range(-1, Infinity)]
[BindingOverride(BindingMode.TwoWay)]
public int SelectedIndex { get; set; }
Property Value
Remarks
An index past the current items doesn't throw: it's remembered, and selected as soon as the items cover it
(until then SelectedIndex stays -1). So markup can set SelectedIndex="1" next to an
ItemsSource binding that resolves only when the data context arrives. Setting
SelectedIndex or SelectedItem again replaces the remembered index.
Exceptions
- ArgumentOutOfRangeException
valueis less than-1.
SelectedItem
Gets or sets the currently selected item, or null for no selection. Setting an item not present in ItemsSource clears selection instead of throwing.
[BindingOverride(BindingMode.TwoWay)]
public object? SelectedItem { get; set; }
Property Value
Methods
AttachContainer(ItemContainer, int)
Stamps a freshly realized container's IsSelected from
SelectedIndex, on top of the base ItemsControl wiring - the case the
SelectedIndex setter's own stamp (see its own realizedContainers lookup) can't
reach: index selected before it's ever realized, or re-realized later after a
pool-and-reuse cycle. Selector overrides this again itself, to route into its popup
host instead of this base's default parenting.
protected override void AttachContainer(ItemContainer container, int index)
Parameters
containerItemContainerThe freshly realized container.
indexintThe item index
containerwas realized for.
Remarks
Also wires up click-to-select: subscribes Tapped (handled by
Container_Tapped(object, EventArgs)) and records index in ContainerIndices
so the handler can look it back up. This is inert for Selector and its subclasses
(Dropdown, ComboBox), since their own AttachContainer override
doesn't call this base implementation.
CreateContainer(DataTemplate, object)
Builds a fresh ItemContainer to host item's built visual tree - the
default body RentContainer(DataTemplate, object) falls back to whenever pooling can't supply
one. A Selector overrides this to realize SelectorItems instead.
protected override ItemContainer CreateContainer(DataTemplate template, object item)
Parameters
templateDataTemplateThe template to build
item's content with.itemobjectThe data item the new container is being realized for.
Returns
- ItemContainer
The freshly built container.
DetachContainer(ItemContainer)
Exact inverse of AttachContainer(ItemContainer, int)'s click-to-select wiring -
unsubscribes Tapped and forgets container's
Icy.UI.Controls.SelectingItemsControl.containerIndices entry before deferring to the base detach.
protected override void DetachContainer(ItemContainer container)
Parameters
containerItemContainerThe container being de-realized.
OnContainerTapped(int)
Called when a realized container is tapped. Selects index by default.
protected virtual void OnContainerTapped(int index)
Parameters
indexintThe tapped container's item index.
Remarks
A subclass whose items aren't all selectable (e.g. a tree whose category rows only expand) overrides this to decide what a tap does.
OnItemsChanged()
Called after items has been mutated by any means - a full ItemsSource reassignment
(Icy.UI.Controls.ItemsControl.ResetItems()), a live incremental collection-change notification, or a full
Reset re-snapshot. Empty by default. A subclass with state
that depends on item identity/count (e.g. SelectingItemsControl's
SelectedIndex) overrides this to keep it valid.
protected override void OnItemsChanged()
Remarks
Clears SelectedIndex (and SelectedItem with it) when the item it pointed to no longer exists because the live ItemsSource shrank out from under it, and selects an index set before the items covered it once they do.
OnSelectionChanged()
Called after SelectedIndex/SelectedItem have been updated, before the realized-container stamp and the public SelectionChanged event - empty by default. A subclass with derived state of its own that depends on the new selection (e.g. Selector's SelectedValue) overrides this to refresh it, rather than reacting to its own SelectionChanged subscription (which would depend on subscriber-ordering relative to any external subscriber this control's own consumer adds).
protected virtual void OnSelectionChanged()
Events
SelectionChanged
Occurs when SelectedIndex/SelectedItem changes.
public event EventHandler? SelectionChanged