* Fix CalendarDatePicker.Focus() not focusing the text box
OnGotFocus only forwarded focus to PART_TextBox when the navigation method
was Tab, so a programmatic Focus() call left focus on the picker itself.
Gate on the focus source instead, and keep select-all on Tab only.
* Remove comment from CalendarDatePicker.OnGotFocus
* Added failng popup child margin test
* Ignore popup child margin when positioning
Avoids drop shadows and other out-of-bounds visual effects from affecting popup positioning
* Fix warnings in tests
* Pass Deflate to WSurface
---------
Co-authored-by: Tom Edwards <tom.edwards@chaos.com>
* feat: Add IsVisible to TableViewColumn
* test: Add TableView IsVisible tests
* feat: Add option to hide a column in the ControlCatalog
* chore: Remove unnecessary remark from property
* docs: An other xml comment cleanup
* fix: Use NaN as a sign that the width is reset and needs recalculation
Also, set the ActualWidth of a hidden column to 0
* chore: Add a comment explaining what NaN means for columns ActualSize
* Keep TextBlock text runs in sync with its inlines
_textRuns is built from Inlines, but OnMeasureInvalidated discarded it on
any measure invalidation while Inlines still held the content. Between
that point and the next measure pass, CreateTextLayout read a null
_textRuns as "no inlines" and shaped Text instead, which is null whenever
the content lives in Inlines. That empty result went into the
TextRunCache, keyed by text source index, so every later layout reused it
and the control rendered nothing until something invalidated the cache.
- Discard _textRuns in InvalidateTextLayout, next to the run cache, so
the runs and the cache are dropped by the same event and cannot
disagree about the content.
- Build the runs on demand in EnsureTextRuns, and pick the text source by
HasComplexContent rather than by _textRuns being set.
- Split the constraint-dependent work out of run building. Runs answer to
the content alone; only an embedded control answers to the available
width, so Inline.MeasureEmbeddedControls measures it and
EmbeddedControlRun reports the child's DesiredSize live. Runs now
survive a constraint change instead of being rebuilt every measure.
SelectableTextBlock never hands the run cache to its layout, but it
shared the same fallback and kept the wrong layout on _textLayout.
Fixes#21902
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Invalidate the layout when LineSpacing changes
LineSpacing had no case in the property change switch and is not one of
the properties registered with AffectsRender, so changing it left the
measured size and the rendered text untouched even though CreateTextLayout
feeds it into the paragraph properties. It changes line placement rather
than shaping, so it belongs with LineHeight and the other properties that
keep the run cache.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Drop the text layout whenever the content is invalidated
InvalidateMeasure only raises OnMeasureInvalidated while the measure is
still valid, so a second content change before the next measure pass left
_textLayout holding the layout the first change had already replaced.
MeasureOverride keeps that layout when the constraint has not moved, so
the block measured and rendered the superseded content.
Clear the layout in InvalidateTextLayout and InvalidateTextLayoutKeepCache
rather than relying on OnMeasureInvalidated to run, which is what
TextPresenter already does.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Drop the text layout when an embedded control resizes
A line snapshots its metrics when it is formatted, so a layout built
before a child was measured again keeps reporting the width and height
that child used to have. MeasureOverride reuses the layout whenever the
constraint has not moved, so a control that resizes while the block is
already measure invalid never reaches the measured size.
- MeasureEmbeddedControls reports whether any child came back a different
size, and the layout is dropped only then rather than on every pass.
- Route the remaining layout resets through DisposeTextLayout so every
reset goes through one place.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* add failing test for ghost items after showing a hidden ListBox
* fix ghost items
* add a failing test
* fix for ghost item
* more tests
* Fix return statement in VirtualizingStackPanel
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* add page navigation using generic type
* add tests
* add default impl to INavigation api
* fix nullable errors
* update api diff
* addressed comment
* update api
* move page externsion to own class
* make parameter overload non-optional
* Add failing test for #7536
Reproduces the "Cannot change source while update is in progress" crash that
occurs when a SelectingItemsControl's ItemsSource is changed from within its own
SelectionChanged handler while the selection is being lost. When selection is
lost, CommitOperation bumps the operation UpdateCount before raising
LostSelection but never decrements it, so SelectionChanged is raised with
UpdateCount > 0 and SetSource throws.
Claude-Session: https://claude.ai/code/session_01FXP1ejZ9VhUwecY9QCRKVb
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Fix#7536: crash when changing source during SelectionChanged
When the selection was lost, SelectionModel.CommitOperation incremented the
operation's UpdateCount before raising LostSelection but never decremented it.
As a result the rest of the commit - including the SelectionChanged event - ran
with UpdateCount > 0, so a handler that changed the control's source (as
StructuredLogViewer's UpdateBreadcrumb does) hit the "Cannot change source while
update is in progress." guard in SetSource and threw.
Decrement UpdateCount again once the LostSelection handler has returned so that
the batching only covers the handler itself and the SelectionChanged event is
free to change the source.
Claude-Session: https://claude.ai/code/session_01FXP1ejZ9VhUwecY9QCRKVb
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Add test for changing selection from SelectionChanged when AlwaysSelected reselects
Covers the interaction between the #7536 fix and AlwaysSelected: clearing the
selection makes AlwaysSelected reselect the first item via LostSelection (which
must still fold into the current operation), and a SelectionChanged handler that
then changes the selection must be honoured rather than swallowed by the batch
update wrapping the LostSelection handler. Fails before the fix (selection stays
on the reselected item), passes after.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FXP1ejZ9VhUwecY9QCRKVb
* Assert that `SelectionChanged` was called.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* perf(text): test the selection by index instead of building it
UpdateCommandStates only needs to know whether the selection is empty, but it
called GetSelection, which allocates a substring on every selection change. Test
the indices instead, so a selection change no longer allocates.
Requested by @Gillibald in #21492.
* perf(text): read the selection properties once in HasSelection
* Add BringIntoView pass to the LayoutManager
* Make ProcessBringIntoViewRequests part of the standard layout pass
* Clarify why TryScrollIntoViewNow's result is ignored
---------
Co-authored-by: grokys <grokys@users.noreply.github.com>
* fix: light dismiss behavior fails when opened from the ContextMenu.
* 1. LightDismissOverlayLayer is invisible by default.
2. Eliminate misinformation that Registration may bring.
3. Remove LightDismissOverlayLayer Manually set IsVisible to false at creation time.
---------
Co-authored-by: Steven Kirk <grokys@users.noreply.github.com>
* Update ncrunch config.
* Initial groundwork for typed binding expressions.
- Moved a bunch of stuff from `UntypedBindingExpressionBase` to `BindingExpressionBase`
- Make various APIs accept `BindingExpressionBase` instead of `UntypedBindingExpressionBase`
- Added a typed `IPropertyInfo`
- Added a method on the `CompiledBindingPathBuilder` to build typed property accessors
- Initial basic implementation of `TypedBindingExpression`
* Track values in typed binding expression.
* Support binding mode in typed binding expression.
* Add typed binding expression benchmarks.
Add Setup/Values benchmarks comparing the typed binding expression
against the untyped CompiledBinding (as emitted by the XAML compiler
today) and the reflection-based Binding.
Writing the benchmarks surfaced that TypedBindingExpression never
detached: it had no Dispose override, so disposing a binding (or
rebinding the same property) leaked its PropertyChanged subscriptions
and left it registered in the value store. Wire up disposal mirroring
UntypedBindingExpressionBase: stop, detach handlers, remove from the
value store and frame. This required widening
IBindingExpressionSink.OnCompleted from UntypedBindingExpressionBase to
BindingExpressionBase. Also fix a nullable warning that broke the
Release build.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
* Don't box value in IBindingExpressionSink.OnChanged.
Instead notify the sink of what's changed and let the sink read the value boxed or unboxed.
* Move `IValueEntry` to `BindingExpressionBase`
* Fall back to BindingExpression.
Make `TypedPropertyElement` derive from `PropertyElement` and fall back to creating a standard boxing `BindingExpression` if any of the requirements for a typed binding expression are not met.
* Don't use typed expressions for DataContext.
* Emit typed binding expression from XAML compiler.
When a compiled binding path is shape-eligible (single CLR property on a
reference-type source, no transforms, instance getter) the compiler now
emits a call to the typed CompiledBindingPathBuilder.Property<TSource,
TResult> overload, producing a TypedPropertyElement that the runtime
turns into a non-boxing TypedBindingExpression<TSource, TValue>. Other
shapes continue to use the existing untyped emission path, and runtime
fallback to BindingExpression still kicks in when modifiers (Source,
Converter, StringFormat, etc.) make the typed expression unsuitable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add failing tests for TwoWay typed binding source echo.
TypedBindingExpression writes the source value back to the source both on
attach and whenever the source raises a change: pushing the value to the
target re-enters OnTargetPropertyChanged, which (in TwoWay mode) calls
WriteValueToSource with the value that just arrived from the source.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Don't echo source value back to source in TwoWay typed binding.
When TypedBindingExpression pushed the source value to the target, the
resulting target PropertyChanged re-entered OnTargetPropertyChanged which,
in TwoWay mode, wrote the value straight back to the source - a redundant
round-trip on every attach and every source-originated change.
Guard the source->target push with a flag and skip WriteValueToSource while
it is set, so only genuine target changes are written back to the source.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Address typed binding code-review follow-ups.
- Reset _isRunning when a typed binding is unsubscribed so the expression
can be restarted (and re-subscribe to its source) if the value store
reactivates the entry later, matching UntypedBindingExpressionBase.Stop().
- Remove a stray `using static PropertySetSnapshot` import.
- Extract the duplicated IValueEntry value-unwrapping logic (shared by
EffectiveValue<T> and DirectPropertyBase) into IValueEntry.TryGetValue.
- Drop the unreachable UpdateSourceTrigger NotSupportedException in
CreateTypedExpression; CanUseTypedBindingExpression already constrains it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fix TypedBinding_Values benchmark build error.
The typed binding path was built with a single-argument `Property(propertyInfo)`
call, which has no matching overload, so the benchmark project did not compile.
Use the same typed three-argument overload as TypedBinding_Setup so the benchmark
exercises the TypedPropertyElement path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Skip typed binding expression for data-validation properties.
TypedBindingExpression does not support data validation, but the eligibility
check didn't account for it, so a directly-assignable single-property DataContext
binding to a validation-enabled target (e.g. TextBox.Text, NumericUpDown.Value)
would take the typed path and silently drop validation errors.
Fall back to the untyped BindingExpression when the target property enables data
validation. Data validation support in the typed expression can be added as a
follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Clarify that data validation is not supported.
* Address typed binding review comments.
- React to null/empty PropertyChanged.PropertyName ("all properties
changed") in the typed expression, matching the untyped path.
- Swallow source getter exceptions raised during PropertyChanged.
- Fall back to the untyped path for non-StyledElement targets, read-only
sources and wider target types in TwoWay/OneWayToSource modes.
- Fix a stray space in an exception message.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Share TypedBindingExpression.Attach validation across instantiations.
Factor the validation logic (most notably the exception string
formatting) out of the generic Attach method into a non-generic static
helper. It only uses typeof(TValue), not TValue, so sharing it avoids
duplicating the code per generic instantiation, a meaningful NativeAOT
size saving (~3.3 KB => ~2.5 KB per instantiation).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Update API suppressions.
These APIs are in a `[PrivateApi]`.
* Use cached boolean boxes in typed binding expression.
Merging main brought in the cached boxed booleans emitted for compiled
binding property getters (#21065), but bindings that now take the typed
binding expression path don't go through that getter: the value is boxed
in TypedBindingExpression.GetUntypedValue when the target property isn't
strongly typed (e.g. binding a bool to TextBlock.Tag). Box booleans via a
shared cache there too, restoring the no-allocation-per-read behaviour.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QkxawATDHa6rbPaDmPCLHZ
* Extract the whole TypedBindingExpression.Attach body into AttachCore.
3198196b4c only factored out the validation, leaving the field
assignments in the generic method. As MrJul pointed out, none of those
assignments depend on TSource or TValue either, so the entire body can
move into a method that doesn't reference the generic parameters and be
shared across instantiations rather than duplicated per instantiation.
Attach is now just a forwarder passing typeof(TValue) as a Type, and
AttachCore reads _sink/TargetProperty directly instead of having them
threaded through as parameters, which reads better than the previous
split.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QkxawATDHa6rbPaDmPCLHZ
* Move the method alongside the other privates.
* Add failing tests for typed binding expression breaking changes.
Each test binds the same property twice: once via a path which produces a
TypedBindingExpression and once via a path which produces an untyped
BindingExpression. The assertions describe the untyped behaviour, so the
typed cases fail where the typed expression diverges:
- A binding with no value (null or incompatible DataContext) publishes the
target property's default value at the binding's priority instead of not
contributing a value, overriding style setters and breaking inheritance.
- Writing a value of another type, or null, to an object-typed target
property throws from OnTargetPropertyChanged.
- No binding error is logged when the DataContext is of the wrong type.
- A throwing source getter leaves the stale value in place.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B66teLaFQBr9GJe4a1aTZm
* Fix typed binding expression divergences from the untyped path.
- Don't notify when the expression has no value and had none before: doing
so pushed the target property's default value at the binding's priority,
overriding style setters and breaking property inheritance.
- Read the target property's new value defensively. The binding value type
only needs to be assignable to the target property type, so the property
can hold a value which isn't a TValue; casting it threw out of the caller's
SetValue.
- Log a binding error when the DataContext can't be converted to the source
type, and when the source getter throws. The getter also now clears the
value rather than leaving the stale one in place.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B66teLaFQBr9GJe4a1aTZm
* Pass correct priority to child bindings.
The priority here doesn't actually have any effect as the binding isn't in a value store, but there's no reason to not use the correct one.
* Use the typed overload.
* Set initial priority to default priority.
Makes `TypedBindingExpressionBase` and `UntypedBindingExpressionBase` have the same behavior here.
* Remove unused vars.
* We now have a typed value here.
* Publish typed binding values on the UI thread.
The source's PropertyChanged event can be raised on any thread, so marshal
the notification to the UI thread as UntypedBindingExpressionBase does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B1ZwmCJkThPSeViLnRxoYs
* Share the property info cache lookup.
Emit and EmitTyped had identical cache lookups, differing only in which
dictionary they searched. Also delay constructing the generic types in
EmitTyped until a cache miss, as they aren't needed on a hit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B1ZwmCJkThPSeViLnRxoYs
* Share the property accessor delegate emission.
EmitFunc was duplicated as a local function in both Emit and EmitTyped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B1ZwmCJkThPSeViLnRxoYs
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fix VirtualizingStackPanel offset when scrolling to variable-sized items
* Remove the `isScrollIntoView` parameter.
* Tweak now-failing test.
Change the the test to check that the element is outside of the viewport, instead of asserting its exact coordinates - that's the important part.
* Retrigger CI.
Azure Pipelines never queued a build for 3d81ceb9.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0169P8BipcMkn44hvhpQ2UNF
---------
Co-authored-by: grokys <grokys@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Implement TopLevel.OpenedPopups
* Delete Headless `GetOpenPopups`
* Reorganize popup tests between Headless/Primitives layers
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Add Popup.OpenedPopups to keep opened popups in tree structure
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Help NUnit suppressing CS8777
* Update suppresions
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* Add failing tests for SelectionBoxItem with a control
* Use ItemTemplate for ComboBox's selection box for controls
* Add failing selection box tests when properties change
* Update selection box when its template changes
* invalidate image measure when IAffectsRender source invalidates
* compare drawing image bounds when checking for measure invalidation
* addressed nit
* use dispatcher run overload in test
* gate subscription behind attachment
* fix typo
* add one more attached check
* nit if style
TopLevel teardown (HandleClosed) was only ever triggered by the backend
invoking ITopLevelImpl.Closed. Browser, iOS, Android, macOS and the offscreen
(designer previewer) impls never raise it from Dispose(), so
EmbeddableControlRoot.Closed was never raised there and StopRendering() was
never reached - the top level stayed registered in MediaContext forever.
Make teardown idempotent behind EnsureClosed() and call it from the managed
Dispose() paths (EmbeddableControlRoot, OffscreenTopLevel, PopupRoot). The
guard lives in a new non-virtual entry point because WindowBase and Window
override HandleClosed and run side effects before calling base.
Also drop ChoreographerTimer's view-visibility gate on Android. It predates
the render timer rewrite and vetoed ticks that DefaultRenderLoop had explicitly
asked for, so the synchronous compositor round-trip in HandleClosed
(Renderer.Dispose -> MediaContext.SyncDisposeCompositionTarget) could never
complete once the view had unsubscribed - deadlocking every activity destroy.
DefaultRenderLoop already owns the sleep/wake state machine, driven by
StartRendering/StopRendering, which makes the extra gate redundant.
Verified at runtime on X11, Wayland, Win32, macOS, Headless, Browser (WASM),
Android and iOS: Closed fires exactly once per teardown, MediaContext returns
to baseline, and platform-initiated closes still fire exactly once.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Add failing test for template child presented inside Viewbox not being reparented on template change
When a ControlTemplate contains Viewbox > ContentPresenter presenting a
control that outlives the template, swapping the template throws
"The control already has a visual parent" because the old presenter
never releases the presented control.
Repro for #9551.
* Forward TemplatedParent to Viewbox's internal container so template teardown reaches its subtree
Viewbox hosts its Child inside an internal ViewboxContainer visual that
is a visual child but not a logical child of the Viewbox. Template
teardown (TemplatedControl.ApplyTemplate) finds descendants to
disconnect via GetTemplateDescendants, a visual-tree walk that stops
recursing at any visual whose TemplatedParent is null. ViewboxContainer
never had a TemplatedParent, so the walk stopped at it and template
descendants hosted inside the Viewbox (e.g. a ContentPresenter) never
had their TemplatedParent cleared. Their template bindings therefore
stayed live, the old ContentPresenter kept the presented control as its
visual child, and re-presenting that control in the new template threw
"The control already has a visual parent".
Forward the Viewbox's TemplatedParent to the container, restoring the
invariant that every template descendant is reachable through visuals
whose TemplatedParent is non-null. Null is deliberately not forwarded:
teardown clears the Viewbox's TemplatedParent while iterating lazily,
and forwarding null would prune the walk before it reaches the
container's subtree; the walk clears the container's TemplatedParent
itself instead.
Fixes#9551
* Trim down comment
---------
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
TableViewColumn extends StyledElement. However, many features such as StyleSelectors did not work because no logical parent was set. This is solved by registering the TableView as a logical parent when the TableViewColumn is attached (and setting it to null when detacting).
* test(VirtualizingStackPanel): cover focused item virtualization
A focused item remains attached so keyboard focus can be preserved after it leaves the realized range. It must nevertheless stop rendering until it is realized again; otherwise an estimated position can surface it in the viewport.
* fix(VirtualizingStackPanel): hide cached focused items
A focused container stays attached after it leaves the realized range so keyboard focus survives. Its estimated position can overlap the viewport when item sizes vary, causing it to render and receive pointer input as a ghost item.\n\nSuppress rendering and hit testing while the container is cached, then restore its original state before re-realization or recycling. This preserves focus and template-provided opacity.
* fix(VirtualizingStackPanel): restore current focused container state
Focused-container suppression previously replayed the opacity and hit-test values observed when virtualization began. That could hide updates from bindings, styles, or application code while the container was retained.\n\nUse disposable animation-priority overrides for the temporary suppression so disposing them reveals the property's current underlying value.
* fix(VirtualizingStackPanel): keep retained focused container out of the viewport
The container of a focused item that leaves the realized range is retained
to preserve keyboard focus, and arranged at a position estimated from the
average realized element size. With differing item sizes that estimate can
place the container so that it overlaps the viewport, where it renders as a
ghost item and intercepts pointer input (#17935).
An unrealized item before the realized range always ends at or before the
range's start, and one after it always starts at or after the range's end.
Clamp the estimated arrange position to that invariant so the retained
container can never overlap the realized elements, replacing the previous
approach of masking the container with animation-priority opacity and
hit-test overrides.
* Proposal: Add week numbers to Calendar
* FluentTheme for adding CW
* Add sample to ControlCatalog about week numbers
* workaround for XAML not accepting empty strings
* SimpleTheme
* refractor TemplateParts of Calendar
* ensure Calendar updates and improve xml comment
* undo whitespace formatting
* Add some test to CalendarTests
* adress self review
* Introduce a method to show / hide the week numbers panel
since it is not part of the MonthGrid, it can leak into the year view. We solved this by updating IsVisible in code behind just like other template parts.
Moved the header into the panel to show / hide it together with the entire Grid.
* fix typo and redundant cast
* Crush the last moths
---------
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
* Add VerticalContentAlignment property to DatePicker and TimePicker
This change registers VerticalContentAlignmentProperty as a
StyledProperty using ContentControl's property as the owner for both
DatePicker and TimePicker controls to maintain framework consistency.
Basic xUnit v3 unit tests are introduced to validate property
round-trips (Top, Center, Bottom, Stretch) and to ensure the default
value correctly resolves to Stretch. This establishes the necessary
C# infrastructure before modifying the control templates.
Part of #21211: implment fluent theme layout
* Fix VerticalContentAlignment bindings and integration tests
- Bind internal grids to VerticalContentAlignment in Fluent and Simple
themes for DatePicker and TimePicker controls.
- Add comprehensive integration tests using mocked NameScopes in
DatePickerTests and TimePickerTests to verify visual propagation.
- Validate visual layout correctness using the ControlCatalog app.
Closes AvaloniaUI#21211
* Remove tautological tests
---------
Co-authored-by: Martim Claudino <martimffclaudino@tecnico.ulisboa.pt>
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
* Add unit test for styling affecting measurements in WindowDrawnDecorationsContent
* Refactor layout measurement to apply styling before visibility checks
* Add core measure test
* Add StackPanel test
* Call ApplyStyling unconditionally in MeasureCore
---------
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
* Correctly set ActualThemeVariant
* Don't call OnColorValuesChanged unnecessarily on Win32
* Don't call OnColorValuesChanged unnecessarily on macOS
* Change iOS PlatformSettings to match other platforms
* Don't call OnColorValuesChanged unnecessarily on Android
* Android: set theme using SetLocalNightMode
* Don't call OnColorValuesChanged unnecessarily on X11/Wayland
* Don't call OnColorValuesChanged unnecessarily on Browser
* Fix timer Start order
* Remove dead code in DBusPlatformSettings
* fix(SelectableTextBlock): fix SelectableTextBlock selection for centered and right-aligned text
* Fix failing unit tests
* Let TextLayout handle out-of-bounds hit testing
---------
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
* test: add failing test for Loaded processing after handler exception
A control whose Loaded handler throws currently breaks the static
loaded-processing machinery: remaining controls in the same dispatcher
batch are skipped and, because _isLoadedProcessing is never reset,
every control created afterwards app-wide never receives Loaded.
The test attaches a throwing control between two siblings, expects the
exception to surface from the dispatcher job, and asserts that both
siblings and a control added in a later batch still get Loaded.
Issue #18742
* fix: recover Loaded event processing after a handler throws
Previously the static loadedProcessingAction iterated the pending
controls with no exception handling. If any control's Loaded handler
or OnLoaded override threw, the batch loop aborted, so:
- _isLoadedProcessing stayed true forever, and since
ScheduleOnLoadedCore only posts a dispatcher job when that flag is
false, no control created afterwards, app-wide, ever received
Loaded;
- the remaining controls in the current batch were skipped;
- the stuck processing queue retained control references (leak).
The invariant violated was that _isLoadedProcessing is true only while
a loaded-processing dispatcher job is actually scheduled or running.
Fix: process the snapshot as a drain queue (Dequeue before invoking,
because OnLoadedCore must not run twice for a control) and restore the
invariant in a finally block: requeue the unprocessed remainder, clear
the processing queue, reset the flag and post a new dispatcher job if
work remains. try/finally was chosen over catching per control so the
exception still propagates to the dispatcher as an unhandled exception
(matching WPF semantics) instead of being swallowed or aggregated,
while controls after the faulty one get Loaded on the next dispatcher
job.
Fixes#18742
* Add failing test for #14718
* Fix for AutoScrollToSelectedItemIfNecessary
* fix failing CI build and move test to the right location
* add failing test for TabItem selection of invisble tab
* introduce a helper method to figure out which item to select
when nothing was selected beforehand and AlwaysSelected is true
* ensure selection works for invisible tabcontrol
* simplify conditions
* propose: Remove redundant logic from ColorView
The TabItem now handles the correct selection of only visible items
* fix test: Need to set SelectedIndex after adding items
* re-add unused method and make it obsolete
Otherwise API-diff will fail.
* Address review
* Update tests/Avalonia.Controls.UnitTests/TabControlTests.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update tests/Avalonia.Controls.UnitTests/TabControlTests.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Adress copilot review
* fix duplicate braces
* address review
* fix for failing tests on CarouselPage and TabbedPage
* address feedback
- adding more tests to avoid regressions
* Fix failing test: add UnitTestApplication.Start() to dedicated thread test
Agent-Logs-Url: https://github.com/timunie/Avalonia/sessions/fe2f1190-6d20-4982-8a03-1ae9b52ee701
Co-authored-by: timunie <47110241+timunie@users.noreply.github.com>
* Refactor SelectingItemsControl auto-scroll duplicate logic
Agent-Logs-Url: https://github.com/timunie/Avalonia/sessions/8895cb60-ef41-473c-972b-bad2483a5a77
Co-authored-by: timunie <47110241+timunie@users.noreply.github.com>
* Add new failing tests for AlwaysSelected mode
* Fix AlwaysSelected scenarios
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
* test(grid): reproduce shared size groups ignoring assigned definitions
Definitions supplied through the ColumnDefinitions or RowDefinitions
setter are already in the collection when the grid claims it, so they
never pass through the collection-changed handler that joins them to the
parent tree. They never register with their shared size group, and the
definitions they replace never unregister from it.
* fix(grid): join assigned definition collections to the parent tree
DefinitionList.SetParent assigned each definition's Parent but never
called OnEnterParentTree, which only ran from the collection-changed
handler. Assigning Parent is not sufficient: OnEnterParentTree also sets
InheritanceParent, and a definition cannot read the inherited
PrivateSharedSizeScope that registers it with its group until that link
exists. Definitions supplied through the ColumnDefinitions setter - an
object initializer, a shared resource, or ColumnDefinitions="Auto,*" -
were therefore silently absent from their shared size group.
Enter and exit the parent tree from SetParent, and release the outgoing
collection when Grid swaps one in. Without that release the replaced
definitions stay registered with the group; nothing resets their measured
minimum any more, so they pin it at whatever they last contributed.
* test(grid): cover the definition ownership contract
Removing a definition leaves it holding its old Parent and its property
inheritance link, so it still reads the grid's shared size scope and can
re-register itself into a scope it has left. Also covers moving a
definition between grids, reassigning the same collection, and row
definitions, which the assignment fix reached but nothing exercised.
* refactor(grid): centralise definition parent-tree transitions
Definition ownership was implemented twice, and the two paths disagreed:
SetParent exited a definition and cleared its Parent, while removing one
from the collection called OnExitParentTree but left Parent set. Detach
was incomplete either way, since OnEnterParentTree establishes
InheritanceParent but OnExitParentTree never cleared it - so a removed
definition kept reading the grid's inherited PrivateSharedSizeScope, and
the grid kept it alive as an inheritance child.
Route every owner change through one transition that exits the old tree,
assigns Parent, and enters the new one, and clear InheritanceParent on
exit so detach mirrors attach.
---------
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>