The binding path parser built type names with ParseIdentifier, which doesn't
accept '+', so a cast like ((local:Outer+Nested)DataContext) stopped parsing at
the '+' and then failed expecting a ')'. Parse type names with a variant that
accepts '+'. Ordinary identifiers are unchanged.
* 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>
* Add failing test for boxed booleans in compiled binding getters
Compiled-binding getters for bool properties emit 'box' on every read,
allocating a fresh object each time (#21065 reports ~300K allocations in
a real app). Add BooleanBoxes with two cached boxed values and a test
asserting that a compiled binding to a bool property produces the cached
box instances. BooleanBoxes must be public because compiled XAML IL is
emitted into user assemblies, which need to reference its fields.
* Reuse cached boxed booleans in compiled binding getters
Instead of emitting 'box bool' in the generated property-info getter,
emit a branch that returns BooleanBoxes.True or BooleanBoxes.False, so
reads of bool properties through compiled bindings no longer allocate.
Other value types keep the existing box. Applies to both the build-time
XAML compiler and the runtime XAML loader, which share this emitter.
Fixes#21065
* fix: emit cached boolean boxes into the generated per-assembly helper
The cached boxed booleans used by compiled-binding bool getters were
exposed as a new public BooleanBoxes class in Avalonia.Base, adding
public API surface that is useful only to compiled XAML. Review asked
for the boxes to stay an implementation detail instead.
Define two private static fields and a static constructor on the
XamlIlHelpers type the XAML compiler already generates into each user
assembly, and point the getter IL at those. The fields are shared by
all XAML files in the assembly, so allocation behaviour is unchanged
while no public API is added. The test now asserts box identity across
repeated reads instead of referencing the removed class.
* Allow single parameter with any type when binding to method
* Port method binding logic to ReflectionBinding
* Don't depend on the order of methods
* Handle overrides properly
* Fix nullability warning
* Fix#20625: fix compiled binding DataContext inference in ItemTemplate
The Sandbox repro failed with AVLN2100/AVLN2000 when compiled
bindings were used with ItemTemplate and name-based DataContext
access.
Root causes:
- No fallback DataContext type was inferred for the root object when
x:DataType was absent.
- Name-scope lookup could capture stale DataContext metadata from an
unrelated traversal branch, causing #ListBoxRoot.DataContext to
incorrectly resolve to the item type.
Fixes:
- Add root-level DataContext fallback inference in
AvaloniaXamlIlDataContextTypeTransformer.
- Update ScopeRegistrationFinder to resolve DataContext from the
current ancestor stack when the matching name is found.
- Preserve first-match semantics and prefer root namescope lookup
before deferred scopes in name binding resolution.
- Add and expand unit tests for ItemTemplate and nested namescope
scenarios, including mismatched runtime DataContext behavior.
Signed-off-by: João Cruz <joaosantaremdacruz@tecnico.ulisboa.pt>
* Fix#20625: simplify compiled binding regression tests
Address review feedback to reduce test complexity in XamlIlTests.
This change removes per-test UserControl helper classes and command
scaffolding that were not required to validate the compiler behavior.
Tests now use inline XAML in each [Fact] and shared lightweight mock
types for root and item data.
The compiled binding scenarios are still covered, including root
fallback inference and named DataContext resolution with ItemTemplate
and nested namescopes, but with less boilerplate and better readability.
* Fix#20625: remove automatic DataContext fallback to root type
Address review feedback by removing the automatic fallback inference
to the root type in AvaloniaXamlIlDataContextTypeTransformer, as it
is not necessary to fix the issue and is not desired behavior.
Also removes the unit tests that validated this specific fallback.
---------
Signed-off-by: João Cruz <joaosantaremdacruz@tecnico.ulisboa.pt>
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
* move gesture events from Gestures to InputElement. Fix holding gesture interactions with context menu
* update api diff
* address review comments
* fix some test types
* rename Cancelled to Canceled
* update api diff
* Onboard onto Central Package Management
* Remove SharpDX
* Add back <clear /> to NuGet.config package source mapping
* Package mapping
* Inline props where appropriate
* Lost a space
* #19962 Add AXAML Source Information to debug Builds
* SimplifyXamlSourceInfo
* Add XamlSourceInfo for as many elements as possible
* Add tests to confirm XamlSourceInfo is set for all types
* Remove property only added for debugging during development
* update skipped test so it runs (even though it doesn't yet pass)
* Wrap XamlAstNewClrObjectNode instead of XamlAstObjectNode, run transformer late
* Remove unsupported value types from the More_Resources_Get_XamlSourceInfo_Set
* Fix Document property not being set in runtime parser
* Add a dedicated CreateSourceInfo parameter for RuntimeXamlLoaderConfiguration, instead of reusing DesignMode
* Inherit real XamlValueWithManipulationNode, move actual manipulation to a separate class
* Fix group transformers by unwrapping manipulation nodes first
* minor Resource related test change
* Update public API as agreed
* Add new failing tests for the dictionaries
* Fix randomly failing tests, that depend on the test order
* Fix assert
* Rename AvaloniaXamlResourceTransformer
* Emit XamlSourceInfo from AvaloniaXamlResourceTransformer
* Rename AvaloniaXamlIlResourceTransformer for consistency
* Cleanup comments
* Remove XamlSourceInfoValueWithManipulationNode, use standard XamlValueWithManipulationNode
* Add new RuntimeXamlLoaderDocument.Document property
* Use UriBuilder trick to support unix paths on windows
* Add private AttachedProperty for avalonia objects, instead of always using weak table
* Fix wrong UriBuilder usage and add more test assets
* Fix "Invalid URI" exception
---------
Co-authored-by: KimHenrik <kimhenrik@outlook.de>
Co-authored-by: Max Katz <maxkatz6@outlook.com>
* Introduce a universal IGlyphTypeface implementation that does not rely on any platform implementation
* Revert changes
* Fix Android
* Make the test happy
* Fix build
* Update baseline
* Fix naming
* Fix headless
* Move interfaces to dedicated files
Make GlyphTypeface.GlyphCount an integer
* Fix GlyphCount
* Make IGlyphTypeface NotClientImplementable
* Make sure we cache platform typefaces by their desired name, style, weight and stretch
* Update baseline
* Only use IGlyphTypeface
* Fix Android
* Try to clear the buffer before we encode somethimg
* Add needed test font
* Add more unit tests
* Reduce allocations
* Remove Direct2D1 test files
* More tests
* More complete table implementations
* More adjustments
* Use batch APIs
* Handle invalid timestamps
* Update baseline
* Introduce a CharacterToGlyphMap struct for faster access
* Remove AggressiveInlining
* Remove AggressiveInlining
* Make the head table optional for legacy fonts
* Remove Load method. Fix TextBlockTests
* Fix nullables
* Remove redundant folder
* Update Api baseline
* revert diff helper changes
* revert changes
* Use bare minimum font for Headless platform and introduce a test font manager that uses the Inter font for testing.
* Add missing font file for Headless platform
---------
Co-authored-by: Gillibald <stebner@avaloniaui.net>
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
* Update to xunit.v3
* Little more progress
* More fixes
* Keep VSTest supported
* Adjust Nuke
* Few fixes
* Fix for xunit 2
* Fix GetData override
* Adjust
* Use MTP for xunit 2
* Fix test
* Better fix
* --no-progress
* Few more fixes
* no progress
* Fix test
* Better fix
* TRX
* Move to Directory.Build.props
* Unify on MTP v2
* Update
* Update to stable
* 1.0.1
* 1.0.2
* Fix some warnings
* Fix more warnings
* Fix more warnings
* Update ncrunch config.
* Tidy up reflection and multi-binding APIs:
- Move `BindingBase` and `MultiBinding` into Avalonia.Base
- `BindingBase` becomes a true base class for all bindings, and contains only the `Instance` method
- Properties common between reflection and compiled bindings are moved into `StandardBindingBase`
- `Binding` is moved to Avalonia.Base and renamed to `ReflectionBinding`
- A compatibility shim for `Binding` remains in Avalonia.Markup
- Remove `IBinding` and `IBinding2`
- Remove `ITreeDataTemplate's usage of `InstancedBinding`
- Remove `NativeMenuBarPresenter`s usage of `InstancedBinding`
- Remove `InstancedBinding` as it is now unused
This required an update to the DataGrid submodule: cell data validation has been temporarily removed as this used `InstancedBinding`.
* `Instance()` => `CreateInstance()`.
The use of "Instance" as a verb is quite unusual apparently ;)
* Seal classes where appropriate.
* Seal classes where appropriate.
* Remove `StandardBindingBase`.
Simply duplicate the members in reflection and compiled binding classes.
* Delete deleted submodule directory.
* Add missing attribute.
Fixes compile error.
* Fix reference to removed class.
* Update suppressions.
* Enable nullability in UnitTests
* Enable nullability in Base.UnitTests
* Enable nullability in Markup.UnitTests
* Enable nullability in Markup.Xaml.UnitTests
* Support Design.PreviewWith for any previewing content
* ApplyDesignModeProperties should use bindings
* Add support for IDataTemplate PreviewWith
* Add new members as agreed on API review
* ApplyDesignModeProperties probably should be privateapi
* Add xml comments
* Extract Design.CreatePreviewWithControl and move it out of DesignerSupport project
* Several fixes, add tets
* Update API diff
* wip
* Add DesignModeTests, move relevant tests to this class
* Restore methods accepting control, add IStyle target overload
* Restore Design.SetPreviewWith(AvaloniaObject, Control)
* `SetPreviewWith(any, Control? control)` should accept nullable control
* Add `SetDataContext(IDataTemplate control, object? value)`
* Better Obsolete message
* Remove netstandard2.0 from almost all projects
* Fix duplicated target frameworks in unit tests
* Fix DesignerSupport tests
* Fix Designer.HostApp packaging
* Build ControlCatalog.Desktop on CI
* Fix another bad auto merge
* Fix LeakTests duplicated target frameworks
* Don't hardcode target framework in DesignerSupportTests
* Feat: Add unit test for RelativePanel.
The ScrollViewer's scrollbar does not display correctly.
* Fix: RelativePanel calculation error
The ScrollViewer's scrollbar does not display correctly.
* Fix XamlTypeExtensionNode not being handled on the x:DataType transformer
* Add testt with complex DataType
* Make vm:MainWindowViewModel+TestItem nested type generic on BindingDemo
* Implement AVLN2208 diagnostic - ItemContainerInsideTemplate
* Add AVLN2208 tests
* Enable AVLN2208 as an error in the repository globally
* Fix invalid ListBoxItem inside of DataTemplate in devtools
* Add failing style test without selector
* Fix XAML target type of style without selector
* Address review
* Throw for style without selector in ControlTheme
* Add failing test for TemplateBinding inside custom control template
* Fix TemplateBinding XAML compilation error for custom IControlTemplate
---------
Co-authored-by: Max Katz <maxkatz6@outlook.com>
* Try to infer DataContext type from #named binding nodes
* Try to infer DataContext type from $parent binding nodes
* Use new syntax in the repo (Rider still marks it as an error)
* Add tests ensuring type casing still works
* Fix $parent regression
* Make new tests StringSyntax compatible
* feat: Allow use of (Classes.`classname`) syntax on Style and ControlTheme Setter elements
* feat: Add Selector validation
* test: Check binding Classes in Setter
* Implement AvaloniaObject on ResourceProvider
* ColorPaletteResources and ColorPaletteResourcesCollection should inherit ResourceProvider
* Use ResourceProvider in tests too
* Adjust API diff
---------
Co-authored-by: Steven Kirk <grokys@users.noreply.github.com>
The typo causes the resources `implicitSharedInstance1` and `implicitSharedInstance2` are both always `AvaloniaProperty.UnsetValue`, where `Assert.Same` passes.
* Update BindingBase.Instance signature.
- Swap `target` and `targetProperty` order to make it consistent with other similar methods
- Make `targetProperty` nullable as it will need to be null for `MultiBinding`
* IBinding2.Instance needs to accept a null target property.
It will need to be null for `MultiBinding`.
* Attach needs to accept a null target property.
It will need to be null for `MultiBinding`.
* Initial implementation of MultiBindingExpression.
* Fix failing template binding test.
Only publish unset value if we've already published a value.
* Enabled nullability annotations.
* Added passing test for #16084.
* Remove obsolete API usages.
* Bind to Tag not Text.
Prevents test passing when it shouldn't. See https://github.com/AvaloniaUI/Avalonia/pull/16219#discussion_r1665466968
* Handle DoNothing in MultiBindingExpression.