gum-forms-itemscontrol
Reference guide for ItemsControl and ListBox — the Items/ListBoxItems relationship, templates, InnerPanel sync, and gotchas. Load this when working on ItemsControl, ListBox, ListBoxItem, VisualTemplate, FrameworkElementTemplate, Items collection behavior, ListBoxItems desync, or adding/removing items from a list box.
Best use case
gum-forms-itemscontrol is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Reference guide for ItemsControl and ListBox — the Items/ListBoxItems relationship, templates, InnerPanel sync, and gotchas. Load this when working on ItemsControl, ListBox, ListBoxItem, VisualTemplate, FrameworkElementTemplate, Items collection behavior, ListBoxItems desync, or adding/removing items from a list box.
Teams using gum-forms-itemscontrol should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/gum-forms-itemscontrol/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How gum-forms-itemscontrol Compares
| Feature / Agent | gum-forms-itemscontrol | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Reference guide for ItemsControl and ListBox — the Items/ListBoxItems relationship, templates, InnerPanel sync, and gotchas. Load this when working on ItemsControl, ListBox, ListBoxItem, VisualTemplate, FrameworkElementTemplate, Items collection behavior, ListBoxItems desync, or adding/removing items from a list box.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# ItemsControl and ListBox Reference ## Key Files | File | Purpose | |------|---------| | `MonoGameGum/Forms/Controls/ItemsControl.cs` | Base class: Items property, template resolution, InnerPanel sync | | `MonoGameGum/Forms/Controls/ListBox.cs` | Adds ListBoxItems tracking, selection, and ListBoxItem creation | | `MonoGameGum/Forms/Controls/ListBoxItem.cs` | Individual row control; holds IsSelected, IsHighlighted, events | | `MonoGameGum/Forms/VisualTemplate.cs` | Creates `GraphicalUiElement` instances (visual-first) | | `MonoGameGum/Forms/FrameworkElementTemplate.cs` | Creates `FrameworkElement` instances (forms-first) | ## The Two Collections `Items` and `ListBoxItems` are separate and can get out of sync. - **`Items`** (`IList`, default `ObservableCollection<object>`) — the logical data collection. Can hold anything: strings, view models, `ListBoxItem` instances, or any `FrameworkElement` / `GraphicalUiElement`. - **`ListBoxItems`** (`ReadOnlyCollection<ListBoxItem>`) — the visual row controls actually shown. Wraps `ListBoxItemsInternal` (a `List<ListBoxItem>`). In normal usage (adding data objects to `Items`) they stay in sync. They diverge in several cases — see **Desync Gotchas** below. ## Data Flow: Items → InnerPanel → ListBoxItems Adding to `Items` triggers a two-stage pipeline: 1. **`HandleItemsCollectionChanged`** — responds to `Items`. Creates or locates a visual and inserts it into `InnerPanel.Children`. 2. **`HandleInnerPanelCollectionChanged`** — responds to `InnerPanel.Children`. Calls `HandleCollectionNewItemCreated(frameworkElement, index)`. 3. **`HandleCollectionNewItemCreated`** (ListBox override) — if the item is a `ListBoxItem`, inserts it into `ListBoxItemsInternal` and calls `AssignListBoxEvents`. `HandleCollectionNewItemCreated` is NOT called directly from step 1. It is only triggered by InnerPanel firing its own `CollectionChanged`. This indirection is intentional. ## What Gets Created Per Item Type `HandleItemsCollectionChanged` dispatches based on what was added to `Items`: | Item type added to `Items` | What happens | |---------------------------|-------------| | `FrameworkElement` | Its `.Visual` is inserted into InnerPanel directly — no new wrapper created | | `GraphicalUiElement` | Inserted into InnerPanel directly — no wrapper | | Any other data object AND `VisualTemplate` is set | `VisualTemplate.CreateContent(item, createFormsInternally:false)` is called; result inserted | | Any other data object, no `VisualTemplate` | `CreateNewItemFrameworkElement(item)` is called | **ListBox overrides `CreateNewItemFrameworkElement`** with additional logic: | Item type | ListBox behavior | |-----------|----------------| | `ListBoxItem` | Used as-is — no template, no wrapping | | Anything else | Calls `CreateNewVisual(vm)` (uses `VisualTemplate` or `DefaultFormsTemplates[typeof(ListBoxItem)]`), then wraps result in a `ListBoxItem` via `CreateNewListBoxItem`. `BindingContext` and `UpdateToObject` are called on the result. | ## Templates There are two template types with different roles: **`VisualTemplate`** — produces a `GraphicalUiElement` (visual-first). - Constructed with a `Type` (must have `(bool, bool)` or no-arg constructor), `Func<GraphicalUiElement>`, `Func<object, GraphicalUiElement>`, or `Func<object, bool, GraphicalUiElement>`. - Used by `CreateNewVisual`. When constructed from a `Type`, calls it with `(true, false)` — `createFormsInternally:false` prevents the visual from creating its own Forms object, since the ListBox will wrap it. - Set on `ItemsControl.VisualTemplate`. Changing it clears and rebuilds all visuals. **`FrameworkElementTemplate`** — produces a `FrameworkElement` (forms-first). - Constructed with a `Type` or `Func<FrameworkElement>`. - Used by `CreateNewItemFrameworkElement`. For ListBox, the result must be a `ListBoxItem` subclass or an exception is thrown. - Set on `ItemsControl.FrameworkElementTemplate`. **Global fallback** — if neither template is set, `DefaultFormsTemplates[typeof(ListBoxItem)]` is used (set during app initialization). This is the normal path for default apps. **Setting a template clears and rebuilds all existing items** — both `VisualTemplate` and `FrameworkElementTemplate` setters call `ClearVisualsInternal()` and replay the Items collection. ## Desync Gotchas ### 1. Adding a non-ListBoxItem FrameworkElement to Items When a `Button`, `CheckBox`, or other `FrameworkElement` is added to `Items`, its Visual is inserted into InnerPanel. `HandleInnerPanelCollectionChanged` fires, but `asGue.FormsControlAsObject` is the `Button` (not a `ListBoxItem`), so `HandleCollectionNewItemCreated` is called with a `Button`. ListBox's override only inserts into `ListBoxItemsInternal` if the item `is ListBoxItem`, so the `Button` is silently skipped. **`Items.Count` increases but `ListBoxItems.Count` does not.** ### 2. Adding directly to InnerPanel.Children `HandleInnerPanelCollectionChanged` fires and can populate `ListBoxItemsInternal` if the child's `FormsControlAsObject` is a `ListBoxItem`. But **`Items` is never updated** — it stays at 0 (or whatever it was). This is the case when a ListBox's visual is constructed by the Gum tool with pre-filled children. ### 3. Gum tool pre-filled ListBox (ReactToVisualChanged recovery) If a ListBox Visual arrives with children already in InnerPanel and `Items.Count == 0`, `ReactToVisualChanged` in ListBox iterates `InnerPanel.Children`, adds `ListBoxItem` instances to both `Items` and `ListBoxItemsInternal`, and calls `AssignListBoxEvents`. This recovery only runs once at construction; it does not stay in sync afterward. ### 4. Index alignment assumption `HandleItemSelected` resolves the data object via `Items[ListBoxItemsInternal.IndexOf(listBoxItem)]`. If `Items` and `ListBoxItems` have drifted (any of the above cases), **selection silently fails or selects the wrong item** — the check `clickedIndex >= Items.Count` causes an early return. ### 5. AssignListBoxEvents idempotency `ListBoxItem.AssignListBoxEvents` is guarded by `hasHadListBoxEventsAssigned`. A `ListBoxItem` that bypasses normal item creation (e.g., added to InnerPanel directly without going through `Items`) may or may not have its events assigned. If events are missing, the item renders but clicking it produces no selection change. ## Selection `SelectedItems` is an `ObservableCollection<object>` (not replaceable, only modified). `SelectedObject` and `SelectedIndex` are convenience properties that read/write the first entry in `SelectedItems`. `SyncIsSelectedFromSelectedItems` walks `ListBoxItemsInternal` and reconciles `IsSelected` on each item. It runs whenever `SelectedItems` changes or `SelectedObject`/`SelectedIndex` are set. `SelectionMode` controls click behavior: `Single` (default), `Multiple` (each click toggles), `Extended` (Ctrl/Shift modifier keys). Gamepad/keyboard input always uses single-selection behavior regardless of mode. ## DisplayMemberPath When set, `DisplayMemberPath` causes `listBoxItem.UpdateToObject(property_value_as_string)` instead of `UpdateToObject(the_object_itself)`. This applies both on initial creation and when `DisplayMemberPath` is changed after items are already loaded.
Related Skills
gum-forms-default-visuals
Reference guide for Forms DefaultVisuals — the code-only visual classes that back Forms controls. Load when working on ButtonVisual, any *Visual class in DefaultVisuals/, Styling, DefaultFormsTemplates registration, or building custom code-only Forms visuals.
gum-forms-controls
Reference guide for Forms controls — classes inheriting from FrameworkElement. Load this when working on Button, CheckBox, ListBox, ComboBox, TextBox, ScrollViewer, or any class in Gum.Forms.Controls (or FlatRedBall.Forms.Controls). Also load when working on FrameworkElement itself, the Visual/InteractiveGue relationship, state machines, DefaultVisuals, or ReactToVisualChanged.
gum-forms-behaviors
Covers Gum's behaviors system and the design-time → runtime Forms wrapping lifecycle. Load this when working on BehaviorSave, ElementBehaviorReference, StandardFormsBehaviorNames, FormsUtilities.RegisterFromFileFormRuntimeDefaults, DefaultFromFileXxxRuntime classes, or when investigating why Forms properties cannot be set at design time in the Gum tool.
validate-code-changes
Validate all code changes on the current branch. Spawns QA and refactoring agents in parallel to review for correctness, edge cases, code quality, and pattern adherence. Use when ready to review branch changes before merging.
skills-writer
Creates and updates skill files (.claude/skills/*/SKILL.md) by reading source code and condensing knowledge into concise reference guides. Use when asked to create a new skill, update an existing skill, or document a subsystem for Claude Code agent context.
gum-variable-deep-dive
Deep dive into the full variable lifecycle — from VariableSave on ElementSave through runtime application on GraphicalUiElement and Forms controls. Load this when working on styling, theming, RefreshStyles, or when you need to understand how variable values flow from save data to live visuals.
gum-unit-tests
Reference guide for writing unit tests in the Gum repository. Load this when writing or modifying tests in Gum.ProjectServices.Tests, Gum.Cli.Tests, or any other Gum test project.
gum-tool-viewmodels
Reference guide for Gum tool ViewModel conventions. Load this when working on ViewModels, XAML views, data binding, DependsOn, or visibility properties in the Gum tool.
gum-tool-variable-references
Reference guide for Gum's variable reference system — Excel-like cross-instance/cross-element variable binding using Roslyn-parsed assignment syntax. Load this when working on VariableReferenceLogic, EvaluatedSyntax, ApplyVariableReferences, VariableChangedThroughReference, or the VariableReferences VariableListSave.
gum-tool-variable-grid
Reference guide for Gum's Variables tab and DataUiGrid system. Load this when working on the Variables tab, DataUiGrid control, MemberCategory, InstanceMember, category population, property grid refresh, or category expansion state persistence.
gum-tool-undo
Reference guide for Gum's undo/redo system. Load this when working on undo/redo behavior, the History tab, UndoManager, UndoPlugin, UndoSnapshot, or stale reference issues after undo.
gum-tool-selection
Reference guide for Gum's editor selection system. Load this when working on click/drag selection, the rectangle/marquee selector, input handlers (move, resize, rotate, polygon points), the IsActive flag, locked instance behavior, SelectionManager coordination, or the selection event cascade (plugin events, forced default state, tree view sync).