Alright — this is one of the most important topics in real WPF systems.
If you understand ItemsControl + Virtualization, you understand:
- why some apps feel instant
- why others freeze with 10k items
- how WPF actually builds UI from data
Let’s go step by step, from intuition → internals → production.
1. Big Picture
WPF is built around this idea:
“UI is just a projection of data.”
When you bind a collection:
ObservableCollection<InspectionResult>WPF does not render data directly.
Instead, it:
- Creates a UI container per item
- Applies a template
- Adds it to the visual tree
👉 So:
Data → Container → Template → Visual Tree⚠️ Problem:
If you have 10,000 items, naive rendering means:
- 10,000 UI elements
- 10,000 layouts
- 10,000 bindings
→ 💥 UI freezes, memory explodes
👉 Solution: Virtualization
Only render what the user can see.
2. Beginner Mental Model
ItemsControl
Think of it like:
“Repeat this UI for every item in a collection”
ItemsControl = foreach (item in ItemsSource) → render UIVirtualization
Think of it like:
“Only create UI for visible items”
If you see 20 rows:
- Only 20 UI elements exist
- Not 10,000
👉 Simple analogy:
| Without virtualization | With virtualization |
|---|---|
| Render entire book | Render only visible page |
3. Basic Example
ViewModel
public class InspectionViewModel
{
public ObservableCollection<InspectionResult> Results { get; } = new();
public InspectionViewModel()
{
for (int i = 0; i < 1000; i++)
{
Results.Add(new InspectionResult
{
Id = i,
Status = i % 2 == 0 ? "OK" : "NG"
});
}
}
}ItemsControl
<ItemsControl ItemsSource="{Binding Results}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id}" Margin="5"/>
<TextBlock Text="{Binding Status}" Margin="5"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>👉 Result:
- 1000 items → 1000 UI elements
- No virtualization
- OK for small lists, bad for large ones
4. How It Really Works in WPF
Now the real engine.
4.1 ItemContainer
Each ItemsControl creates a container per item.
Examples:
| Control | Container |
|---|---|
| ItemsControl | ContentPresenter |
| ListBox | ListBoxItem |
| ListView | ListViewItem |
👉 So actual structure:
ListBox
├── ListBoxItem
│ └── (DataTemplate UI)
├── ListBoxItem
│ └── (DataTemplate UI)4.2 ItemContainerGenerator
This is the hidden engine.
Converts data items → UI containers
Conceptually:
foreach (var item in ItemsSource)
{
var container = CreateContainer(item);
ApplyTemplate(container);
AddToVisualTree(container);
}👉 Important:
- Happens lazily when virtualization is enabled
- Happens eagerly when disabled
4.3 DataTemplate
This defines:
“What UI does each item look like?”
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>👉 Flow:
Data → Container → DataTemplate → Visual elements5. Virtualization Deep Dive
Now the real power.
5.1 What Virtualization Actually Does
Without virtualization:
10000 items → 10000 UI elementsWith virtualization:
Viewport shows 20 items → 20 UI elementsAs you scroll:
- old items destroyed or reused
- new items created or reused
5.2 VirtualizingStackPanel
This is the key.
Default panel used by ListBox/ListView for virtualization
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>👉 Responsibilities:
- Measure only visible items
- Request containers only for visible range
- Manage scrolling offset
5.3 Container Recycling
Two modes:
Standard mode
- Destroy old containers
- Create new ones
Recycling mode (better)
VirtualizingStackPanel.VirtualizationMode="Recycling"👉 Reuses containers:
Scroll down:
Item #1 container → reused for Item #21💡 Huge benefit:
- Less allocation
- Less GC pressure
- smoother scrolling
6. Real-World Example
Example 1: Inspection Machine UI
You display:
10,000 inspection results
(each with image + status + metadata)Without virtualization:
- UI freeze on load
- memory spike
- scroll lag
With virtualization:
- only ~20–50 items rendered
- smooth scrolling
- stable memory
Example 2: Log Viewer
Real-time logs streaming:
100 logs/sec
total = 50,000 rowsWithout virtualization:
- UI dies quickly
With virtualization:
- UI stays responsive
- only visible logs rendered
7. Common Mistakes (VERY important)
❌ 1. Using StackPanel
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel /> <!-- BAD -->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>👉 This disables virtualization completely.
❌ 2. Wrapping in ScrollViewer
<ScrollViewer>
<ListBox ... />
</ScrollViewer>👉 Breaks virtualization
Because:
- ListBox can no longer control scrolling
❌ 3. Using ItemsControl instead of ListBox
ItemsControl:
- ❌ no virtualization by default
ListBox/ListView:
- ✅ virtualization enabled
❌ 4. Complex DataTemplates
<DataTemplate>
<Grid>
<Image ... />
<Charts ... />
<Heavy controls ... />
</Grid>
</DataTemplate>👉 Even with virtualization:
- heavy rendering = slow scrolling
❌ 5. Binding to non-virtualizing collections
- using LINQ
.ToList()repeatedly - recreating collections every frame
8. Performance & Tuning
✅ Ensure virtualization is ON
<ListBox
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
ScrollViewer.CanContentScroll="True"/>✅ Keep templates lightweight
- avoid deep visual trees
- avoid heavy controls inside items
✅ Use UI virtualization + data virtualization
UI virtualization:
- only render visible UI
Data virtualization:
- only load data when needed
✅ Batch updates
Bad:
foreach (var item in items)
collection.Add(item);Better:
- suspend notifications
- bulk add
✅ Avoid frequent collection reset
collection = new ObservableCollection(...)👉 causes full UI rebuild
9. Practical Guidance
When to use what
| Control | Use case |
|---|---|
| ItemsControl | small lists, custom layout |
| ListBox | general lists |
| ListView | lists with columns |
| DataGrid | tabular large data |
Design scalable templates
Good:
<TextBlock Text="{Binding Name}" />Bad:
<Grid>
<Image Source="{Binding HugeImage}" />
<3 nested panels>
</Grid>Monitor performance
Use:
- Visual Studio Diagnostic Tools
- Live Visual Tree
- PerfView (advanced)
Watch:
- UI thread usage
- memory growth
- layout time
10. Summary
Here’s what you should remember:
Core mental model
Collection → Container → Template → UIKey truths
- ItemsControl = repeat UI per item
- Each item = real UI object (expensive)
- Virtualization = only create visible UI
Performance rule
“UI elements are expensive. Avoid creating too many.”
Virtualization essentials
- Use ListBox/ListView/DataGrid
- Use VirtualizingStackPanel
- Enable Recycling mode
- Avoid breaking it (StackPanel, ScrollViewer)
Real-world mindset
When you see:
large collections (1000+ items)Always ask:
- Is virtualization enabled?
- Is template lightweight?
- Are containers reused?
If you want next level, we can go deeper into:
👉 “Why virtualization sometimes silently stops working” 👉 “UI virtualization vs data virtualization (very important for backend-heavy apps)” 👉 “How WPF scrolling pipeline actually works internally”
Just tell me 👍