# Avalonia + .NET 10 Gotchas
Distillation of foot-guns hit while building this app. Future contributors (and AI sessions) repeat these mistakes if they're not warned. Each entry: the symptom, the cause, the fix.
This doc covers Avalonia 12 + .NET 10 + Semi.Avalonia + the supporting packages used by ClaudeForge. Pair with [TRIMMING.md](../TRIMMING.md) (trim-specific concerns) and [LINUX-DESKTOP-INTEGRATION.md](LINUX-DESKTOP-INTEGRATION.md) (Linux platform integration).
---
## XAML / layout
### `Orientation="Horizontal" StackPanel` doesn't constrain children — `TextWrapping="Wrap"` never engages
**Symptom:** Long text overflows the right edge of a horizontal `StackPanel` even with `TextWrapping="Wrap"` set on the inner `TextBlock`.
**Cause:** `StackPanel.MeasureOverride` passes `double.PositiveInfinity` in its stack direction (horizontal in this case). The child receives infinite available width during measure, so wrapping never has a width to wrap against.
**Fix:** Use `DockPanel LastChildFill="True"` instead. Dock the bullet glyph (or icon) `Left`, the wrappable text takes the constrained remaining width.
```xml
```
Same caveat applies to `Orientation="Vertical" StackPanel` in the vertical direction — child gets infinite available height. Less commonly a problem because vertical scrolling is the usual escape hatch.
### Tooltips don't propagate from child to parent
**Symptom:** `ToolTip.Tip` set on a parent `Border` works when hovering the empty padding/fill area but NOT when hovering the inner `TextBlock` content.
**Cause:** Avalonia tooltip resolution doesn't walk up the visual tree. The hit-tested control either has a tooltip or it doesn't — child controls don't inherit from ancestors.
**Fix:** Set `ToolTip.Tip` on BOTH the parent and the inner `TextBlock` (or any child the user is likely to hover):
```xml
```
This pattern is documented in `PropertyEditorWrapper.axaml`'s scope-badge with the comment "set on BOTH so the entire coloured chiclet triggers the tooltip on hover."
### XML-comment double-dash (`--`) breaks AXAML
**Symptom:** Build fails with `Avalonia error AVLN1001: An XML comment cannot contain '--'`.
**Cause:** XML 1.0 disallows `--` inside ``. Easy to hit when writing comment text that mentions a CLI flag (`--showAllNew`).
**Fix:** Reword to avoid the literal `--`. e.g. "the showAllNew flag" instead of "the `--showAllNew` flag".
### `IsVisible="False"` collapses to zero space inside `StackPanel`
This one is GOOD — it's how the nav-tree icon column hides on sub-items without leaving an indent. Documented here so contributors don't reach for `Visibility=Collapsed` (WPF idiom) which doesn't exist in Avalonia.
```xml
```
---
## Styling / theming
> Colour-token policy (why we don't use the `SystemControl*` family under Semi.Avalonia) lives in [UI-STYLE-GUIDE.md](UI-STYLE-GUIDE.md) §2. The two entries below are the *mechanical* traps that make a correct-looking style silently do nothing.
### A local value OUTRANKS every Style setter — a styled property must not also be set as an attribute
**Symptom:** A conditional class (`Classes.foo="{Binding Bool}"`) is applied correctly and the selector matches, but the visual never changes. Selector specificity, `!important`-style tricks, and reordering all fail to help.
**Cause:** Avalonia's value precedence is **Animation > LocalValue > Style > Inherited > Default**. Writing `BorderBrush="Transparent"` as an *attribute* sets a **LocalValue**, which beats **every** Style setter regardless of selector. The style isn't losing on specificity — it's outranked by category, so it can never win.
This is the same priority rule that forces `MarkdownBodyView` to re-colour Markdown.Avalonia's output with a tree-walk: the package writes `Foreground` inline at LocalValue priority, so no Style setter can override it.
**Fix:** Put **both** states in styles. The element declares no attribute for that property; the default comes from a base-class style and the active state from a more specific one.
```xml
```
Note `BorderThickness` may stay an attribute — nothing styles it, so there's no conflict. The rule is only about properties a style also sets. Keeping a constant thickness with a transparent default is what stops the layout shifting when the frame appears.
Working reference: `Button.hint-segment` / `.active` in `GuidedRuleBuilderView.axaml` — its default `Background="Transparent"` is a *Style setter*, which is exactly why its conditional `.active` override works.
### A control's `Styles` apply to its DESCENDANTS, not to itself
**Symptom:** A `
```
Both traps can be present at once and mask each other — fixing only the scoping still leaves the LocalValue beating the setter. If a class-driven style does nothing, check **both**.
---
## Templates / controls
### `DataTemplate`s match in DECLARATION ORDER — a subclass template must be declared BEFORE the base type's
**Symptom:** A new view-model that derives from an existing one renders with the *base* type's template; the new template appears to be ignored.
**Cause:** Avalonia walks the `DataTemplates` collection in order and takes the first template whose type matches. A `DataTemplate` for the base type matches derived instances too, so whichever is declared first wins.
**Fix:** Declare the most-derived template first.
```xml
... ...
```
Reference: `PropertyEditorWrapper.axaml` — the `model` picker template sits immediately above the generic enum template for exactly this reason.
### `AutoCompleteBox.ItemFilter` SUPERSEDES `FilterMode`
**Symptom:** Code that "shows the full list" by setting `FilterMode = AutoCompleteFilterMode.None` has no effect on a box that uses a custom filter — the list stays filtered to the current text.
**Cause:** When an `ItemFilter` **delegate** is set, `FilterMode` is ignored entirely.
**Fix:** To temporarily show everything, swap the *delegate*, then restore it on `DropDownClosed`.
```csharp
AutoCompleteFilterPredicate