diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml
index cab4bf5580..9dcc016cbd 100644
--- a/api/Avalonia.nupkg.xml
+++ b/api/Avalonia.nupkg.xml
@@ -1,4 +1,4 @@
-
+
@@ -7,12 +7,36 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0001
+ T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPath
+ baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll
+ current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll
+
+
+ CP0001
+ T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPathBuilder
+ baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll
+ current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll
+
CP0001
T:Avalonia.Media.IGlyphTypeface
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0001
+ T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPath
+ baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll
+ current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll
+
+
+ CP0001
+ T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPathBuilder
+ baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll
+ current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll
+
CP0002
M:Avalonia.Media.FontManager.TryGetGlyphTypeface(Avalonia.Media.Typeface,Avalonia.Media.IGlyphTypeface@)
@@ -289,6 +313,24 @@
baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll
current/Avalonia/lib/net10.0/Avalonia.Controls.dll
+
+ CP0002
+ M:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension.#ctor(Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPath)
+ baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll
+ current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll
+
+
+ CP0002
+ M:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension.get_Path
+ baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll
+ current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll
+
+
+ CP0002
+ M:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension.ProvideValue(System.IServiceProvider)
+ baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll
+ current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll
+
CP0002
M:Avalonia.Media.GlyphMetrics.get_Height
@@ -655,6 +697,24 @@
baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll
current/Avalonia/lib/net8.0/Avalonia.Controls.dll
+
+ CP0002
+ M:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension.#ctor(Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPath)
+ baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll
+ current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll
+
+
+ CP0002
+ M:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension.get_Path
+ baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll
+ current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll
+
+
+ CP0002
+ M:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension.ProvideValue(System.IServiceProvider)
+ baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll
+ current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll
+
CP0002
M:Avalonia.Media.GlyphMetrics.get_Height
@@ -1309,4 +1369,4 @@
baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll
current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Base/Data/CompiledBinding.cs b/src/Avalonia.Base/Data/CompiledBinding.cs
new file mode 100644
index 0000000000..764b04957e
--- /dev/null
+++ b/src/Avalonia.Base/Data/CompiledBinding.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using Avalonia.Controls;
+using Avalonia.Data.Converters;
+using Avalonia.Data.Core;
+using Avalonia.Data.Core.ExpressionNodes;
+using Avalonia.Data.Core.Parsers;
+
+namespace Avalonia.Data;
+
+///
+/// A binding which does not use reflection to access members.
+///
+public class CompiledBinding : BindingBase
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CompiledBinding() { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The binding path.
+ public CompiledBinding(CompiledBindingPath path) => Path = path;
+
+ ///
+ /// Gets or sets the amount of time, in milliseconds, to wait before updating the binding
+ /// source after the value on the target changes.
+ ///
+ ///
+ /// There is no delay when the source is updated via
+ /// or . Nor is there a delay when
+ /// is active and a new source object is provided.
+ ///
+ public int Delay { get; set; }
+
+ ///
+ /// Gets or sets the to use.
+ ///
+ public IValueConverter? Converter { get; set; }
+
+ ///
+ /// Gets or sets the culture in which to evaluate the converter.
+ ///
+ /// The default value is null.
+ ///
+ /// If this property is not set then will be used.
+ ///
+ [TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
+ public CultureInfo? ConverterCulture { get; set; }
+
+ ///
+ /// Gets or sets a parameter to pass to .
+ ///
+ public object? ConverterParameter { get; set; }
+
+ ///
+ /// Gets or sets the value to use when the binding is unable to produce a value.
+ ///
+ public object? FallbackValue { get; set; } = AvaloniaProperty.UnsetValue;
+
+ ///
+ /// Gets or sets the binding mode.
+ ///
+ public BindingMode Mode { get; set; }
+
+ ///
+ /// Gets or sets the binding path.
+ ///
+ public CompiledBindingPath? Path { get; set; }
+
+ ///
+ /// Gets or sets the binding priority.
+ ///
+ public BindingPriority Priority { get; set; }
+
+ ///
+ /// Gets or sets the source for the binding.
+ ///
+ public object? Source { get; set; } = AvaloniaProperty.UnsetValue;
+
+ ///
+ /// Gets or sets the string format.
+ ///
+ public string? StringFormat { get; set; }
+
+ ///
+ /// Gets or sets the value to use when the binding result is null.
+ ///
+ public object? TargetNullValue { get; set; } = AvaloniaProperty.UnsetValue;
+
+ ///
+ /// Gets or sets a value that determines the timing of binding source updates for
+ /// and bindings.
+ ///
+ public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
+
+ internal WeakReference? DefaultAnchor { get; set; }
+ internal WeakReference? NameScope { get; set; }
+
+ internal override BindingExpressionBase CreateInstance(
+ AvaloniaObject target,
+ AvaloniaProperty? targetProperty,
+ object? anchor)
+ {
+ var enableDataValidation = targetProperty?.GetMetadata(target).EnableDataValidation ?? false;
+ var nodes = new List();
+ var isRooted = false;
+
+ Path?.BuildExpression(nodes, out isRooted);
+
+ // If the binding isn't rooted (i.e. doesn't have a Source or start with $parent, $self,
+ // #elementName etc.) then we need to add a data context source node.
+ if (Source == AvaloniaProperty.UnsetValue && !isRooted)
+ nodes.Insert(0, ExpressionNodeFactory.CreateDataContext(targetProperty));
+
+ // If the first node is an ISourceNode then allow it to select the source; otherwise
+ // use the binding source if specified, falling back to the target.
+ var source = nodes?.Count > 0 && nodes[0] is SourceNode sn
+ ? sn.SelectSource(Source, target, anchor ?? DefaultAnchor?.Target)
+ : Source != AvaloniaProperty.UnsetValue ? Source : target;
+
+ var (mode, trigger) = ResolveDefaultsFromMetadata(target, targetProperty);
+
+ return new BindingExpression(
+ source,
+ nodes,
+ FallbackValue,
+ delay: TimeSpan.FromMilliseconds(Delay),
+ converter: Converter,
+ converterCulture: ConverterCulture,
+ converterParameter: ConverterParameter,
+ enableDataValidation: enableDataValidation,
+ mode: mode,
+ priority: Priority,
+ stringFormat: StringFormat,
+ targetNullValue: TargetNullValue,
+ targetProperty: targetProperty,
+ targetTypeConverter: TargetTypeConverter.GetDefaultConverter(),
+ updateSourceTrigger: trigger);
+ }
+
+ private (BindingMode, UpdateSourceTrigger) ResolveDefaultsFromMetadata(
+ AvaloniaObject target,
+ AvaloniaProperty? targetProperty)
+ {
+ var mode = Mode;
+ var trigger = UpdateSourceTrigger == UpdateSourceTrigger.Default ?
+ UpdateSourceTrigger.PropertyChanged : UpdateSourceTrigger;
+
+ if (mode == BindingMode.Default)
+ {
+ if (targetProperty?.GetMetadata(target) is { } metadata)
+ mode = metadata.DefaultBindingMode;
+ else
+ mode = BindingMode.OneWay;
+ }
+
+ return (mode, trigger);
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs b/src/Avalonia.Base/Data/CompiledBindingPath.cs
similarity index 92%
rename from src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
rename to src/Avalonia.Base/Data/CompiledBindingPath.cs
index 20e9c6e886..aea320702e 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
+++ b/src/Avalonia.Base/Data/CompiledBindingPath.cs
@@ -4,10 +4,10 @@ using System.Reflection;
using Avalonia.Controls;
using Avalonia.Data.Core;
using Avalonia.Data.Core.ExpressionNodes;
+using Avalonia.Data.Core.Parsers;
using Avalonia.Data.Core.Plugins;
-using Avalonia.Markup.Parsers;
-namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
+namespace Avalonia.Data
{
public class CompiledBindingPath
{
@@ -96,21 +96,17 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
///
public override string ToString()
- => string.Concat((IEnumerable) _elements);
+ => string.Concat((IEnumerable)_elements);
}
public class CompiledBindingPathBuilder
{
- private readonly int _apiVersion;
private readonly List _elements = new();
public CompiledBindingPathBuilder()
{
}
- // TODO12: Remove this constructor. apiVersion is only needed for compatibility with
- // versions of Avalonia which used $self.Property() for building TemplatedParent bindings.
- public CompiledBindingPathBuilder(int apiVersion) => _apiVersion = apiVersion;
public CompiledBindingPathBuilder Not()
{
@@ -120,22 +116,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public CompiledBindingPathBuilder Property(IPropertyInfo info, Func, IPropertyInfo, IPropertyAccessor> accessorFactory)
{
- // Older versions of Avalonia used $self.Property() for building TemplatedParent bindings.
- // Try to detect this and upgrade to using a TemplatedParentPathElement so that logging works
- // correctly.
- if (_apiVersion == 0 &&
- info.Name == "TemplatedParent" &&
- _elements.Count >= 1 &&
- _elements[_elements.Count - 1] is SelfPathElement)
- {
- _elements.Add(new TemplatedParentPathElement());
- }
- else
- {
- return Property(info, accessorFactory, acceptsNull: false);
- }
-
- return this;
+ return Property(info, accessorFactory, acceptsNull: false);
}
public CompiledBindingPathBuilder Property(
@@ -285,7 +266,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public MethodInfo Method { get; }
public Type DelegateType { get; }
-
+
public bool AcceptsNull { get; }
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
index 88d708335e..a11dea95e4 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
@@ -142,7 +142,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
IXamlType? itemsCollectionType = null;
if (context.GetAvaloniaTypes().BindingBase.IsAssignableFrom(parentItemsValue.Type.GetClrType()))
{
- if (parentItemsValue.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension)
+ if (parentItemsValue.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBinding)
&& parentItemsValue is XamlMarkupExtensionNode ext && ext.Value is XamlAstConstructableObjectNode parentItemsBinding)
{
var parentItemsDataContext = context.ParentNodes().SkipWhile(n => n != parentObject).OfType().FirstOrDefault();
@@ -176,7 +176,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
private static AvaloniaXamlIlDataContextTypeMetadataNode ParseDataContext(AstTransformationContext context, XamlAstConstructableObjectNode on, XamlAstConstructableObjectNode obj)
{
var bindingType = context.GetAvaloniaTypes().BindingBase;
- if (!bindingType.IsAssignableFrom(obj.Type.GetClrType()) && !obj.Type.GetClrType().Equals(context.GetAvaloniaTypes().ReflectionBindingExtension))
+ if (!bindingType.IsAssignableFrom(obj.Type.GetClrType()) &&
+ !(obj.Type.GetClrType().Equals(context.GetAvaloniaTypes().ReflectionBindingExtension) ||
+ obj.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension)))
{
return new AvaloniaXamlIlDataContextTypeMetadataNode(on, obj.Type.GetClrType());
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
index 2b7a835fd8..8659eb1299 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
@@ -51,6 +51,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType ClrPropertyInfo { get; }
public IXamlType IPropertyAccessor { get; }
public IXamlType PropertyInfoAccessorFactory { get; }
+ public IXamlType CompiledBinding { get; }
public IXamlType CompiledBindingPathBuilder { get; }
public IXamlType CompiledBindingPath { get; }
public IXamlType CompiledBindingExtension { get; }
@@ -242,8 +243,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
ClrPropertyInfo = cfg.TypeSystem.GetType("Avalonia.Data.Core.ClrPropertyInfo");
IPropertyAccessor = cfg.TypeSystem.GetType("Avalonia.Data.Core.Plugins.IPropertyAccessor");
PropertyInfoAccessorFactory = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.PropertyInfoAccessorFactory");
- CompiledBindingPathBuilder = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPathBuilder");
- CompiledBindingPath = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPath");
+ CompiledBinding = cfg.TypeSystem.GetType("Avalonia.Data.CompiledBinding");
+ CompiledBindingPathBuilder = cfg.TypeSystem.GetType("Avalonia.Data.CompiledBindingPathBuilder");
+ CompiledBindingPath = cfg.TypeSystem.GetType("Avalonia.Data.CompiledBindingPath");
CompiledBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension");
ResolveByNameExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ResolveByNameExtension");
DataTemplate = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Templates.DataTemplate");
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
index 68dd595ea2..96af9a3a18 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
@@ -1013,11 +1013,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
var intType = context.Configuration.TypeSystem.GetType("System.Int32");
var types = context.GetAvaloniaTypes();
- // We're calling the CompiledBindingPathBuilder(int apiVersion) with an apiVersion
- // of 1 to indicate that we don't want TemplatedParent compatibility hacks enabled.
- codeGen
- .Ldc_I4(1)
- .Newobj(types.CompiledBindingPathBuilder.GetConstructor(new() { intType }));
+ codeGen.Newobj(types.CompiledBindingPathBuilder.GetConstructor());
foreach (var transform in _transformElements)
{
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index 7a52f94a9f..e6186bbea6 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -20,7 +20,6 @@
-
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs
index 0913903ff3..22606bdd93 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs
@@ -7,15 +7,13 @@ using Avalonia.Data.Converters;
using Avalonia.Data.Core;
using Avalonia.Data.Core.ExpressionNodes;
using Avalonia.Data.Core.Parsers;
-using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
namespace Avalonia.Markup.Xaml.MarkupExtensions
{
- public sealed class CompiledBindingExtension : BindingBase
+ public sealed class CompiledBindingExtension : CompiledBinding
{
public CompiledBindingExtension()
{
- Path = new CompiledBindingPath();
}
public CompiledBindingExtension(CompiledBindingPath path)
@@ -23,9 +21,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
Path = path;
}
- public CompiledBindingExtension ProvideValue(IServiceProvider provider)
+ public CompiledBinding ProvideValue(IServiceProvider? provider)
{
- return new CompiledBindingExtension
+ return new CompiledBinding
{
Path = Path,
Delay = Delay,
@@ -38,184 +36,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
Priority = Priority,
StringFormat = StringFormat,
Source = Source,
- DefaultAnchor = new WeakReference(provider.GetDefaultAnchor()),
+ DefaultAnchor = new WeakReference(provider?.GetDefaultAnchor()),
UpdateSourceTrigger = UpdateSourceTrigger,
};
}
- ///
- /// Gets or sets the amount of time, in milliseconds, to wait before updating the binding
- /// source after the value on the target changes.
- ///
- ///
- /// There is no delay when the source is updated via
- /// or . Nor is there a delay when
- /// is active and a new source object is provided.
- ///
- public int Delay { get; set; }
-
- ///
- /// Gets or sets the to use.
- ///
- public IValueConverter? Converter { get; set; }
-
- ///
- /// Gets or sets the culture in which to evaluate the converter.
- ///
- /// The default value is null.
- ///
- /// If this property is not set then will be used.
- ///
- [TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
- public CultureInfo? ConverterCulture { get; set; }
-
- ///
- /// Gets or sets a parameter to pass to .
- ///
- public object? ConverterParameter { get; set; }
-
public Type? DataType { get; set; }
-
- ///
- /// Gets or sets the value to use when the binding is unable to produce a value.
- ///
- public object? FallbackValue { get; set; } = AvaloniaProperty.UnsetValue;
-
- ///
- /// Gets or sets the binding mode.
- ///
- public BindingMode Mode { get; set; }
-
- [ConstructorArgument("path")]
- public CompiledBindingPath Path { get; set; }
-
- ///
- /// Gets or sets the binding priority.
- ///
- public BindingPriority Priority { get; set; }
-
- ///
- /// Gets or sets the source for the binding.
- ///
- public object? Source { get; set; } = AvaloniaProperty.UnsetValue;
-
- ///
- /// Gets or sets the string format.
- ///
- public string? StringFormat { get; set; }
-
- ///
- /// Gets or sets the value to use when the binding result is null.
- ///
- public object? TargetNullValue { get; set; } = AvaloniaProperty.UnsetValue;
-
- ///
- /// Gets or sets a value that determines the timing of binding source updates for
- /// and bindings.
- ///
- public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
-
- internal WeakReference? DefaultAnchor { get; set; }
-
- internal override BindingExpressionBase CreateInstance(
- AvaloniaObject target,
- AvaloniaProperty? targetProperty,
- object? anchor)
- {
- var enableDataValidation = targetProperty?.GetMetadata(target).EnableDataValidation ?? false;
- return InstanceCore(target, targetProperty, anchor, enableDataValidation);
- }
-
- ///
- /// Hack for TreeDataTemplate to create a binding expression for an item.
- ///
- /// The item.
- ///
- /// Ideally we'd do this in a more generic way but didn't have time to refactor
- /// ITreeDataTemplate in time for 11.0. We should revisit this in 12.0.
- ///
- // TODO12: Refactor
- internal BindingExpression CreateObservableForTreeDataTemplate(object source)
- {
- if (Source != AvaloniaProperty.UnsetValue)
- throw new NotSupportedException("Source bindings are not supported in this context.");
-
- var nodes = new List();
-
- Path.BuildExpression(nodes, out var isRooted);
-
- if (isRooted)
- throw new NotSupportedException("Rooted binding paths are not supported in this context.");
-
- return new BindingExpression(
- source,
- nodes,
- FallbackValue,
- delay: TimeSpan.FromMilliseconds(Delay),
- converter: Converter,
- converterParameter: ConverterParameter,
- targetNullValue: TargetNullValue);
- }
-
- private BindingExpression InstanceCore(
- AvaloniaObject target,
- AvaloniaProperty? targetProperty,
- object? anchor,
- bool enableDataValidation)
- {
- var nodes = new List();
-
- // Build the expression nodes from the binding path.
- Path.BuildExpression(nodes, out var isRooted);
-
- // If the binding isn't rooted (i.e. doesn't have a Source or start with $parent, $self,
- // #elementName etc.) then we need to add a data context source node.
- if (Source == AvaloniaProperty.UnsetValue && !isRooted)
- nodes.Insert(0, ExpressionNodeFactory.CreateDataContext(targetProperty));
-
- // If the first node is an ISourceNode then allow it to select the source; otherwise
- // use the binding source if specified, falling back to the target.
- var source = nodes.Count > 0 && nodes[0] is SourceNode sn
- ? sn.SelectSource(Source, target, anchor ?? DefaultAnchor?.Target)
- : Source != AvaloniaProperty.UnsetValue ? Source : target;
-
- var (mode, trigger) = ResolveDefaultsFromMetadata(target, targetProperty);
-
- return new BindingExpression(
- source,
- nodes,
- FallbackValue,
- delay: TimeSpan.FromMilliseconds(Delay),
- converter: Converter,
- converterCulture: ConverterCulture,
- converterParameter: ConverterParameter,
- enableDataValidation: enableDataValidation,
- mode: mode,
- priority: Priority,
- stringFormat: StringFormat,
- targetNullValue: TargetNullValue,
- targetProperty: targetProperty,
- targetTypeConverter: TargetTypeConverter.GetDefaultConverter(),
- updateSourceTrigger: trigger);
- }
-
- private (BindingMode, UpdateSourceTrigger) ResolveDefaultsFromMetadata(
- AvaloniaObject target,
- AvaloniaProperty? targetProperty)
- {
- var mode = Mode;
- var trigger = UpdateSourceTrigger == UpdateSourceTrigger.Default ?
- UpdateSourceTrigger.PropertyChanged : UpdateSourceTrigger;
-
- if (mode == BindingMode.Default)
- {
- if (targetProperty?.GetMetadata(target) is { } metadata)
- mode = metadata.DefaultBindingMode;
- else
- mode = BindingMode.OneWay;
- }
-
- return (mode, trigger);
- }
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlTypes.cs b/src/Markup/Avalonia.Markup.Xaml/XamlTypes.cs
index da4d7374d4..513b18c7a7 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlTypes.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlTypes.cs
@@ -33,7 +33,7 @@ namespace Avalonia.Markup.Xaml
Type Resolve (string qualifiedTypeName);
}
-
+ // TODO12: Move to Avalonia.Base
[AttributeUsage(AttributeTargets.Property)]
public sealed class ConstructorArgumentAttribute : Attribute
{
diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/CompiledBindingPathFromExpressionBuilder.cs b/tests/Avalonia.Base.UnitTests/Data/Core/CompiledBindingPathFromExpressionBuilder.cs
index 0662fd94b0..d3623fde80 100644
--- a/tests/Avalonia.Base.UnitTests/Data/Core/CompiledBindingPathFromExpressionBuilder.cs
+++ b/tests/Avalonia.Base.UnitTests/Data/Core/CompiledBindingPathFromExpressionBuilder.cs
@@ -1,10 +1,9 @@
using System;
-using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
+using Avalonia.Data;
using Avalonia.Data.Core;
using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
index 051129e12c..727b3ddc45 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
@@ -639,8 +639,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
target.ApplyTemplate();
// Assert DataGridLikeColumn.Binding data type.
- var compiledPath = ((CompiledBindingExtension)column.Binding!).Path;
- var node = Assert.IsType(Assert.Single(compiledPath.Elements));
+ var compiledPath = ((CompiledBinding)column.Binding!).Path;
+ var node = Assert.IsType(Assert.Single(compiledPath!.Elements));
Assert.Equal(typeof(string), node.Property.PropertyType);
Assert.Equal(nameof(TestData.StringProperty), node.Property.Name);
@@ -682,8 +682,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
target.ApplyTemplate();
// Assert DataGridLikeColumn.Binding data type.
- var compiledPath = ((CompiledBindingExtension)column.Binding!).Path;
- var node = Assert.IsType(Assert.Single(compiledPath.Elements));
+ var compiledPath = ((CompiledBinding)column.Binding!).Path;
+ var node = Assert.IsType(Assert.Single(compiledPath!.Elements));
Assert.Equal(typeof(int), node.Property.PropertyType);
// Assert DataGridLikeColumn.Template data type by evaluating the template.
@@ -727,8 +727,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
target.ApplyTemplate();
// Assert DataGridLikeColumn.Binding data type.
- var compiledPath = ((CompiledBindingExtension)column.Binding!).Path;
- var node = Assert.IsType(Assert.Single(compiledPath.Elements));
+ var compiledPath = ((CompiledBinding)column.Binding!).Path;
+ var node = Assert.IsType(Assert.Single(compiledPath!.Elements));
Assert.Equal(typeof(int), node.Property.PropertyType);
// Assert DataGridLikeColumn.Template data type by evaluating the template.
@@ -2060,9 +2060,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
x:DataType='local:TestDataContext'
X='{CompiledBinding StringProperty}' />";
var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(xaml);
- var compiledPath = ((CompiledBindingExtension)control.X!).Path;
+ var compiledPath = ((CompiledBinding)control.X!).Path;
- var node = Assert.IsType(Assert.Single(compiledPath.Elements));
+ var node = Assert.IsType(Assert.Single(compiledPath!.Elements));
Assert.Equal(typeof(string), node.Property.PropertyType);
}
}
@@ -2078,9 +2078,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
X='{CompiledBinding StringProperty, DataType=local:TestDataContext}' />";
var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(xaml);
- var compiledPath = ((CompiledBindingExtension)control.X!).Path;
+ var compiledPath = ((CompiledBinding)control.X!).Path;
- var node = Assert.IsType(Assert.Single(compiledPath.Elements));
+ var node = Assert.IsType(Assert.Single(compiledPath!.Elements));
Assert.Equal(typeof(string), node.Property.PropertyType);
}
}
@@ -2097,9 +2097,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
X='{CompiledBinding StringProperty, DataType=local:TestDataContext}' />";
var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(new RuntimeXamlLoaderDocument(xaml),
new RuntimeXamlLoaderConfiguration { UseCompiledBindingsByDefault = true });
- var compiledPath = ((CompiledBindingExtension)control.X!).Path;
+ var compiledPath = ((CompiledBinding)control.X!).Path;
- var node = Assert.IsType(Assert.Single(compiledPath.Elements));
+ var node = Assert.IsType(Assert.Single(compiledPath!.Elements));
Assert.Equal(typeof(string), node.Property.PropertyType);
}
}