Agent skill
navigating-visual-logical-tree
Navigates WPF Visual Tree and Logical Tree with VisualTreeHelper and LogicalTreeHelper patterns. Use when traversing elements, accessing template internals, or understanding event routing.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/navigating-visual-logical-tree
SKILL.md
WPF Visual Tree & Logical Tree Patterns
In WPF, element relationships are represented by two tree structures.
1. Core Differences
1.1 Logical Tree
- Structure of elements explicitly declared in XAML
- Based on Content relationships
- Event routing path
- Inherited property (DataContext, FontSize, etc.) propagation path
1.2 Visual Tree
- Includes all elements actually rendered
- Includes elements inside ControlTemplate
- Basis for Hit Testing
- Determines rendering order
1.3 Comparison Example
<!-- XAML definition -->
<Window>
<Button Content="Click"/>
</Window>
Logical Tree: Visual Tree:
Window Window
└── Button └── Border (inside Button's Template)
└── ContentPresenter
└── TextBlock ("Click")
2. VisualTreeHelper
2.1 Key Methods
namespace MyApp.Helpers;
using System.Windows;
using System.Windows.Media;
public static class VisualTreeHelperEx
{
/// <summary>
/// Get child element count
/// </summary>
public static int GetChildCount(DependencyObject parent)
{
return VisualTreeHelper.GetChildrenCount(parent);
}
/// <summary>
/// Get child element by index
/// </summary>
public static DependencyObject? GetChild(DependencyObject parent, int index)
{
return VisualTreeHelper.GetChild(parent, index);
}
/// <summary>
/// Get parent element
/// </summary>
public static DependencyObject? GetParent(DependencyObject child)
{
return VisualTreeHelper.GetParent(child);
}
}
2.2 Finding Children of Specific Type
namespace MyApp.Helpers;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
public static class VisualTreeSearcher
{
/// <summary>
/// Find all child elements of specific type
/// </summary>
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject parent) where T : DependencyObject
{
var childCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T typedChild)
{
yield return typedChild;
}
// Recursive search
foreach (var descendant in FindVisualChildren<T>(child))
{
yield return descendant;
}
}
}
/// <summary>
/// Find first child element of specific type
/// </summary>
public static T? FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
var childCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T typedChild)
{
return typedChild;
}
var result = FindVisualChild<T>(child);
if (result is not null)
{
return result;
}
}
return null;
}
/// <summary>
/// Find child element by name
/// </summary>
public static T? FindVisualChildByName<T>(DependencyObject parent, string name) where T : FrameworkElement
{
var childCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T element && element.Name == name)
{
return element;
}
var result = FindVisualChildByName<T>(child, name);
if (result is not null)
{
return result;
}
}
return null;
}
}
2.3 Finding Parent Elements
namespace MyApp.Helpers;
using System.Windows;
using System.Windows.Media;
public static class VisualParentSearcher
{
/// <summary>
/// Find parent element of specific type
/// </summary>
public static T? FindVisualParent<T>(DependencyObject child) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(child);
while (parent is not null)
{
if (parent is T typedParent)
{
return typedParent;
}
parent = VisualTreeHelper.GetParent(parent);
}
return null;
}
/// <summary>
/// Find parent element matching condition
/// </summary>
public static DependencyObject? FindVisualParent(
DependencyObject child,
Func<DependencyObject, bool> predicate)
{
var parent = VisualTreeHelper.GetParent(child);
while (parent is not null)
{
if (predicate(parent))
{
return parent;
}
parent = VisualTreeHelper.GetParent(parent);
}
return null;
}
}
3. LogicalTreeHelper
3.1 Key Methods
namespace MyApp.Helpers;
using System.Collections;
using System.Windows;
public static class LogicalTreeHelperEx
{
/// <summary>
/// Enumerate child elements
/// </summary>
public static IEnumerable GetLogicalChildren(DependencyObject parent)
{
return LogicalTreeHelper.GetChildren(parent);
}
/// <summary>
/// Get parent element
/// </summary>
public static DependencyObject? GetLogicalParent(DependencyObject child)
{
return LogicalTreeHelper.GetParent(child);
}
}
3.2 Logical Tree Search
namespace MyApp.Helpers;
using System.Collections.Generic;
using System.Windows;
public static class LogicalTreeSearcher
{
/// <summary>
/// Find all logical children of specific type
/// </summary>
public static IEnumerable<T> FindLogicalChildren<T>(DependencyObject parent) where T : DependencyObject
{
foreach (var child in LogicalTreeHelper.GetChildren(parent))
{
if (child is T typedChild)
{
yield return typedChild;
}
if (child is DependencyObject depObj)
{
foreach (var descendant in FindLogicalChildren<T>(depObj))
{
yield return descendant;
}
}
}
}
/// <summary>
/// Find logical parent of specific type
/// </summary>
public static T? FindLogicalParent<T>(DependencyObject child) where T : DependencyObject
{
var parent = LogicalTreeHelper.GetParent(child);
while (parent is not null)
{
if (parent is T typedParent)
{
return typedParent;
}
parent = LogicalTreeHelper.GetParent(parent);
}
return null;
}
}
4. Scenario-based Selection
4.1 Visual Tree Use Scenarios
// 1. Access elements inside template
var scrollViewer = VisualTreeSearcher.FindVisualChild<ScrollViewer>(listBox);
// 2. Register focus event to all TextBoxes
foreach (var textBox in VisualTreeSearcher.FindVisualChildren<TextBox>(window))
{
textBox.GotFocus += OnTextBoxGotFocus;
}
// 3. Find ListBoxItem of clicked element
var listBoxItem = VisualParentSearcher.FindVisualParent<ListBoxItem>(clickedElement);
4.2 Logical Tree Use Scenarios
// 1. Check DataContext inheritance path
var dataContextSource = LogicalTreeSearcher.FindLogicalParent<FrameworkElement>(element);
// 2. Process only explicitly declared children
foreach (var button in LogicalTreeSearcher.FindLogicalChildren<Button>(panel))
{
// Buttons inside ControlTemplate are excluded
}
5. Event Routing and Trees
5.1 Bubbling (Upward)
Event propagates along Visual Tree path
Button click → ContentPresenter → Border → Grid → Window
5.2 Tunneling (Downward)
Preview events start from root and propagate downward
Window → Grid → Border → ContentPresenter → Button
5.3 Code Example
// PreviewMouseDown: Tunneling (Window → Target)
window.PreviewMouseDown += (s, e) =>
{
// Check target element
var target = e.OriginalSource as DependencyObject;
// Check parent in Visual Tree
var button = VisualParentSearcher.FindVisualParent<Button>(target);
if (button is not null)
{
// Click inside button area
}
};
// MouseDown: Bubbling (Target → Window)
button.MouseDown += (s, e) =>
{
// Stop bubbling if already handled
e.Handled = true;
};
6. Advanced Patterns
For advanced patterns, see references/tree-advanced.md:
- Template Access: OnApplyTemplate, GetTemplateChild patterns
- Performance Optimization: Depth-limited search, cached results
7. Summary Comparison Table
| Aspect | Visual Tree | Logical Tree |
|---|---|---|
| Included elements | All rendered elements | XAML-declared only |
| Template internals | Included | Not included |
| Helper class | VisualTreeHelper | LogicalTreeHelper |
| Use case | Rendering, Hit Test | Inherited properties, structure |
| Completion time | After Loaded | Immediately on creation |
8. References
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?