diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index e54dc13a83..05753738cb 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -7,6 +7,7 @@ using Avalonia.Data.Core;
using Avalonia.Diagnostics;
using Avalonia.Logging;
using Avalonia.PropertyStore;
+using Avalonia.Reactive;
using Avalonia.Threading;
namespace Avalonia
@@ -407,6 +408,24 @@ namespace Avalonia
}
}
+ ///
+ /// Binds a to an .
+ ///
+ /// The property.
+ /// The binding.
+ ///
+ /// A disposable which can be used to terminate the binding.
+ ///
+ public IBindingExpression Bind(AvaloniaProperty property, IBinding binding)
+ {
+ if (binding is not IBinding2 b)
+ throw new NotSupportedException($"Unsupported IBinding implementation '{binding}'.");
+ if (b.Instance(this, property) is not UntypedBindingExpressionBase expression)
+ throw new NotSupportedException("Binding returned unsupported IBindingExpression.");
+
+ return property.RouteBind(this, expression);
+ }
+
///
/// Binds a to an observable.
///
@@ -611,10 +630,9 @@ namespace Avalonia
internal IDisposable Bind(
AvaloniaProperty property,
- UntypedBindingExpressionBase expression,
- BindingPriority priority)
+ UntypedBindingExpressionBase expression)
{
- return property.RouteBind(this, expression, priority);
+ return property.RouteBind(this, expression);
}
///
diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs
index 7becd1c990..0e7ccbf4f3 100644
--- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs
+++ b/src/Avalonia.Base/AvaloniaObjectExtensions.cs
@@ -1,5 +1,6 @@
using System;
using Avalonia.Data;
+using Avalonia.Data.Core;
using Avalonia.Reactive;
namespace Avalonia
@@ -233,9 +234,10 @@ namespace Avalonia
/// An optional anchor from which to locate required context. When binding to objects that
/// are not in the logical tree, certain types of binding need an anchor into the tree in
/// order to locate named controls or resources. The parameter
- /// can be used to provice this context.
+ /// can be used to provide this context.
///
/// An which can be used to cancel the binding.
+ [Obsolete]
public static IDisposable Bind(
this AvaloniaObject target,
AvaloniaProperty property,
@@ -367,7 +369,7 @@ namespace Avalonia
return observable.Subscribe(new ClassHandlerObserver(action));
}
- private class BindingAdaptor : IBinding
+ private class BindingAdaptor : IBinding2
{
private readonly IObservable
/// The object instance.
/// The binding source.
- /// The priority.
- internal abstract IDisposable RouteBind(
+ internal abstract IBindingExpression RouteBind(
AvaloniaObject o,
- UntypedBindingExpressionBase source,
- BindingPriority priority);
+ UntypedBindingExpressionBase source);
///
/// Overrides the metadata for the property on the specified type.
diff --git a/src/Avalonia.Base/Data/BindingOperations.cs b/src/Avalonia.Base/Data/BindingOperations.cs
index c3463e4c51..a2194fae1d 100644
--- a/src/Avalonia.Base/Data/BindingOperations.cs
+++ b/src/Avalonia.Base/Data/BindingOperations.cs
@@ -1,5 +1,5 @@
using System;
-using Avalonia.Data.Core;
+using Avalonia.Diagnostics;
using Avalonia.Reactive;
namespace Avalonia.Data
@@ -15,6 +15,7 @@ namespace Avalonia.Data
/// The property to bind.
/// The instanced binding.
/// An which can be used to cancel the binding.
+ [Obsolete(ObsoletionMessages.MayBeRemovedInAvalonia12)]
public static IDisposable Apply(
AvaloniaObject target,
AvaloniaProperty property,
@@ -26,7 +27,7 @@ namespace Avalonia.Data
if (binding.Expression is { } expression)
{
- return target.Bind(property, expression, binding.Priority);
+ return target.Bind(property, expression);
}
var mode = binding.Mode;
diff --git a/src/Avalonia.Base/Data/Core/IBinding2.cs b/src/Avalonia.Base/Data/Core/IBinding2.cs
new file mode 100644
index 0000000000..23ff3808ee
--- /dev/null
+++ b/src/Avalonia.Base/Data/Core/IBinding2.cs
@@ -0,0 +1,6 @@
+namespace Avalonia.Data.Core;
+
+internal interface IBinding2 : IBinding
+{
+ IBindingExpression Instance(AvaloniaObject target, AvaloniaProperty targetProperty);
+}
diff --git a/src/Avalonia.Base/Data/Core/IndexerBindingExpression.cs b/src/Avalonia.Base/Data/Core/IndexerBindingExpression.cs
new file mode 100644
index 0000000000..b126c91027
--- /dev/null
+++ b/src/Avalonia.Base/Data/Core/IndexerBindingExpression.cs
@@ -0,0 +1,67 @@
+using System;
+
+namespace Avalonia.Data.Core;
+
+internal class IndexerBindingExpression : UntypedBindingExpressionBase
+{
+ private readonly AvaloniaObject _source;
+ private readonly AvaloniaProperty _sourceProperty;
+ private readonly AvaloniaObject _target;
+ private readonly AvaloniaProperty? _targetProperty;
+ private readonly BindingMode _mode;
+
+ public IndexerBindingExpression(
+ AvaloniaObject source,
+ AvaloniaProperty sourceProperty,
+ AvaloniaObject target,
+ AvaloniaProperty? targetProperty,
+ BindingMode mode)
+ {
+ _source = source;
+ _sourceProperty = sourceProperty;
+ _target = target;
+ _targetProperty = targetProperty;
+ _mode = mode;
+ }
+
+ public override string Description => $"IndexerBinding {_sourceProperty})";
+
+ internal override bool WriteValueToSource(object? value)
+ {
+ _source.SetValue(_sourceProperty, value);
+ return true;
+ }
+
+ protected override void StartCore()
+ {
+ if (_mode is BindingMode.TwoWay or BindingMode.OneWayToSource && _targetProperty is not null)
+ _target.PropertyChanged += OnTargetPropertyChanged;
+
+ if (_mode is not BindingMode.OneWayToSource)
+ {
+ _source.PropertyChanged += OnSourcePropertyChanged;
+ PublishValue(_source.GetValue(_sourceProperty));
+ }
+
+ if (_mode is BindingMode.OneTime)
+ Stop();
+ }
+
+ protected override void StopCore()
+ {
+ _source.PropertyChanged -= OnSourcePropertyChanged;
+ _target.PropertyChanged -= OnTargetPropertyChanged;
+ }
+
+ private void OnSourcePropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
+ {
+ if (e.Property == _sourceProperty)
+ PublishValue(_source.GetValue(_sourceProperty));
+ }
+
+ private void OnTargetPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
+ {
+ if (e.Property == _targetProperty)
+ WriteValueToSource(e.NewValue);
+ }
+}
diff --git a/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs b/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs
index 07ccd32c06..a0ea7b471c 100644
--- a/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs
+++ b/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs
@@ -15,7 +15,8 @@ namespace Avalonia.Data.Core;
/// Base class for binding expressions which produce untyped values.
///
[PrivateApi]
-public abstract class UntypedBindingExpressionBase : BindingExpressionBase,
+public abstract class UntypedBindingExpressionBase : BindingExpressionBase,
+ IBindingExpression,
IDisposable,
IDescription,
IValueEntry
diff --git a/src/Avalonia.Base/Data/Core/UntypedObservableBindingExpression.cs b/src/Avalonia.Base/Data/Core/UntypedObservableBindingExpression.cs
new file mode 100644
index 0000000000..5cfac3d443
--- /dev/null
+++ b/src/Avalonia.Base/Data/Core/UntypedObservableBindingExpression.cs
@@ -0,0 +1,32 @@
+using System;
+
+namespace Avalonia.Data.Core;
+
+internal class UntypedObservableBindingExpression : UntypedBindingExpressionBase, IObserver