@ -5,6 +5,7 @@ using Avalonia.Controls;
using Avalonia.Data.Converters ;
using Avalonia.Data.Core ;
using Avalonia.Data.Core.ExpressionNodes ;
using Avalonia.Diagnostics ;
using Avalonia.Markup.Parsers ;
using Avalonia.Utilities ;
@ -59,16 +60,44 @@ namespace Avalonia.Data
/// </summary>
public Func < string? , string , Type > ? TypeResolver { get ; set ; }
[Obsolete(ObsoletionMessages.MayBeRemovedInAvalonia12)]
public override InstancedBinding ? Initiate (
AvaloniaObject target ,
AvaloniaProperty ? targetProperty ,
object? anchor = null ,
bool enableDataValidation = false )
{
var expression = InstanceCore ( targetProperty , target , enableDataValidation ) ;
return new InstancedBinding ( target , expression , Mode , Priority ) ;
}
private protected override IBindingExpression Instance ( AvaloniaProperty targetProperty , AvaloniaObject target )
{
var enableDataValidation = targetProperty . GetMetadata ( target . GetType ( ) ) . EnableDataValidation ? ? false ;
return InstanceCore ( targetProperty , target , enableDataValidation ) ;
}
/// <summary>
/// Hack for TreeDataTemplate to create a binding expression for an item.
/// </summary>
/// <param name="source">The item.</param>
/// <remarks>
/// 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.
/// </remarks>
// TODO12: Refactor
internal BindingExpression CreateObservableForTreeDataTemplate ( object source )
{
if ( ! string . IsNullOrEmpty ( ElementName ) )
throw new NotSupportedException ( "ElementName bindings are not supported in this context." ) ;
if ( RelativeSource is not null & & RelativeSource . Mode ! = RelativeSourceMode . DataContext )
throw new NotSupportedException ( "RelativeSource bindings are not supported in this context." ) ;
if ( Source ! = AvaloniaProperty . UnsetValue )
throw new NotSupportedException ( "Source bindings are not supported in this context." ) ;
var nodes = new List < ExpressionNode > ( ) ;
var isRooted = false ;
// Build the expression nodes from the binding path.
if ( ! string . IsNullOrEmpty ( Path ) )
{
var reader = new CharacterReader ( Path . AsSpan ( ) ) ;
@ -81,55 +110,27 @@ namespace Avalonia.Data
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 source node. The type of source node will
// depend on the ElementName and RelativeSource properties of the binding and if
// neither of those are set will default to a data context node.
if ( Source = = AvaloniaProperty . UnsetValue & & ! isRooted & & CreateSourceNode ( targetProperty ) is { } sourceNode )
nodes . Insert ( 0 , sourceNode ) ;
// 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 ;
if ( isRooted )
throw new NotSupportedException ( "Rooted binding paths are not supported in this context." ) ;
// Create the binding expression and wrap it in an InstancedBinding.
var expression = new BindingExpression (
return new BindingExpression (
source ,
nodes ,
FallbackValue ,
converter : Converter ,
converterCulture : ConverterCulture ,
converterParameter : ConverterParameter ,
enableDataValidation : enableDataValidation ,
mode : ResolveBindingMode ( target , targetProperty ) ,
stringFormat : StringFormat ,
targetNullValue : TargetNullValue ,
targetTypeConverter : TargetTypeConverter . GetReflectionConverter ( ) ) ;
return new InstancedBinding ( target , expression , Mode , Priority ) ;
targetNullValue : TargetNullValue ) ;
}
/// <summary>
/// Hack for TreeDataTemplate to create a binding expression for an item.
/// </summary>
/// <param name="source">The item.</param>
/// <remarks>
/// 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.
/// </remarks>
internal BindingExpression CreateObservableForTreeDataTemplate ( object source )
private UntypedBindingExpressionBase InstanceCore (
AvaloniaProperty ? targetProperty ,
AvaloniaObject target ,
bool enableDataValidation )
{
if ( ! string . IsNullOrEmpty ( ElementName ) )
throw new NotSupportedException ( "ElementName bindings are not supported in this context." ) ;
if ( RelativeSource is not null & & RelativeSource . Mode ! = RelativeSourceMode . DataContext )
throw new NotSupportedException ( "RelativeSource bindings are not supported in this context." ) ;
if ( Source ! = AvaloniaProperty . UnsetValue )
throw new NotSupportedException ( "Source bindings are not supported in this context." ) ;
var nodes = new List < ExpressionNode > ( ) ;
var isRooted = false ;
// Build the expression nodes from the binding path.
if ( ! string . IsNullOrEmpty ( Path ) )
{
var reader = new CharacterReader ( Path . AsSpan ( ) ) ;
@ -142,16 +143,31 @@ namespace Avalonia.Data
out isRooted ) ;
}
if ( isRooted )
throw new NotSupportedException ( "Rooted binding paths are not supported in this context." ) ;
// 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 source node. The type of source node will
// depend on the ElementName and RelativeSource properties of the binding and if
// neither of those are set will default to a data context node.
if ( Source = = AvaloniaProperty . UnsetValue & & ! isRooted & & CreateSourceNode ( targetProperty ) is { } sourceNode )
nodes . Insert ( 0 , sourceNode ) ;
// 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 , DefaultAnchor ? . Target ) :
Source ! = AvaloniaProperty . UnsetValue ? Source : target ;
return new BindingExpression (
source ,
nodes ,
FallbackValue ,
converter : Converter ,
converterCulture : ConverterCulture ,
converterParameter : ConverterParameter ,
targetNullValue : TargetNullValue ) ;
enableDataValidation : enableDataValidation ,
mode : ResolveBindingMode ( target , targetProperty ) ,
stringFormat : StringFormat ,
targetNullValue : TargetNullValue ,
targetTypeConverter : TargetTypeConverter . GetReflectionConverter ( ) ) ;
}
private INameScope ? GetNameScope ( )