Browse Source

Move `IBinding` to `BindingBase` and tidy up some binding APIs (#19589)

* 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.
Steven Kirk 5 days ago
parent
commit
f2fc8d02f4
85 changed files with 1264 additions and 1304 deletions
  1. 5 0
      .ncrunch/Avalonia.Diagnostics.net6.0.v3.ncrunchproject
  2. 5 0
      .ncrunch/Avalonia.Diagnostics.net8.0.v3.ncrunchproject
  3. 5 0
      .ncrunch/Avalonia.Diagnostics.netstandard2.0.v3.ncrunchproject
  4. 506 2
      api/Avalonia.nupkg.xml
  5. 1 1
      src/Avalonia.Base/Animation/AnimatorKeyFrame.cs
  6. 10 12
      src/Avalonia.Base/AvaloniaObject.cs
  7. 9 16
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  8. 1 1
      src/Avalonia.Base/ClassBindingManager.cs
  9. 28 0
      src/Avalonia.Base/Data/BindingBase.cs
  10. 0 2
      src/Avalonia.Base/Data/BindingExpressionBase.cs
  11. 0 122
      src/Avalonia.Base/Data/BindingOperations.cs
  12. 1 1
      src/Avalonia.Base/Data/Core/BindingExpression.cs
  13. 0 20
      src/Avalonia.Base/Data/Core/IBinding2.cs
  14. 3 8
      src/Avalonia.Base/Data/Core/MultiBindingExpression.cs
  15. 1 2
      src/Avalonia.Base/Data/Core/Parsers/ArgumentListParser.cs
  16. 1 1
      src/Avalonia.Base/Data/Core/Parsers/BindingExpressionGrammar.cs
  17. 1 1
      src/Avalonia.Base/Data/Core/Parsers/ExpressionNodeFactory.cs
  18. 4 5
      src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs
  19. 0 35
      src/Avalonia.Base/Data/IBinding.cs
  20. 5 12
      src/Avalonia.Base/Data/IndexerBinding.cs
  21. 0 172
      src/Avalonia.Base/Data/InstancedBinding.cs
  22. 5 18
      src/Avalonia.Base/Data/MultiBinding.cs
  23. 237 0
      src/Avalonia.Base/Data/ReflectionBinding.cs
  24. 0 0
      src/Avalonia.Base/Data/RelativeSource.cs
  25. 0 21
      src/Avalonia.Base/Data/TemplateBinding.Observable.cs
  26. 10 199
      src/Avalonia.Base/Data/TemplateBinding.cs
  27. 170 0
      src/Avalonia.Base/Data/TemplateBindingExpression.cs
  28. 1 1
      src/Avalonia.Base/StyledElementExtensions.cs
  29. 3 5
      src/Avalonia.Base/Styling/Setter.cs
  30. 2 2
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  31. 1 1
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
  32. 4 4
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
  33. 4 4
      src/Avalonia.Controls/ComboBox.cs
  34. 1 1
      src/Avalonia.Controls/Control.cs
  35. 4 4
      src/Avalonia.Controls/ItemsControl.cs
  36. 1 4
      src/Avalonia.Controls/NativeMenuBarPresenter.cs
  37. 2 3
      src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs
  38. 2 3
      src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs
  39. 6 6
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  40. 4 4
      src/Avalonia.Controls/Primitives/TextSearch.cs
  41. 4 7
      src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs
  42. 7 6
      src/Avalonia.Controls/Templates/ITreeDataTemplate.cs
  43. 3 3
      src/Avalonia.Controls/Utils/BindingEvaluator.cs
  44. 1 1
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
  45. 2 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlClassesPropertyResolver.cs
  46. 2 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
  47. 1 1
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
  48. 4 4
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  49. 4 4
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
  50. 4 6
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
  51. 92 15
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs
  52. 0 3
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
  53. 3 15
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
  54. 21 50
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs
  55. 5 20
      src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
  56. 1 1
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
  57. 0 1
      src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
  58. 13 206
      src/Markup/Avalonia.Markup/Data/Binding.cs
  59. 0 137
      src/Markup/Avalonia.Markup/Data/BindingBase.cs
  60. 3 3
      src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs
  61. 2 0
      src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs
  62. 4 0
      src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
  63. 0 19
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
  64. 0 51
      tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Obsolete.cs
  65. 1 1
      tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs
  66. 2 2
      tests/Avalonia.Base.UnitTests/Data/Core/Parsers/BindingExpressionGrammarTests.cs
  67. 1 1
      tests/Avalonia.Base.UnitTests/Data/Core/Parsers/BindingExpressionGrammarTests_Errors.cs
  68. 2 2
      tests/Avalonia.Benchmarks/Data/TemplateBinding_Setup.cs
  69. 2 2
      tests/Avalonia.Benchmarks/Data/TemplateBinding_Values.cs
  70. 2 2
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  71. 1 1
      tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs
  72. 11 21
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
  73. 5 4
      tests/Avalonia.Markup.UnitTests/Data/BindingTests_Converters.cs
  74. 1 1
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionNodeFactoryTests.cs
  75. 1 1
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs
  76. 1 1
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AvaloniaProperty.cs
  77. 1 1
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Indexer.cs
  78. 1 1
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Method.cs
  79. 1 1
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs
  80. 1 1
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Property.cs
  81. 2 2
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
  82. 2 2
      tests/Avalonia.Markup.Xaml.UnitTests/StyleTests.cs
  83. 5 5
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/AssignBindingTests.cs
  84. 1 1
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  85. 1 1
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs

+ 5 - 0
.ncrunch/Avalonia.Diagnostics.net6.0.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Diagnostics.net8.0.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Diagnostics.netstandard2.0.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
+  </Settings>
+</ProjectConfiguration>

+ 506 - 2
api/Avalonia.nupkg.xml

@@ -1,24 +1,84 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Avalonia.Data.IBinding</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Avalonia.Data.InstancedBinding</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:Avalonia.Media.Fonts.FontFamilyLoader</Target>
     <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
     <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Avalonia.Data.BindingBase</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Avalonia.Data.RelativeSourceMode</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Avalonia.Data.TreeType</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:Avalonia.Media.Fonts.FontFamilyLoader</Target>
     <Left>baseline/Avalonia/lib/net6.0/Avalonia.Base.dll</Left>
     <Right>current/Avalonia/lib/net6.0/Avalonia.Base.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Avalonia.Data.IBinding</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Avalonia.Data.InstancedBinding</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:Avalonia.Media.Fonts.FontFamilyLoader</Target>
     <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
     <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Avalonia.Data.BindingBase</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Avalonia.Data.RelativeSourceMode</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Avalonia.Data.TreeType</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:Avalonia.Media.Fonts.FontFamilyLoader</Target>
@@ -31,6 +91,60 @@
     <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
     <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.AvaloniaObject.Bind(Avalonia.AvaloniaProperty,Avalonia.Data.IBinding)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.AvaloniaObject.get_Item(Avalonia.Data.IndexerDescriptor)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.AvaloniaObjectExtensions.Bind(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,Avalonia.Data.IBinding,System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.AvaloniaObjectExtensions.ToBinding``1(System.IObservable{``0})</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.BindingOperations.Apply(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,Avalonia.Data.InstancedBinding,System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.BindingOperations.Apply(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,Avalonia.Data.InstancedBinding)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.TemplateBinding.Initiate(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,System.Object,System.Boolean)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.TemplateBinding.ProvideValue</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.TemplateBinding.Subscribe(System.IObserver{System.Object})</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:Avalonia.Media.Fonts.FontCollectionBase.Initialize(Avalonia.Platform.IFontManagerImpl)</Target>
@@ -55,6 +169,30 @@
     <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
     <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.StyledElementExtensions.BindClass(Avalonia.StyledElement,System.String,Avalonia.Data.IBinding,System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>F:Avalonia.Controls.ItemsControl.DisplayMemberBindingProperty</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>F:Avalonia.Controls.Primitives.SelectingItemsControl.SelectedValueBindingProperty</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>F:Avalonia.Controls.Primitives.TextSearch.TextBindingProperty</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>F:Avalonia.Controls.TextBlock.LetterSpacingProperty</Target>
@@ -67,6 +205,24 @@
     <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
     <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.AutoCompleteBox.BindingEvaluator`1.#ctor(Avalonia.Data.IBinding)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.AutoCompleteBox.BindingEvaluator`1.get_ValueBinding</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.AutoCompleteBox.get_ValueMemberBinding</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:Avalonia.Controls.Design.CreatePreviewWithControl(System.Object)</Target>
@@ -133,6 +289,72 @@
     <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
     <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.ItemsControl.get_DisplayMemberBinding</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.Primitives.SelectingItemsControl.get_SelectedValueBinding</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.Primitives.TextSearch.GetTextBinding(Avalonia.Interactivity.Interactive)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.Primitives.TextSearch.SetTextBinding(Avalonia.Interactivity.Interactive,Avalonia.Data.IBinding)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.Templates.FuncTreeDataTemplate.ItemsSelector(System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.Templates.ITreeDataTemplate.ItemsSelector(System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.MultiBinding.get_Bindings</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.MultiBinding.Initiate(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,System.Object,System.Boolean)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Markup.Xaml.MarkupExtensions.DynamicResourceExtension.ProvideValue(System.IServiceProvider)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension.ProvideValue(System.IServiceProvider)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Markup.Xaml.Templates.TreeDataTemplate.ItemsSelector(System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>F:Avalonia.Media.Fonts.FontCollectionBase._glyphTypefaceCache</Target>
@@ -169,6 +391,60 @@
     <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
     <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.AvaloniaObject.Bind(Avalonia.AvaloniaProperty,Avalonia.Data.IBinding)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.AvaloniaObject.get_Item(Avalonia.Data.IndexerDescriptor)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.AvaloniaObjectExtensions.Bind(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,Avalonia.Data.IBinding,System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.AvaloniaObjectExtensions.ToBinding``1(System.IObservable{``0})</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.BindingOperations.Apply(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,Avalonia.Data.InstancedBinding,System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.BindingOperations.Apply(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,Avalonia.Data.InstancedBinding)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.TemplateBinding.Initiate(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,System.Object,System.Boolean)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.TemplateBinding.ProvideValue</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.TemplateBinding.Subscribe(System.IObserver{System.Object})</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:Avalonia.Media.Fonts.FontCollectionBase.Initialize(Avalonia.Platform.IFontManagerImpl)</Target>
@@ -193,6 +469,30 @@
     <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
     <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.StyledElementExtensions.BindClass(Avalonia.StyledElement,System.String,Avalonia.Data.IBinding,System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>F:Avalonia.Controls.ItemsControl.DisplayMemberBindingProperty</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>F:Avalonia.Controls.Primitives.SelectingItemsControl.SelectedValueBindingProperty</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>F:Avalonia.Controls.Primitives.TextSearch.TextBindingProperty</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>F:Avalonia.Controls.TextBlock.LetterSpacingProperty</Target>
@@ -205,6 +505,24 @@
     <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
     <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.AutoCompleteBox.BindingEvaluator`1.#ctor(Avalonia.Data.IBinding)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.AutoCompleteBox.BindingEvaluator`1.get_ValueBinding</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.AutoCompleteBox.get_ValueMemberBinding</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:Avalonia.Controls.Design.CreatePreviewWithControl(System.Object)</Target>
@@ -271,12 +589,78 @@
     <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
     <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.ItemsControl.get_DisplayMemberBinding</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.Primitives.SelectingItemsControl.get_SelectedValueBinding</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.Primitives.TextSearch.GetTextBinding(Avalonia.Interactivity.Interactive)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.Primitives.TextSearch.SetTextBinding(Avalonia.Interactivity.Interactive,Avalonia.Data.IBinding)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.Templates.FuncTreeDataTemplate.ItemsSelector(System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Controls.Templates.ITreeDataTemplate.ItemsSelector(System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:Avalonia.Dialogs.Internal.ManagedFileChooserFilterViewModel.#ctor(Avalonia.Platform.Storage.FilePickerFileType)</Target>
     <Left>baseline/Avalonia/lib/net8.0/Avalonia.Dialogs.dll</Left>
     <Right>current/Avalonia/lib/net8.0/Avalonia.Dialogs.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.MultiBinding.get_Bindings</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Data.MultiBinding.Initiate(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,System.Object,System.Boolean)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Markup.Xaml.MarkupExtensions.DynamicResourceExtension.ProvideValue(System.IServiceProvider)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension.ProvideValue(System.IServiceProvider)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:Avalonia.Markup.Xaml.Templates.TreeDataTemplate.ItemsSelector(System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>F:Avalonia.Media.Fonts.FontCollectionBase._glyphTypefaceCache</Target>
@@ -331,6 +715,12 @@
     <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
     <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0006</DiagnosticId>
+    <Target>M:Avalonia.Controls.Templates.ITreeDataTemplate.BindChildren(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0006</DiagnosticId>
     <Target>M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)</Target>
@@ -439,6 +829,12 @@
     <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
     <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0006</DiagnosticId>
+    <Target>M:Avalonia.Controls.Templates.ITreeDataTemplate.BindChildren(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,System.Object)</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0006</DiagnosticId>
     <Target>M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)</Target>
@@ -517,6 +913,114 @@
     <Left>baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll</Left>
     <Right>current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0007</DiagnosticId>
+    <Target>T:Avalonia.Data.TemplateBinding</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0007</DiagnosticId>
+    <Target>T:Avalonia.Data.TemplateBinding</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:Avalonia.Data.TemplateBinding</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:Avalonia.Data.Binding</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:Avalonia.Data.TemplateBinding</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:Avalonia.Data.Binding</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0009</DiagnosticId>
+    <Target>T:Avalonia.Data.TemplateBinding</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0009</DiagnosticId>
+    <Target>T:Avalonia.Data.MultiBinding</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0009</DiagnosticId>
+    <Target>T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0009</DiagnosticId>
+    <Target>T:Avalonia.Markup.Xaml.MarkupExtensions.DynamicResourceExtension</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0009</DiagnosticId>
+    <Target>T:Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension</Target>
+    <Left>baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0009</DiagnosticId>
+    <Target>T:Avalonia.Data.TemplateBinding</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0009</DiagnosticId>
+    <Target>T:Avalonia.Data.MultiBinding</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0009</DiagnosticId>
+    <Target>T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0009</DiagnosticId>
+    <Target>T:Avalonia.Markup.Xaml.MarkupExtensions.DynamicResourceExtension</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0009</DiagnosticId>
+    <Target>T:Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension</Target>
+    <Left>baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Left>
+    <Right>current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0012</DiagnosticId>
     <Target>M:Avalonia.Media.Fonts.FontCollectionBase.get_Count</Target>
@@ -637,4 +1141,4 @@
     <Left>baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll</Left>
     <Right>current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll</Right>
   </Suppression>
-</Suppressions>
+</Suppressions>

+ 1 - 1
src/Avalonia.Base/Animation/AnimatorKeyFrame.cs

@@ -46,7 +46,7 @@ namespace Avalonia.Animation
             Property = setter.Property;
             var value = setter.Value;
 
-            if (value is IBinding binding)
+            if (value is BindingBase binding)
             {
                 return Bind(ValueProperty, binding, targetControl);
             }

+ 10 - 12
src/Avalonia.Base/AvaloniaObject.cs

@@ -98,7 +98,7 @@ namespace Avalonia
         /// Gets or sets a binding for a <see cref="AvaloniaProperty"/>.
         /// </summary>
         /// <param name="binding">The binding information.</param>
-        public IBinding this[IndexerDescriptor binding]
+        public BindingBase this[IndexerDescriptor binding]
         {
             get { return new IndexerBinding(this, binding.Property!, binding.Mode); }
             set { this.Bind(binding.Property!, value); }
@@ -417,14 +417,14 @@ namespace Avalonia
         }
 
         /// <summary>
-        /// Binds a <see cref="AvaloniaProperty"/> to an <see cref="IBinding"/>.
+        /// Binds a <see cref="AvaloniaProperty"/> to an <see cref="BindingBase"/>.
         /// </summary>
         /// <param name="property">The property.</param>
         /// <param name="binding">The binding.</param>
         /// <returns>
         /// The binding expression which represents the binding instance on this object.
         /// </returns>
-        public BindingExpressionBase Bind(AvaloniaProperty property, IBinding binding)
+        public BindingExpressionBase Bind(AvaloniaProperty property, BindingBase binding)
         {
             return Bind(property, binding, null);
         }
@@ -474,9 +474,9 @@ namespace Avalonia
             VerifyAccess();
             ValidatePriority(priority);
 
-            if (source is IBinding2 b)
+            if (source is BindingBase b)
             {
-                if (b.Instance(this, property, null) is not UntypedBindingExpressionBase expression)
+                if (b.CreateInstance(this, property, null) is not UntypedBindingExpressionBase expression)
                     throw new NotSupportedException($"Binding returned unsupported {nameof(BindingExpressionBase)}.");
 
                 if (priority != expression.Priority)
@@ -574,9 +574,9 @@ namespace Avalonia
                 throw new ArgumentException($"The property {property.Name} is readonly.");
             }
 
-            if (source is IBinding2 b)
+            if (source is BindingBase b)
             {
-                if (b.Instance(this, property, null) is not UntypedBindingExpressionBase expression)
+                if (b.CreateInstance(this, property, null) is not UntypedBindingExpressionBase expression)
                     throw new NotSupportedException($"Binding returned unsupported {nameof(BindingExpressionBase)}.");
                 return GetValueStore().AddBinding(property, expression);
             }
@@ -643,7 +643,7 @@ namespace Avalonia
         public void CoerceValue(AvaloniaProperty property) => _values.CoerceValue(property);
 
         /// <summary>
-        /// Binds a <see cref="AvaloniaProperty"/> to an <see cref="IBinding"/>.
+        /// Binds a <see cref="AvaloniaProperty"/> to an <see cref="BindingBase"/>.
         /// </summary>
         /// <param name="property">The property.</param>
         /// <param name="binding">The binding.</param>
@@ -656,11 +656,9 @@ namespace Avalonia
         /// <returns>
         /// The binding expression which represents the binding instance on this object.
         /// </returns>
-        internal BindingExpressionBase Bind(AvaloniaProperty property, IBinding binding, object? anchor)
+        internal BindingExpressionBase Bind(AvaloniaProperty property, BindingBase binding, object? anchor)
         {
-            if (binding is not IBinding2 b)
-                throw new NotSupportedException($"Unsupported IBinding implementation '{binding}'.");
-            if (b.Instance(this, property, anchor) is not UntypedBindingExpressionBase expression)
+            if (binding.CreateInstance(this, property, anchor) is not UntypedBindingExpressionBase expression)
                 throw new NotSupportedException($"Binding returned unsupported {nameof(BindingExpressionBase)}.");
 
             return GetValueStore().AddBinding(property, expression);

+ 9 - 16
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@@ -11,12 +11,12 @@ namespace Avalonia
     public static class AvaloniaObjectExtensions
     {
         /// <summary>
-        /// Converts an <see cref="IObservable{T}"/> to an <see cref="IBinding"/>.
+        /// Converts an <see cref="IObservable{T}"/> to an <see cref="BindingBase"/>.
         /// </summary>
         /// <typeparam name="T">The type produced by the observable.</typeparam>
         /// <param name="source">The observable</param>
-        /// <returns>An <see cref="IBinding"/>.</returns>
-        public static IBinding ToBinding<T>(this IObservable<T> source)
+        /// <returns>An <see cref="BindingBase"/>.</returns>
+        public static BindingBase ToBinding<T>(this IObservable<T> source)
         {
             return new BindingAdaptor(
                 typeof(T).IsValueType
@@ -228,7 +228,7 @@ namespace Avalonia
         }
 
         /// <summary>
-        /// Binds a property on an <see cref="AvaloniaObject"/> to an <see cref="IBinding"/>.
+        /// Binds a property on an <see cref="AvaloniaObject"/> to an <see cref="BindingBase"/>.
         /// </summary>
         /// <param name="target">The object.</param>
         /// <param name="property">The property to bind.</param>
@@ -244,7 +244,7 @@ namespace Avalonia
         public static IDisposable Bind(
             this AvaloniaObject target,
             AvaloniaProperty property,
-            IBinding binding,
+            BindingBase binding,
             object? anchor = null)
         {
             target = target ?? throw new ArgumentNullException(nameof(target));
@@ -359,7 +359,7 @@ namespace Avalonia
             return observable.Subscribe(new ClassHandlerObserver<TTarget, TValue>(action));
         }
 
-        private class BindingAdaptor : IBinding2
+        private class BindingAdaptor : BindingBase
         {
             private readonly IObservable<object?> _source;
 
@@ -368,17 +368,10 @@ namespace Avalonia
                 this._source = source;
             }
 
-            public InstancedBinding? Initiate(
+            internal override BindingExpressionBase CreateInstance(
                 AvaloniaObject target,
-                AvaloniaProperty? targetProperty,
-                object? anchor = null,
-                bool enableDataValidation = false)
-            {
-                var expression = new UntypedObservableBindingExpression(_source, BindingPriority.LocalValue);
-                return new InstancedBinding(expression, BindingMode.OneWay, BindingPriority.LocalValue);
-            }
-
-            BindingExpressionBase IBinding2.Instance(AvaloniaObject target, AvaloniaProperty? property, object? anchor)
+                AvaloniaProperty? property,
+                object? anchor)
             {
                 return new UntypedObservableBindingExpression(_source, BindingPriority.LocalValue);
             }

+ 1 - 1
src/Avalonia.Base/ClassBindingManager.cs

@@ -12,7 +12,7 @@ namespace Avalonia
         private static readonly Dictionary<string, AvaloniaProperty> s_RegisteredProperties =
             new Dictionary<string, AvaloniaProperty>();
 
-        public static IDisposable Bind(StyledElement target, string className, IBinding source, object anchor)
+        public static IDisposable Bind(StyledElement target, string className, BindingBase source, object anchor)
         {
             var prop = GetClassProperty(className);
             return target.Bind(prop, source);

+ 28 - 0
src/Avalonia.Base/Data/BindingBase.cs

@@ -0,0 +1,28 @@
+namespace Avalonia.Data;
+
+/// <summary>
+/// Base class for the various types of binding supported by Avalonia.
+/// </summary>
+public abstract class BindingBase
+{
+    /// <summary>
+    /// Creates a <see cref="BindingExpressionBase"/> from a binding.
+    /// </summary>
+    /// <param name="target">The target of the binding.</param>
+    /// <param name="targetProperty">The target property of the binding.</param>
+    /// <param name="anchor">
+    /// If <paramref name="target"/> is not a control, provides an anchor object from which to
+    /// locate a data context or other controls.
+    /// </param>
+    /// <returns>
+    /// A newly instantiated <see cref="BindingExpressionBase"/>.
+    /// </returns>
+    /// <remarks>
+    /// This is a low-level method which returns a binding expression that is not yet connected to
+    /// a binding sink, and so is inactive.
+    /// </remarks>
+    internal abstract BindingExpressionBase CreateInstance(
+        AvaloniaObject target,
+        AvaloniaProperty? targetProperty,
+        object? anchor);
+}

+ 0 - 2
src/Avalonia.Base/Data/BindingExpressionBase.cs

@@ -10,8 +10,6 @@ public abstract class BindingExpressionBase : IDisposable, ISetterInstance
     {
     }
 
-    internal BindingMode Mode { get; private protected set; }
-
     public virtual void Dispose()
     {
         GC.SuppressFinalize(this);

+ 0 - 122
src/Avalonia.Base/Data/BindingOperations.cs

@@ -1,6 +1,4 @@
 using System;
-using Avalonia.Diagnostics;
-using Avalonia.Reactive;
 
 namespace Avalonia.Data
 {
@@ -8,99 +6,6 @@ namespace Avalonia.Data
     {
         public static readonly object DoNothing = new DoNothingType();
 
-        /// <summary>
-        /// Applies an <see cref="InstancedBinding"/> a property on an <see cref="AvaloniaObject"/>.
-        /// </summary>
-        /// <param name="target">The target object.</param>
-        /// <param name="property">The property to bind.</param>
-        /// <param name="binding">The instanced binding.</param>
-        /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
-        [Obsolete(ObsoletionMessages.MayBeRemovedInAvalonia12)]
-        public static IDisposable Apply(
-            AvaloniaObject target,
-            AvaloniaProperty property,
-            InstancedBinding binding)
-        {
-            _ = target ?? throw new ArgumentNullException(nameof(target));
-            _ = property ?? throw new ArgumentNullException(nameof(property));
-            _ = binding ?? throw new ArgumentNullException(nameof(binding));
-
-            if (binding.Expression is { } expression)
-            {
-                return target.GetValueStore().AddBinding(property, expression);
-            }
-
-            var mode = binding.Mode;
-
-            if (mode == BindingMode.Default)
-            {
-                mode = property.GetMetadata(target).DefaultBindingMode;
-            }
-
-            switch (mode)
-            {
-                case BindingMode.Default:
-                case BindingMode.OneWay:
-                    return target.Bind(property, binding.Source, binding.Priority);
-                case BindingMode.TwoWay:
-                {
-                    if (binding.Source is not IObserver<object?> observer)
-                        throw new InvalidOperationException("InstancedBinding does not contain a subject.");
-                    return new TwoWayBindingDisposable(
-                        target.Bind(property, binding.Source, binding.Priority),
-                        target.GetObservable(property).Subscribe(observer));
-                }
-                case BindingMode.OneTime:
-                {
-                    // Perf: Avoid allocating closure in the outer scope.
-                    var targetCopy = target;
-                    var propertyCopy = property;
-                    var bindingCopy = binding;
-
-                    return binding.Source
-                        .Where(x => BindingNotification.ExtractValue(x) != AvaloniaProperty.UnsetValue)
-                        .Take(1)
-                        .Subscribe(x => targetCopy.SetValue(
-                            propertyCopy,
-                            BindingNotification.ExtractValue(x),
-                            bindingCopy.Priority));
-                }
-
-                case BindingMode.OneWayToSource:
-                {
-                    if (binding.Source is not IObserver<object?> observer)
-                        throw new InvalidOperationException("InstancedBinding does not contain a subject.");
-
-                    return Observable.CombineLatest(
-                        binding.Source,
-                        target.GetObservable(property),
-                        (_, v) => v)
-                    .Subscribe(x => observer.OnNext(x));
-                }
-
-                default:
-                    throw new ArgumentException("Invalid binding mode.");
-            }
-        }
-
-        /// <summary>
-        /// Applies an <see cref="InstancedBinding"/> a property on an <see cref="AvaloniaObject"/>.
-        /// </summary>
-        /// <param name="target">The target object.</param>
-        /// <param name="property">The property to bind.</param>
-        /// <param name="binding">The instanced binding.</param>
-        /// <param name="anchor">Obsolete, unused.</param>
-        /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
-        [Obsolete("Use the Apply(AvaloniaObject, AvaloniaProperty, InstancedBinding) overload.")]
-        public static IDisposable Apply(
-            AvaloniaObject target,
-            AvaloniaProperty property,
-            InstancedBinding binding,
-            object? anchor)
-        {
-            return Apply(target, property, binding);
-        }
-
         /// <summary>
         /// Retrieves the <see cref="BindingExpressionBase"/> that is currently active on the
         /// specified property.
@@ -119,33 +24,6 @@ namespace Avalonia.Data
         {
             return target.GetValueStore().GetExpression(property);
         }
-
-        private sealed class TwoWayBindingDisposable : IDisposable
-        {
-            private readonly IDisposable _toTargetSubscription;
-            private readonly IDisposable _fromTargetSubsription;
-
-            private bool _isDisposed;
-
-            public TwoWayBindingDisposable(IDisposable toTargetSubscription, IDisposable fromTargetSubsription)
-            {
-                _toTargetSubscription = toTargetSubscription;
-                _fromTargetSubsription = fromTargetSubsription;
-            }
-
-            public void Dispose()
-            {
-                if (_isDisposed)
-                {
-                    return;
-                }
-
-                _fromTargetSubsription.Dispose();
-                _toTargetSubscription.Dispose();
-
-                _isDisposed = true;
-            }
-        }
     }
 
     public sealed class DoNothingType

+ 1 - 1
src/Avalonia.Base/Data/Core/BindingExpression.cs

@@ -23,7 +23,7 @@ namespace Avalonia.Data.Core;
 /// A <see cref="BindingExpression"/> represents a untyped binding which has been
 /// instantiated on an object.
 /// </remarks>
-internal partial class BindingExpression : UntypedBindingExpressionBase, IDescription, IDisposable
+internal class BindingExpression : UntypedBindingExpressionBase, IDescription, IDisposable
 {
     private static readonly List<ExpressionNode> s_emptyExpressionNodes = new();
     private readonly WeakReference<object?>? _source;

+ 0 - 20
src/Avalonia.Base/Data/Core/IBinding2.cs

@@ -1,20 +0,0 @@
-namespace Avalonia.Data.Core;
-
-/// <summary>
-/// Internal interface for instancing bindings on an <see cref="AvaloniaObject"/>.
-/// </summary>
-/// <remarks>
-/// TODO12: The presence of this interface is a hack needed because we can't break our API until
-/// 12.0. The Instance method would ideally be located as an internal method on a BindingBase
-/// class, but we already have a BindingBase in 11.x which is not suitable for this as it contains
-/// extra members that are not needed on all of the binding types. The current BindingBase should
-/// be renamed to something like BindingMarkupExtensionBase and a new BindingBase created with the
-/// Instance method from this interface. This interface should then be removed.
-/// </remarks>
-internal interface IBinding2 : IBinding
-{
-    BindingExpressionBase Instance(
-        AvaloniaObject target,
-        AvaloniaProperty? targetProperty,
-        object? anchor);
-}

+ 3 - 8
src/Avalonia.Base/Data/Core/MultiBindingExpression.cs

@@ -10,7 +10,7 @@ namespace Avalonia.Data.Core;
 internal class MultiBindingExpression : UntypedBindingExpressionBase, IBindingExpressionSink
 {
     private static readonly object s_uninitialized = new object();
-    private readonly IBinding[] _bindings;
+    private readonly BindingBase[] _bindings;
     private readonly IMultiValueConverter? _converter;
     private readonly CultureInfo? _converterCulture;
     private readonly object? _converterParameter;
@@ -22,7 +22,7 @@ internal class MultiBindingExpression : UntypedBindingExpressionBase, IBindingEx
 
     public MultiBindingExpression(
         BindingPriority priority,
-        IList<IBinding> bindings,
+        IList<BindingBase> bindings,
         IMultiValueConverter? converter,
         CultureInfo? converterCulture,
         object? converterParameter,
@@ -63,12 +63,7 @@ internal class MultiBindingExpression : UntypedBindingExpressionBase, IBindingEx
 
         for (var i = 0; i < _bindings.Length; ++i)
         {
-            var binding = _bindings[i]; 
-
-            if (binding is not IBinding2 b)
-                throw new NotSupportedException($"Unsupported IBinding implementation '{binding}'.");
-
-            var expression = b.Instance(target, null, null);
+            var expression = _bindings[i].CreateInstance(target, null, null);
 
             if (expression is not UntypedBindingExpressionBase e)
                 throw new NotSupportedException($"Unsupported BindingExpressionBase implementation '{expression}'.");

+ 1 - 2
src/Markup/Avalonia.Markup/Markup/Parsers/ArgumentListParser.cs → src/Avalonia.Base/Data/Core/Parsers/ArgumentListParser.cs

@@ -1,8 +1,7 @@
 using System.Collections.Generic;
-using Avalonia.Data.Core;
 using Avalonia.Utilities;
 
-namespace Avalonia.Markup.Parsers
+namespace Avalonia.Data.Core.Parsers
 {
     internal static class ArgumentListParser
     {

+ 1 - 1
src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs → src/Avalonia.Base/Data/Core/Parsers/BindingExpressionGrammar.cs

@@ -6,7 +6,7 @@ using System.Collections.Generic;
 using Avalonia.Data.Core;
 using Avalonia.Utilities;
 
-namespace Avalonia.Markup.Parsers
+namespace Avalonia.Data.Core.Parsers
 {
     internal enum SourceMode
     {

+ 1 - 1
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionNodeFactory.cs → src/Avalonia.Base/Data/Core/Parsers/ExpressionNodeFactory.cs

@@ -7,7 +7,7 @@ using Avalonia.Data;
 using Avalonia.Data.Core.ExpressionNodes;
 using Avalonia.Data.Core.ExpressionNodes.Reflection;
 
-namespace Avalonia.Markup.Parsers
+namespace Avalonia.Data.Core.Parsers
 {
     /// <summary>
     /// Creates <see cref="ExpressionNode"/>s from a <see cref="BindingExpressionGrammar"/>.

+ 4 - 5
src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs

@@ -220,11 +220,10 @@ public abstract class UntypedBindingExpressionBase : BindingExpressionBase,
     /// The binding expression is already instantiated on an AvaloniaObject.
     /// </exception>
     /// <remarks>
-    /// This method is mostly here for backwards compatibility with <see cref="InstancedBinding"/>
-    /// and unit testing and we may want to remove it in future. In particular its usefulness in
-    /// terms of unit testing is limited in that it preserves the semantics of binding expressions
-    /// as expected by unit tests, not necessarily the semantics that will be used when the
-    /// expression is used as an <see cref="IValueEntry"/> instantiated in a
+    /// This method is mostly here for unit testing and we may want to remove it in future. In
+    /// particular its usefulness is limited in that it preserves the semantics of binding
+    /// expressions as expected by unit tests, not necessarily the semantics that will be used
+    /// when the expression is used as an <see cref="IValueEntry"/> instantiated in a
     /// <see cref="ValueStore"/>. Unit tests should be migrated to not test the behaviour of
     /// binding expressions through an observable, and instead test the behaviour of the binding
     /// when applied to an <see cref="AvaloniaObject"/>.

+ 0 - 35
src/Avalonia.Base/Data/IBinding.cs

@@ -1,35 +0,0 @@
-using System;
-using Avalonia.Diagnostics;
-using Avalonia.Metadata;
-
-namespace Avalonia.Data
-{
-    /// <summary>
-    /// Holds a binding that can be applied to a property on an object.
-    /// </summary>
-    [NotClientImplementable]
-    public interface IBinding
-    {
-        /// <summary>
-        /// Initiates the binding on a target object.
-        /// </summary>
-        /// <param name="target">The target instance.</param>
-        /// <param name="targetProperty">The target property. May be null.</param>
-        /// <param name="anchor">
-        /// 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 <paramref name="anchor"/> parameter 
-        /// can be used to provide this context.
-        /// </param>
-        /// <param name="enableDataValidation">Whether data validation should be enabled.</param>
-        /// <returns>
-        /// A <see cref="InstancedBinding"/> or null if the binding could not be resolved.
-        /// </returns>
-        [Obsolete(ObsoletionMessages.MayBeRemovedInAvalonia12)]
-        InstancedBinding? Initiate(
-            AvaloniaObject target, 
-            AvaloniaProperty? targetProperty,
-            object? anchor = null,
-            bool enableDataValidation = false);
-    }
-}

+ 5 - 12
src/Avalonia.Base/Data/IndexerBinding.cs

@@ -4,7 +4,7 @@ using Avalonia.Diagnostics;
 
 namespace Avalonia.Data
 {
-    internal class IndexerBinding : IBinding2
+    internal class IndexerBinding : BindingBase
     {
         public IndexerBinding(
             AvaloniaObject source,
@@ -16,22 +16,15 @@ namespace Avalonia.Data
             Mode = mode;
         }
 
-        private AvaloniaObject Source { get; }
         public AvaloniaProperty Property { get; }
+
+        private AvaloniaObject Source { get; }
         private BindingMode Mode { get; }
 
-        [Obsolete(ObsoletionMessages.MayBeRemovedInAvalonia12)]
-        public InstancedBinding? Initiate(
+        internal override BindingExpressionBase CreateInstance(
             AvaloniaObject target,
             AvaloniaProperty? targetProperty,
-            object? anchor = null,
-            bool enableDataValidation = false)
-        {
-            var expression = new IndexerBindingExpression(Source, Property, target, targetProperty, Mode);
-            return new InstancedBinding(expression, Mode, BindingPriority.LocalValue);
-        }
-
-        BindingExpressionBase IBinding2.Instance(AvaloniaObject target, AvaloniaProperty? targetProperty, object? anchor)
+            object? anchor)
         {
             return new IndexerBindingExpression(Source, Property, target, targetProperty, Mode);
         }

+ 0 - 172
src/Avalonia.Base/Data/InstancedBinding.cs

@@ -1,172 +0,0 @@
-using System;
-using System.ComponentModel;
-using Avalonia.Data.Core;
-using Avalonia.Reactive;
-using ObservableEx = Avalonia.Reactive.Observable;
-
-namespace Avalonia.Data
-{
-    /// <summary>
-    /// Holds the result of calling <see cref="IBinding.Initiate"/>.
-    /// </summary>
-    /// <remarks>
-    /// Whereas an <see cref="IBinding"/> holds a description of a binding such as "Bind to the X
-    /// property on a control's DataContext"; this class represents a binding that has been 
-    /// *instanced* by calling <see cref="IBinding.Initiate(AvaloniaObject, AvaloniaProperty, object, bool)"/>
-    /// on a target object.
-    /// </remarks>
-    public sealed class InstancedBinding
-    {
-        private readonly AvaloniaObject? _target;
-        private readonly UntypedBindingExpressionBase? _expression;
-        private IObservable<object?>? _observable;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="InstancedBinding"/> class.
-        /// </summary>
-        /// <param name="source">The binding source.</param>
-        /// <param name="mode">The binding mode.</param>
-        /// <param name="priority">The priority of the binding.</param>
-        /// <remarks>
-        /// This constructor can be used to create any type of binding and as such requires an
-        /// <see cref="IObservable{Object}"/> as the binding source because this is the only binding
-        /// source which can be used for all binding modes. If you wish to create an instance with
-        /// something other than a subject, use one of the static creation methods on this class.
-        /// </remarks>
-        internal InstancedBinding(IObservable<object?> source, BindingMode mode, BindingPriority priority)
-        {
-            Mode = mode;
-            Priority = priority;
-            _observable = source ?? throw new ArgumentNullException(nameof(source));
-        }
-
-        internal InstancedBinding(
-            UntypedBindingExpressionBase source,
-            BindingMode mode,
-            BindingPriority priority)
-        {
-            Mode = mode;
-            Priority = priority;
-            _expression = source ?? throw new ArgumentNullException(nameof(source));
-        }
-
-        internal InstancedBinding(
-            AvaloniaObject? target,
-            UntypedBindingExpressionBase source, 
-            BindingMode mode, 
-            BindingPriority priority)
-        {
-            Mode = mode;
-            Priority = priority;
-            _expression = source ?? throw new ArgumentNullException(nameof(source));
-            _target = target;
-        }
-
-        /// <summary>
-        /// Gets the binding mode with which the binding was initiated.
-        /// </summary>
-        public BindingMode Mode { get; }
-
-        /// <summary>
-        /// Gets the binding priority.
-        /// </summary>
-        public BindingPriority Priority { get; }
-
-        /// <summary>
-        /// Gets the binding source observable.
-        /// </summary>
-        public IObservable<object?> Source => _observable ??= _expression!.ToObservable(_target);
-
-        [Obsolete("Use Source property"), EditorBrowsable(EditorBrowsableState.Never)]
-        public IObservable<object?> Observable => Source;
-
-        internal UntypedBindingExpressionBase? Expression => _expression;
-
-        /// <summary>
-        /// Creates a new one-time binding with a fixed value.
-        /// </summary>
-        /// <param name="value">The value.</param>
-        /// <param name="priority">The priority of the binding.</param>
-        /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
-        public static InstancedBinding OneTime(
-            object value,
-            BindingPriority priority = BindingPriority.LocalValue)
-        {
-            return new InstancedBinding(ObservableEx.SingleValue(value), BindingMode.OneTime, priority);
-        }
-
-        /// <summary>
-        /// Creates a new one-time binding.
-        /// </summary>
-        /// <param name="observable">The source observable.</param>
-        /// <param name="priority">The priority of the binding.</param>
-        /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
-        public static InstancedBinding OneTime(
-            IObservable<object?> observable,
-            BindingPriority priority = BindingPriority.LocalValue)
-        {
-            _ = observable ?? throw new ArgumentNullException(nameof(observable));
-
-            return new InstancedBinding(observable, BindingMode.OneTime, priority);
-        }
-
-        /// <summary>
-        /// Creates a new one-way binding.
-        /// </summary>
-        /// <param name="observable">The source observable.</param>
-        /// <param name="priority">The priority of the binding.</param>
-        /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
-        public static InstancedBinding OneWay(
-            IObservable<object?> observable,
-            BindingPriority priority = BindingPriority.LocalValue)
-        {
-            _ = observable ?? throw new ArgumentNullException(nameof(observable));
-
-            return new InstancedBinding(observable, BindingMode.OneWay, priority);
-        }
-
-        /// <summary>
-        /// Creates a new one-way to source binding.
-        /// </summary>
-        /// <param name="observer">The binding source.</param>
-        /// <param name="priority">The priority of the binding.</param>
-        /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
-        public static InstancedBinding OneWayToSource(
-            IObserver<object?> observer,
-            BindingPriority priority = BindingPriority.LocalValue)
-        {
-            _ = observer ?? throw new ArgumentNullException(nameof(observer));
-
-            return new InstancedBinding((IObservable<object?>)observer, BindingMode.OneWayToSource, priority);
-        }
-
-        /// <summary>
-        /// Creates a new two-way binding.
-        /// </summary>
-        /// <param name="observable">The binding source.</param>
-        /// <param name="observer">The binding source.</param>
-        /// <param name="priority">The priority of the binding.</param>
-        /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
-        public static InstancedBinding TwoWay(
-            IObservable<object?> observable,
-            IObserver<object?> observer,
-            BindingPriority priority = BindingPriority.LocalValue)
-        {
-            _ = observable ?? throw new ArgumentNullException(nameof(observable));
-            _ = observer ?? throw new ArgumentNullException(nameof(observer));
-
-            var subject = observable == observer ? observable : new CombinedSubject<object?>(observer, observable);
-            return new InstancedBinding(subject, BindingMode.TwoWay, priority);
-        }
-
-        /// <summary>
-        /// Creates a copy of the <see cref="InstancedBinding"/> with a different priority.
-        /// </summary>
-        /// <param name="priority">The priority of the binding.</param>
-        /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
-        public InstancedBinding WithPriority(BindingPriority priority)
-        {
-            return new InstancedBinding(Source, Mode, priority);
-        }
-    }
-}

+ 5 - 18
src/Markup/Avalonia.Markup/Data/MultiBinding.cs → src/Avalonia.Base/Data/MultiBinding.cs

@@ -1,25 +1,23 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Globalization;
-using System.Linq;
-using Avalonia.Reactive;
 using Avalonia.Data.Converters;
-using Avalonia.Metadata;
 using Avalonia.Data.Core;
-using System.ComponentModel;
+using Avalonia.Metadata;
 
 namespace Avalonia.Data
 {
     /// <summary>
     /// A XAML binding that calculates an aggregate value from multiple child <see cref="Bindings"/>.
     /// </summary>
-    public class MultiBinding : IBinding2
+    public sealed class MultiBinding : BindingBase
     {
         /// <summary>
         /// Gets the collection of child bindings.
         /// </summary>
         [Content, AssignBinding]
-        public IList<IBinding> Bindings { get; set; } = new List<IBinding>();
+        public IList<BindingBase> Bindings { get; set; } = new List<BindingBase>();
 
         /// <summary>
         /// Gets or sets the <see cref="IMultiValueConverter"/> to use.
@@ -77,18 +75,7 @@ namespace Avalonia.Data
             TargetNullValue = AvaloniaProperty.UnsetValue;
         }
 
-        /// <inheritdoc/>
-        public InstancedBinding? Initiate(
-            AvaloniaObject target,
-            AvaloniaProperty? targetProperty,
-            object? anchor = null,
-            bool enableDataValidation = false)
-        {
-            var expression = InstanceCore(target, targetProperty);
-            return new InstancedBinding(target, expression, Mode, Priority);
-        }
-
-        BindingExpressionBase IBinding2.Instance(
+        internal override BindingExpressionBase CreateInstance(
             AvaloniaObject target,
             AvaloniaProperty? targetProperty,
             object? anchor)

+ 237 - 0
src/Avalonia.Base/Data/ReflectionBinding.cs

@@ -0,0 +1,237 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using Avalonia.Controls;
+using Avalonia.Data.Converters;
+using Avalonia.Data.Core;
+using Avalonia.Data.Core.ExpressionNodes;
+using Avalonia.Data.Core.Parsers;
+using Avalonia.Utilities;
+
+namespace Avalonia.Data
+{
+    /// <summary>
+    /// A binding that uses reflection to access members.
+    /// </summary>
+    [RequiresUnreferencedCode(TrimmingMessages.ReflectionBindingRequiresUnreferencedCodeMessage)]
+    [RequiresDynamicCode(TrimmingMessages.ReflectionBindingRequiresDynamicCodeMessage)]
+    public class ReflectionBinding : BindingBase
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ReflectionBinding"/> class.
+        /// </summary>
+        public ReflectionBinding()
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ReflectionBinding"/> class.
+        /// </summary>
+        /// <param name="path">The binding path.</param>
+        public ReflectionBinding(string path)
+        {
+            Path = path;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ReflectionBinding"/> class.
+        /// </summary>
+        /// <param name="path">The binding path.</param>
+        /// <param name="mode">The binding mode.</param>
+        public ReflectionBinding(string path, BindingMode mode)
+        {
+            Path = path;
+            Mode = mode;
+        }
+        
+        /// <summary>
+        /// Gets or sets the amount of time, in milliseconds, to wait before updating the binding 
+        /// source after the value on the target changes.
+        /// </summary>
+        /// <remarks>
+        /// There is no delay when the source is updated via <see cref="UpdateSourceTrigger.LostFocus"/> 
+        /// or <see cref="BindingExpressionBase.UpdateSource"/>. Nor is there a delay when 
+        /// <see cref="BindingMode.OneWayToSource"/> is active and a new source object is provided.
+        /// </remarks>
+        public int Delay { get; set; }
+
+        /// <summary>
+        /// Gets or sets the <see cref="IValueConverter"/> to use.
+        /// </summary>
+        public IValueConverter? Converter { get; set; }
+
+        /// <summary>
+        /// Gets or sets the culture in which to evaluate the converter.
+        /// </summary>
+        /// <value>The default value is null.</value>
+        /// <remarks>
+        /// If this property is not set then <see cref="CultureInfo.CurrentCulture"/> will be used.
+        /// </remarks>
+        [TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
+        public CultureInfo? ConverterCulture { get; set; }
+
+        /// <summary>
+        /// Gets or sets a parameter to pass to <see cref="Converter"/>.
+        /// </summary>
+        public object? ConverterParameter { get; set; }
+
+        /// <summary>
+        /// Gets or sets the name of the element to use as the binding source.
+        /// </summary>
+        public string? ElementName { get; set; }
+
+        /// <summary>
+        /// Gets or sets the value to use when the binding is unable to produce a value.
+        /// </summary>
+        public object? FallbackValue { get; set; } = AvaloniaProperty.UnsetValue;
+
+        /// <summary>
+        /// Gets or sets the binding mode.
+        /// </summary>
+        public BindingMode Mode { get; set; }
+
+        /// <summary>
+        /// Gets or sets the binding path.
+        /// </summary>
+        public string Path { get; set; } = "";
+
+        /// <summary>
+        /// Gets or sets the binding priority.
+        /// </summary>
+        public BindingPriority Priority { get; set; }
+
+        /// <summary>
+        /// Gets or sets the relative source for the binding.
+        /// </summary>
+        public RelativeSource? RelativeSource { get; set; }
+
+        /// <summary>
+        /// Gets or sets the source for the binding.
+        /// </summary>
+        public object? Source { get; set; } = AvaloniaProperty.UnsetValue;
+
+        /// <summary>
+        /// Gets or sets the string format.
+        /// </summary>
+        public string? StringFormat { get; set; }
+
+        /// <summary>
+        /// Gets or sets the value to use when the binding result is null.
+        /// </summary>
+        public object? TargetNullValue { get; set; } = AvaloniaProperty.UnsetValue;
+
+        /// <summary>
+        /// Gets or sets a value that determines the timing of binding source updates for
+        /// <see cref="BindingMode.TwoWay"/> and <see cref="BindingMode.OneWayToSource"/> bindings.
+        /// </summary>
+        public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
+
+        /// <summary>
+        /// Gets or sets a function used to resolve types from names in the binding path.
+        /// </summary>
+        public Func<string?, string, Type>? TypeResolver { get; set; }
+
+        internal WeakReference? DefaultAnchor { get; set; }
+        internal WeakReference<INameScope?>? NameScope { get; set; }
+
+        internal override BindingExpressionBase CreateInstance(
+            AvaloniaObject target,
+            AvaloniaProperty? targetProperty,
+            object? anchor)
+        {
+            List<ExpressionNode>? nodes = null;
+            var isRooted = false;
+            var enableDataValidation = targetProperty?.GetMetadata(target).EnableDataValidation ?? false;
+
+            // Build the expression nodes from the binding path.
+            if (!string.IsNullOrEmpty(Path))
+            {
+                var reader = new CharacterReader(Path.AsSpan());
+                var (astPool, sourceMode) = BindingExpressionGrammar.ParseToPooledList(ref reader);
+                nodes = ExpressionNodeFactory.CreateFromAst(
+                    astPool,
+                    TypeResolver,
+                    GetNameScope(),
+                    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 ??= new();
+                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;
+
+            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,
+                targetProperty: targetProperty,
+                targetNullValue: TargetNullValue,
+                targetTypeConverter: TargetTypeConverter.GetReflectionConverter(),
+                updateSourceTrigger: trigger);
+        }
+
+        private INameScope? GetNameScope()
+        {
+            INameScope? result = null;
+            NameScope?.TryGetTarget(out result);
+            return result;
+        }
+
+        private ExpressionNode? CreateSourceNode(AvaloniaProperty? targetProperty)
+        {
+            if (!string.IsNullOrEmpty(ElementName))
+            {
+                var nameScope = GetNameScope() ?? throw new InvalidOperationException(
+                    "Cannot create ElementName binding when NameScope is null");
+                return new NamedElementNode(nameScope, ElementName);
+            }
+
+            if (RelativeSource is not null)
+                return ExpressionNodeFactory.CreateRelativeSource(RelativeSource);
+
+            return ExpressionNodeFactory.CreateDataContext(targetProperty);
+        }
+
+        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);
+        }
+    }
+}

+ 0 - 0
src/Markup/Avalonia.Markup/Data/RelativeSource.cs → src/Avalonia.Base/Data/RelativeSource.cs


+ 0 - 21
src/Avalonia.Base/Data/TemplateBinding.Observable.cs

@@ -1,21 +0,0 @@
-using System;
-using Avalonia.Reactive;
-
-namespace Avalonia.Data
-{
-    // TODO12: Remove IAvaloniaSubject<object?> support from TemplateBinding.
-    public partial class TemplateBinding : IAvaloniaSubject<object?>
-    {
-        private IAvaloniaSubject<object?>? _observableAdapter;
-
-        public IDisposable Subscribe(IObserver<object?> observer)
-        {
-            _observableAdapter ??= ToObservable();
-            return _observableAdapter.Subscribe(observer);
-        }
-
-        void IObserver<object?>.OnCompleted() => _observableAdapter?.OnCompleted();
-        void IObserver<object?>.OnError(Exception error) => _observableAdapter?.OnError(error);
-        void IObserver<object?>.OnNext(object? value) => _observableAdapter?.OnNext(value);
-    }
-}

+ 10 - 199
src/Avalonia.Base/Data/TemplateBinding.cs

@@ -1,35 +1,21 @@
 using System;
 using System.ComponentModel;
-using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using Avalonia.Data.Converters;
-using Avalonia.Data.Core;
-using Avalonia.Logging;
 using Avalonia.Metadata;
-using Avalonia.Styling;
 
 namespace Avalonia.Data
 {
     /// <summary>
     /// A XAML binding to a property on a control's templated parent.
     /// </summary>
-    public partial class TemplateBinding : UntypedBindingExpressionBase,
-        IBinding,
-        IBinding2,
-        IDescription,
-        ISetterValue,
-        IDisposable
+    public sealed partial class TemplateBinding : BindingBase
     {
-        private bool _isSetterValue;
-        private bool _hasPublishedValue;
-
         public TemplateBinding()
-            : base(BindingPriority.Template)
         {
         }
 
         public TemplateBinding([InheritDataTypeFrom(InheritDataTypeFromScopeKind.ControlTemplate)] AvaloniaProperty property)
-            : base(BindingPriority.Template)
         {
             Property = property;
         }
@@ -57,11 +43,7 @@ namespace Avalonia.Data
         /// <summary>
         /// Gets or sets the binding mode.
         /// </summary>
-        public new BindingMode Mode 
-        { 
-            get => base.Mode;
-            set => base.Mode = value;
-        }
+        public BindingMode Mode { get; set; }
 
         /// <summary>
         /// Gets or sets the name of the source property on the templated parent.
@@ -69,190 +51,19 @@ namespace Avalonia.Data
         [InheritDataTypeFrom(InheritDataTypeFromScopeKind.ControlTemplate)]
         public AvaloniaProperty? Property { get; set; }
 
-        /// <inheritdoc/>
-        public override string Description => "TemplateBinding: " + Property;
-
-        public IBinding ProvideValue() => this;
-
-        public InstancedBinding? Initiate(
-            AvaloniaObject target,
-            AvaloniaProperty? targetProperty,
-            object? anchor = null,
-            bool enableDataValidation = false)
-        {
-            return new(target, InstanceCore(), Mode, BindingPriority.Template);
-        }
-
-        BindingExpressionBase IBinding2.Instance(AvaloniaObject target, AvaloniaProperty? property, object? anchor)
-        {
-            return InstanceCore();
-        }
-
-        internal override bool WriteValueToSource(object? value)
-        {
-            if (Property is not null && TryGetTemplatedParent(out var templatedParent))
-            {
-                if (Converter is not null)
-                    value = ConvertBack(Converter, ConverterCulture, ConverterParameter, value, TargetType);
-
-                if (value != BindingOperations.DoNothing)
-                    templatedParent.SetCurrentValue(Property, value);
-
-                return true;
-            }
+        public BindingBase ProvideValue() => this;
 
-            return false;
-        }
-
-        /// <inheritdoc/>
-        void ISetterValue.Initialize(SetterBase setter) => _isSetterValue = true;
-
-        protected override void StartCore()
-        {
-            _hasPublishedValue = false;
-            OnTemplatedParentChanged();
-            if (TryGetTarget(out var target))
-                target.PropertyChanged += OnTargetPropertyChanged;
-        }
-
-        protected override void StopCore()
-        {
-            if (TryGetTarget(out var target))
-            {
-                if (target is StyledElement targetElement &&
-                    targetElement?.TemplatedParent is { } templatedParent)
-                {
-                    templatedParent.PropertyChanged -= OnTemplatedParentPropertyChanged;
-                }
-
-                if (target is not null)
-                {
-                    target.PropertyChanged -= OnTargetPropertyChanged;
-                }
-            }
-        }
-
-        private object? ConvertToTargetType(object? value)
-        {
-            var converter = TargetTypeConverter.GetDefaultConverter();
-
-            if (converter.TryConvert(value, TargetType, CultureInfo.InvariantCulture, out var result))
-            {
-                return result;
-            }
-            else
-            {
-                if (TryGetTarget(out var target))
-                {
-                    var valueString = value?.ToString() ?? "(null)";
-                    var valueTypeName = value?.GetType().FullName ?? "null";
-                    var message = $"Could not convert '{valueString}' ({valueTypeName}) to '{TargetType}'.";
-                    Log(target, message, LogEventLevel.Warning);
-                }
-
-                return AvaloniaProperty.UnsetValue;
-            }
-        }
-
-        private TemplateBinding InstanceCore()
+        internal override BindingExpressionBase CreateInstance(AvaloniaObject target, AvaloniaProperty? targetProperty, object? anchor)
         {
             if (Mode is BindingMode.OneTime or BindingMode.OneWayToSource)
                 throw new NotSupportedException("TemplateBinding does not support OneTime or OneWayToSource bindings.");
 
-            // Usually each `TemplateBinding` will only be instantiated once; in this case we can
-            // use the `TemplateBinding` object itself as the binding expression in order to save
-            // allocating a new object.
-            //
-            // If the binding appears in a `Setter`, then make a clone and instantiate that because
-            // because the setter can outlive the control and cause a leak.
-            if (!_isSetterValue)
-            {
-                return this;
-            }
-            else
-            {
-                var clone = new TemplateBinding
-                {
-                    Converter = Converter,
-                    ConverterCulture = ConverterCulture,
-                    ConverterParameter = ConverterParameter,
-                    Mode = Mode,
-                    Property = Property,
-                };
-
-                return clone;
-            }
-        }
-
-        private void PublishValue()
-        {
-            if (Mode == BindingMode.OneWayToSource)
-                return;
-
-            if (TryGetTemplatedParent(out var templatedParent))
-            {
-                var value = Property is not null ?
-                    templatedParent.GetValue(Property) :
-                    templatedParent;
-                BindingError? error = null;
-
-                if (Converter is not null)
-                    value = Convert(Converter, ConverterCulture, ConverterParameter, value, TargetType, ref error);
-
-                value = ConvertToTargetType(value);
-                PublishValue(value, error);
-                _hasPublishedValue = true;
-
-                if (Mode == BindingMode.OneTime)
-                    Stop();
-            }
-            else if (_hasPublishedValue)
-            {
-                PublishValue(AvaloniaProperty.UnsetValue);
-            }
-        }
-
-        private void OnTemplatedParentChanged()
-        {
-            if (TryGetTemplatedParent(out var templatedParent))
-                templatedParent.PropertyChanged += OnTemplatedParentPropertyChanged;
-
-            PublishValue();
-        }
-
-        private void OnTemplatedParentPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
-        {
-            if (e.Property == Property)
-                PublishValue();
-        }
-
-        private void OnTargetPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
-        {
-            if (e.Property == StyledElement.TemplatedParentProperty)
-            {
-                if (e.OldValue is AvaloniaObject oldValue)
-                    oldValue.PropertyChanged -= OnTemplatedParentPropertyChanged;
-
-                OnTemplatedParentChanged();
-            }
-            else if (Mode is BindingMode.TwoWay or BindingMode.OneWayToSource && e.Property == TargetProperty)
-            {
-                WriteValueToSource(e.NewValue);
-            }
-        }
-
-        private bool TryGetTemplatedParent([NotNullWhen(true)] out AvaloniaObject? result)
-        {
-            if (TryGetTarget(out var target) &&
-                target is StyledElement targetElement &&
-                targetElement.TemplatedParent is { } templatedParent)
-            {
-                result = templatedParent;
-                return true;
-            }
-
-            result = null;
-            return false;
+            return new TemplateBindingExpression(
+                Property,
+                Converter,
+                ConverterCulture,
+                ConverterParameter,
+                Mode);
         }
     }
 }

+ 170 - 0
src/Avalonia.Base/Data/TemplateBindingExpression.cs

@@ -0,0 +1,170 @@
+using System;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using Avalonia.Data.Converters;
+using Avalonia.Data.Core;
+using Avalonia.Logging;
+
+namespace Avalonia.Data;
+
+internal class TemplateBindingExpression : UntypedBindingExpressionBase
+{
+    private IValueConverter? _converter;
+    private CultureInfo? _converterCulture;
+    private object? _converterParameter;
+    private BindingMode _mode;
+    private readonly AvaloniaProperty? _property;
+    private bool _hasPublishedValue;
+
+    public TemplateBindingExpression(
+        AvaloniaProperty? property,
+        IValueConverter? converter,
+        CultureInfo? converterCulture,
+        object? converterParameter,
+        BindingMode mode)
+        : base(BindingPriority.Template)
+    {
+        _property = property;
+        _converter = converter;
+        _converterCulture = converterCulture;
+        _converterParameter = converterParameter;
+        _mode = mode;
+    }
+
+    public override string Description => $"{{TemplateBinding {_property}}}";
+
+    protected override void StartCore()
+    {
+        _hasPublishedValue = false;
+        OnTemplatedParentChanged();
+        if (TryGetTarget(out var target))
+            target.PropertyChanged += OnTargetPropertyChanged;
+    }
+
+    protected override void StopCore()
+    {
+        if (TryGetTarget(out var target))
+        {
+            if (target is StyledElement targetElement &&
+                targetElement?.TemplatedParent is { } templatedParent)
+            {
+                templatedParent.PropertyChanged -= OnTemplatedParentPropertyChanged;
+            }
+
+            if (target is not null)
+            {
+                target.PropertyChanged -= OnTargetPropertyChanged;
+            }
+        }
+    }
+
+    internal override bool WriteValueToSource(object? value)
+    {
+        if (_property is not null && TryGetTemplatedParent(out var templatedParent))
+        {
+            if (_converter is not null)
+                value = ConvertBack(_converter, _converterCulture, _converterParameter, value, TargetType);
+
+            if (value != BindingOperations.DoNothing)
+                templatedParent.SetCurrentValue(_property, value);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    private object? ConvertToTargetType(object? value)
+    {
+        var converter = TargetTypeConverter.GetDefaultConverter();
+
+        if (converter.TryConvert(value, TargetType, CultureInfo.InvariantCulture, out var result))
+        {
+            return result;
+        }
+        else
+        {
+            if (TryGetTarget(out var target))
+            {
+                var valueString = value?.ToString() ?? "(null)";
+                var valueTypeName = value?.GetType().FullName ?? "null";
+                var message = $"Could not convert '{valueString}' ({valueTypeName}) to '{TargetType}'.";
+                Log(target, message, LogEventLevel.Warning);
+            }
+
+            return AvaloniaProperty.UnsetValue;
+        }
+    }
+
+    private void PublishValue()
+    {
+        if (_mode == BindingMode.OneWayToSource)
+            return;
+
+        if (TryGetTemplatedParent(out var templatedParent))
+        {
+            var value = _property is not null ?
+                templatedParent.GetValue(_property) :
+                templatedParent;
+            BindingError? error = null;
+
+            if (_converter is not null)
+                value = Convert(_converter, _converterCulture, _converterParameter, value, TargetType, ref error);
+
+            value = ConvertToTargetType(value);
+            PublishValue(value, error);
+            _hasPublishedValue = true;
+
+            if (_mode == BindingMode.OneTime)
+                Stop();
+        }
+        else if (_hasPublishedValue)
+        {
+            PublishValue(AvaloniaProperty.UnsetValue);
+        }
+    }
+
+    private void OnTemplatedParentChanged()
+    {
+        if (TryGetTemplatedParent(out var templatedParent))
+            templatedParent.PropertyChanged += OnTemplatedParentPropertyChanged;
+
+        PublishValue();
+    }
+
+    private void OnTemplatedParentPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
+    {
+        if (e.Property == _property)
+            PublishValue();
+    }
+
+    private void OnTargetPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
+    {
+        if (e.Property == StyledElement.TemplatedParentProperty)
+        {
+            if (e.OldValue is AvaloniaObject oldValue)
+                oldValue.PropertyChanged -= OnTemplatedParentPropertyChanged;
+
+            OnTemplatedParentChanged();
+        }
+        else if (_mode is BindingMode.TwoWay or BindingMode.OneWayToSource && e.Property == TargetProperty)
+        {
+            WriteValueToSource(e.NewValue);
+        }
+    }
+
+    private bool TryGetTemplatedParent([NotNullWhen(true)] out AvaloniaObject? result)
+    {
+        if (TryGetTarget(out var target) &&
+            target is StyledElement targetElement &&
+            targetElement.TemplatedParent is { } templatedParent)
+        {
+            result = templatedParent;
+            return true;
+        }
+
+        result = null;
+        return false;
+    }
+}

+ 1 - 1
src/Avalonia.Base/StyledElementExtensions.cs

@@ -6,7 +6,7 @@ namespace Avalonia
 {
     public static class StyledElementExtensions
     {
-        public static IDisposable BindClass(this StyledElement target, string className, IBinding source, object anchor) =>
+        public static IDisposable BindClass(this StyledElement target, string className, BindingBase source, object anchor) =>
             ClassBindingManager.Bind(target, className, source, anchor);
 
         public static AvaloniaProperty GetClassProperty(string className) =>

+ 3 - 5
src/Avalonia.Base/Styling/Setter.cs

@@ -79,10 +79,8 @@ namespace Avalonia.Styling
                 throw new InvalidOperationException(
                         $"Cannot set Class Binding property '(Classes.{classPropertyName})' in '{instance.Source}' because the style has an activator.");
 
-            if (Value is IBinding2 binding)
+            if (Value is BindingBase binding)
                 return SetBinding((StyleInstance)instance, ao, binding);
-            else if (Value is IBinding)
-                throw new AvaloniaInternalException("TODO: Make all IBindings implement IBinding2.");
             else if (Value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(Property.PropertyType))
                 return new PropertySetterTemplateInstance(Property, template);
             else if (!Property.IsValidValue(Value))
@@ -108,11 +106,11 @@ namespace Avalonia.Styling
             return Property ?? throw new InvalidOperationException("Setter.Property must be set.");
         }
 
-        private ISetterInstance SetBinding(StyleInstance instance, AvaloniaObject target, IBinding2 binding)
+        private ISetterInstance SetBinding(StyleInstance instance, AvaloniaObject target, BindingBase binding)
         {
             if (!Property!.IsDirect)
             {
-                var expression = binding.Instance(target, Property, null);
+                var expression = binding.CreateInstance(target, Property, null);
                 expression.Attach(target.GetValueStore(), null, target, Property, instance.Priority);
                 return expression;
             }

+ 2 - 2
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@@ -71,13 +71,13 @@
         <Link>Markup\AssetLoader.cs</Link>
       </Compile>
       <Compile Include="..\Avalonia.Base\Utilities\StringBuilderCache.cs" Link="Utilities\StringBuilderCache.cs" />
-      <Compile Include="..\Markup\Avalonia.Markup\Markup\Parsers\ArgumentListParser.cs">
+      <Compile Include="..\Avalonia.Base\Data\Core\Parsers\ArgumentListParser.cs">
         <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
       </Compile>
       <Compile Include="../Avalonia.Base/Utilities/KeywordParser.cs">
         <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
       </Compile>
-      <Compile Include="..\Markup\Avalonia.Markup\Markup\Parsers\BindingExpressionGrammar.cs">
+      <Compile Include="..\Avalonia.Base\Data\Core\Parsers\BindingExpressionGrammar.cs">
         <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
       </Compile>
       <Compile Include="../Avalonia.Base/Utilities/StyleClassParser.cs">

+ 1 - 1
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs

@@ -320,7 +320,7 @@ namespace Avalonia.Controls
         /// when binding to a collection property.</value>
         [AssignBinding]
         [InheritDataTypeFromItems(nameof(ItemsSource))]
-        public IBinding? ValueMemberBinding
+        public BindingBase? ValueMemberBinding
         {
             get => _valueBindingEvaluator?.ValueBinding;
             set

+ 4 - 4
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs

@@ -439,7 +439,7 @@ namespace Avalonia.Controls
             if (!_settingItemTemplateFromValueMemberBinding)
                 _itemTemplateIsFromValueMemberBinding = false;
         }
-        private void OnValueMemberBindingChanged(IBinding? value)
+        private void OnValueMemberBindingChanged(BindingBase? value)
         {
             if (_itemTemplateIsFromValueMemberBinding)
             {
@@ -2032,7 +2032,7 @@ namespace Avalonia.Controls
             /// <summary>
             /// Gets or sets the string value binding used by the control.
             /// </summary>
-            private IBinding? _binding;
+            private BindingBase? _binding;
 
             /// <summary>
             /// Identifies the Value dependency property.
@@ -2054,7 +2054,7 @@ namespace Avalonia.Controls
             /// <summary>
             /// Gets or sets the value binding.
             /// </summary>
-            public IBinding? ValueBinding
+            public BindingBase? ValueBinding
             {
                 get => _binding;
                 set
@@ -2076,7 +2076,7 @@ namespace Avalonia.Controls
             /// setting the initial binding to the provided parameter.
             /// </summary>
             /// <param name="binding">The initial string value binding.</param>
-            public BindingEvaluator(IBinding? binding)
+            public BindingEvaluator(BindingBase? binding)
                 : this()
             {
                 ValueBinding = binding;

+ 4 - 4
src/Avalonia.Controls/ComboBox.cs

@@ -656,15 +656,15 @@ namespace Avalonia.Controls
         private void HandleTextValueBindingValueChanged(AvaloniaPropertyChangedEventArgs? textSearchPropChange,
             AvaloniaPropertyChangedEventArgs? displayMemberPropChange)
         {
-            IBinding? textValueBinding;
+            BindingBase? textValueBinding;
             //prioritise using the TextSearch.TextBindingProperty if possible
-            if (textSearchPropChange == null && TextSearch.GetTextBinding(this) is IBinding textSearchBinding)
+            if (textSearchPropChange == null && TextSearch.GetTextBinding(this) is BindingBase textSearchBinding)
                 textValueBinding = textSearchBinding;
 
-            else if (textSearchPropChange != null && textSearchPropChange.NewValue is IBinding eventTextSearchBinding)
+            else if (textSearchPropChange != null && textSearchPropChange.NewValue is BindingBase eventTextSearchBinding)
                 textValueBinding = eventTextSearchBinding;
 
-            else if (displayMemberPropChange != null && displayMemberPropChange.NewValue is IBinding eventDisplayMemberBinding)
+            else if (displayMemberPropChange != null && displayMemberPropChange.NewValue is BindingBase eventDisplayMemberBinding)
                 textValueBinding = eventDisplayMemberBinding;
 
             else

+ 1 - 1
src/Avalonia.Controls/Control.cs

@@ -162,7 +162,7 @@ namespace Avalonia.Controls
             get => GetValue(TagProperty);
             set => SetValue(TagProperty, value);
         }
-        
+
         /// <summary>
         /// Occurs when the user has completed a context input gesture, such as a right-click.
         /// </summary>

+ 4 - 4
src/Avalonia.Controls/ItemsControl.cs

@@ -64,18 +64,18 @@ namespace Avalonia.Controls
         /// <summary>
         /// Defines the <see cref="DisplayMemberBinding" /> property
         /// </summary>
-        public static readonly StyledProperty<IBinding?> DisplayMemberBindingProperty =
-            AvaloniaProperty.Register<ItemsControl, IBinding?>(nameof(DisplayMemberBinding));
+        public static readonly StyledProperty<BindingBase?> DisplayMemberBindingProperty =
+            AvaloniaProperty.Register<ItemsControl, BindingBase?>(nameof(DisplayMemberBinding));
 
         private static readonly AttachedProperty<ControlTheme?> AppliedItemContainerTheme =
             AvaloniaProperty.RegisterAttached<ItemsControl, Control, ControlTheme?>("AppliedItemContainerTheme");
 
         /// <summary>
-        /// Gets or sets the <see cref="IBinding"/> to use for binding to the display member of each item.
+        /// Gets or sets the <see cref="BindingBase"/> to use for binding to the display member of each item.
         /// </summary>
         [AssignBinding]
         [InheritDataTypeFromItems(nameof(ItemsSource))]
-        public IBinding? DisplayMemberBinding
+        public BindingBase? DisplayMemberBinding
         {
             get => GetValue(DisplayMemberBindingProperty);
             set => SetValue(DisplayMemberBindingProperty, value);

+ 1 - 4
src/Avalonia.Controls/NativeMenuBarPresenter.cs

@@ -25,6 +25,7 @@ internal class NativeMenuBarPresenter : Menu
                     nativeItem.GetObservable(NativeMenuItem.HeaderProperty).ToBinding(),
                 [!MenuItem.IconProperty] = nativeItem.GetObservable(NativeMenuItem.IconProperty)
                     .Select(i => i is { } bitmap ? new Image { Source = bitmap } : null).ToBinding(),
+                [!!MenuItem.IsCheckedProperty] = nativeItem[!!NativeMenuItem.IsCheckedProperty],
                 [!MenuItem.IsEnabledProperty] = nativeItem.GetObservable(NativeMenuItem.IsEnabledProperty).ToBinding(),
                 [!MenuItem.IsVisibleProperty] = nativeItem.GetObservable(NativeMenuItem.IsVisibleProperty).ToBinding(),
                 [!MenuItem.CommandProperty] = nativeItem.GetObservable(NativeMenuItem.CommandProperty).ToBinding(),
@@ -38,10 +39,6 @@ internal class NativeMenuBarPresenter : Menu
                     nativeItem.GetObservable(NativeMenuItem.ToolTipProperty).ToBinding(),
             };
 
-            BindingOperations.Apply(newItem, MenuItem.IsCheckedProperty, InstancedBinding.TwoWay(
-                nativeItem.GetObservable(NativeMenuItem.IsCheckedProperty).Select(v => (object)v),
-                new AnonymousObserver<object?>(v => nativeItem.SetValue(NativeMenuItem.IsCheckedProperty, v))));
-
             newItem.Click += MenuItemOnClick;
 
             return newItem;

+ 2 - 3
src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs

@@ -123,10 +123,9 @@ namespace Avalonia.Controls.Primitives
             }
 
             if (headerTemplate is ITreeDataTemplate treeTemplate &&
-                treeTemplate.Match(item) &&
-                treeTemplate.ItemsSelector(item) is { } itemsBinding)
+                treeTemplate.Match(item))
             {
-                _itemsBinding = BindingOperations.Apply(this, ItemsSourceProperty, itemsBinding, null);
+                _itemsBinding = treeTemplate.BindChildren(this, ItemsSourceProperty, item);
             }
         }
 

+ 2 - 3
src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs

@@ -121,10 +121,9 @@ namespace Avalonia.Controls.Primitives
             }
 
             if (headerTemplate is ITreeDataTemplate treeTemplate &&
-                treeTemplate.Match(item) &&
-                treeTemplate.ItemsSelector(item) is { } itemsBinding)
+                treeTemplate.Match(item))
             {
-                _itemsBinding = BindingOperations.Apply(this, ItemsSourceProperty, itemsBinding, null);
+                _itemsBinding = treeTemplate.BindChildren(this, ItemsSourceProperty, item);
             }
         }
 

+ 6 - 6
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@@ -75,8 +75,8 @@ namespace Avalonia.Controls.Primitives
         /// <summary>
         /// Defines the <see cref="SelectedValueBinding"/> property
         /// </summary>
-        public static readonly StyledProperty<IBinding?> SelectedValueBindingProperty =
-            AvaloniaProperty.Register<SelectingItemsControl, IBinding?>(nameof(SelectedValueBinding));
+        public static readonly StyledProperty<BindingBase?> SelectedValueBindingProperty =
+            AvaloniaProperty.Register<SelectingItemsControl, BindingBase?>(nameof(SelectedValueBinding));
 
         /// <summary>
         /// Defines the <see cref="SelectedItems"/> property.
@@ -246,12 +246,12 @@ namespace Avalonia.Controls.Primitives
         }
 
         /// <summary>
-        /// Gets the <see cref="IBinding"/> instance used to obtain the 
+        /// Gets the <see cref="BindingBase"/> instance used to obtain the 
         /// <see cref="SelectedValue"/> property
         /// </summary>
         [AssignBinding]
         [InheritDataTypeFromItems(nameof(ItemsSource))]
-        public IBinding? SelectedValueBinding
+        public BindingBase? SelectedValueBinding
         {
             get => GetValue(SelectedValueBindingProperty);
             set => SetValue(SelectedValueBindingProperty, value);
@@ -648,7 +648,7 @@ namespace Avalonia.Controls.Primitives
                     return;
                 }
 
-                var value = change.GetNewValue<IBinding?>();
+                var value = change.GetNewValue<BindingBase?>();
                 if (value is null)
                 {
                     // Clearing SelectedValueBinding makes the SelectedValue the item itself
@@ -1413,7 +1413,7 @@ namespace Avalonia.Controls.Primitives
             return -1;
         }
 
-        private BindingEvaluator<object?> GetSelectedValueBindingEvaluator(IBinding binding)
+        private BindingEvaluator<object?> GetSelectedValueBindingEvaluator(BindingBase binding)
         {
             _selectedValueBindingEvaluator ??= new();
             _selectedValueBindingEvaluator.UpdateBinding(binding);

+ 4 - 4
src/Avalonia.Controls/Primitives/TextSearch.cs

@@ -21,8 +21,8 @@ namespace Avalonia.Controls.Primitives
         /// Defines the TextBinding attached property.
         /// The binding will be applied to each item during text search in <see cref="SelectingItemsControl"/> (such as <see cref="ComboBox"/>).
         /// </summary>
-        public static readonly AttachedProperty<IBinding?> TextBindingProperty
-            = AvaloniaProperty.RegisterAttached<Interactive, IBinding?>("TextBinding", typeof(TextSearch));
+        public static readonly AttachedProperty<BindingBase?> TextBindingProperty
+            = AvaloniaProperty.RegisterAttached<Interactive, BindingBase?>("TextBinding", typeof(TextSearch));
 
         // TODO12: Control should be Interactive to match the property definition.
         /// <summary>
@@ -47,7 +47,7 @@ namespace Avalonia.Controls.Primitives
         /// </summary>
         /// <param name="interactive">The interactive element.</param>
         /// <param name="value">The search text binding to set.</param>
-        public static void SetTextBinding(Interactive interactive, IBinding? value)
+        public static void SetTextBinding(Interactive interactive, BindingBase? value)
             => interactive.SetValue(TextBindingProperty, value);
 
         /// <summary>
@@ -56,7 +56,7 @@ namespace Avalonia.Controls.Primitives
         /// <param name="interactive">The interactive element.</param>
         /// <returns>The search text binding.</returns>
         [AssignBinding]
-        public static IBinding? GetTextBinding(Interactive interactive)
+        public static BindingBase? GetTextBinding(Interactive interactive)
             => interactive.GetValue(TextBindingProperty);
 
         /// <summary>

+ 4 - 7
src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections;
 using Avalonia.Data;
+using Avalonia.Reactive;
 
 namespace Avalonia.Controls.Templates
 {
@@ -51,14 +52,10 @@ namespace Avalonia.Controls.Templates
             _itemsSelector = itemsSelector;
         }
 
-        /// <summary>
-        /// Selects the child items of an item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>The child items, or null if no child items.</returns>
-        public InstancedBinding ItemsSelector(object item)
+        public IDisposable BindChildren(AvaloniaObject target, AvaloniaProperty targetProperty, object item)
         {
-            return InstancedBinding.OneTime(_itemsSelector(item));
+            target.SetCurrentValue(targetProperty, _itemsSelector(item));
+            return Disposable.Empty;
         }
 
         /// <summary>

+ 7 - 6
src/Avalonia.Controls/Templates/ITreeDataTemplate.cs

@@ -1,4 +1,4 @@
-using Avalonia.Data;
+using System;
 
 namespace Avalonia.Controls.Templates
 {
@@ -8,13 +8,14 @@ namespace Avalonia.Controls.Templates
     public interface ITreeDataTemplate : IDataTemplate
     {
         /// <summary>
-        /// Selects the child items of an item.
+        /// Binds the children of the specified item to a property on a target object.
         /// </summary>
-        /// <param name="item">The item.</param>
+        /// <param name="target">The target object.</param>
+        /// <param name="targetProperty">The target property.</param>
+        /// <param name="item">The item whose children should be bound.</param>
         /// <returns>
-        /// An <see cref="InstancedBinding"/> holding the items, or an observable that tracks the
-        /// items. May return null if no child items.
+        /// An <see cref="IDisposable"/> that can be used to remove the binding.
         /// </returns>
-        InstancedBinding? ItemsSelector(object item);
+        IDisposable BindChildren(AvaloniaObject target, AvaloniaProperty targetProperty, object item);
     }
 }

+ 3 - 3
src/Avalonia.Controls/Utils/BindingEvaluator.cs

@@ -10,7 +10,7 @@ namespace Avalonia.Controls.Utils;
 internal sealed class BindingEvaluator<T> : StyledElement, IDisposable
 {
     private BindingExpressionBase? _expression;
-    private IBinding? _lastBinding;
+    private BindingBase? _lastBinding;
 
     [SuppressMessage(
         "AvaloniaProperty",
@@ -37,7 +37,7 @@ internal sealed class BindingEvaluator<T> : StyledElement, IDisposable
         return GetValue(ValueProperty);
     }
 
-    public void UpdateBinding(IBinding binding)
+    public void UpdateBinding(BindingBase binding)
     {
         if (binding == _lastBinding)
             return;
@@ -59,7 +59,7 @@ internal sealed class BindingEvaluator<T> : StyledElement, IDisposable
     }
 
     [return: NotNullIfNotNull(nameof(binding))]
-    public static BindingEvaluator<T>? TryCreate(IBinding? binding)
+    public static BindingEvaluator<T>? TryCreate(BindingBase? binding)
     {
         if (binding is null)
             return null;

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using Avalonia.Data.Core;
-using Avalonia.Markup.Parsers;
+using Avalonia.Data.Core.Parsers;
 using XamlX;
 using XamlX.Ast;
 using XamlX.Transform;

+ 2 - 2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlClassesPropertyResolver.cs

@@ -74,12 +74,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
             {
                 _types = types;
                 _className = className;
-                Parameters = new[] {types.IBinding};
+                Parameters = new[] {types.BindingBase};
             }
             
             public void Emit(IXamlILEmitter emitter)
             {
-                using (var bloc = emitter.LocalsPool.GetLocal(_types.IBinding))
+                using (var bloc = emitter.LocalsPool.GetLocal(_types.BindingBase))
                     emitter
                         .Stloc(bloc.Local)
                         .Ldstr(_className)

+ 2 - 2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs

@@ -140,7 +140,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
             }
 
             IXamlType? itemsCollectionType = null;
-            if (context.GetAvaloniaTypes().IBinding.IsAssignableFrom(parentItemsValue.Type.GetClrType()))
+            if (context.GetAvaloniaTypes().BindingBase.IsAssignableFrom(parentItemsValue.Type.GetClrType()))
             {
                 if (parentItemsValue.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension)
                     && parentItemsValue is XamlMarkupExtensionNode ext && ext.Value is XamlAstConstructableObjectNode parentItemsBinding)
@@ -175,7 +175,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
 
         private static AvaloniaXamlIlDataContextTypeMetadataNode ParseDataContext(AstTransformationContext context, XamlAstConstructableObjectNode on, XamlAstConstructableObjectNode obj)
         {
-            var bindingType = context.GetAvaloniaTypes().IBinding;
+            var bindingType = context.GetAvaloniaTypes().BindingBase;
             if (!bindingType.IsAssignableFrom(obj.Type.GetClrType()) && !obj.Type.GetClrType().Equals(context.GetAvaloniaTypes().ReflectionBindingExtension))
             {
                 return new AvaloniaXamlIlDataContextTypeMetadataNode(on, obj.Type.GetClrType());

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs

@@ -168,7 +168,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
             {
                 Getter = setterType.Methods.First(m => m.Name == "get_Value");
                 var method = setterType.Methods.First(m => m.Name == "set_Value");
-                Setters.Add(new XamlIlDirectCallPropertySetter(method, types.IBinding, false));
+                Setters.Add(new XamlIlDirectCallPropertySetter(method, types.BindingBase, false));
                 Setters.Add(new XamlIlDirectCallPropertySetter(method, types.UnsetValueType, false));
                 Setters.Add(new XamlIlDirectCallPropertySetter(method, targetType, targetType.AcceptsNull()));
             }

+ 4 - 4
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@@ -21,7 +21,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
         public IXamlType StyledPropertyT { get; }
         public IXamlMethod AvaloniaObjectSetStyledPropertyValue { get; }
         public IXamlType AvaloniaAttachedPropertyT { get; }
-        public IXamlType IBinding { get; }
+        public IXamlType BindingBase { get; }
         public IXamlType MultiBinding { get; }
         public IXamlMethod AvaloniaObjectBindMethod { get; }
         public IXamlMethod AvaloniaObjectSetValueMethod { get; }
@@ -195,7 +195,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
                                  && m.Parameters.Count == 3
                                  && m.Parameters[0].Name == "StyledProperty`1"
                                  && m.Parameters[2].Equals(BindingPriority));
-            IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding");
+            BindingBase = cfg.TypeSystem.GetType("Avalonia.Data.BindingBase");
             MultiBinding = cfg.TypeSystem.GetType("Avalonia.Data.MultiBinding");
             IDisposable = cfg.TypeSystem.GetType("System.IDisposable");
             ICommand = cfg.TypeSystem.GetType("System.Windows.Input.ICommand");
@@ -213,7 +213,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
             OnExtensionType = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.On");
             AvaloniaObjectBindMethod = AvaloniaObjectExtensions.GetMethod("Bind", IDisposable, false, AvaloniaObject,
                 AvaloniaProperty,
-                IBinding, cfg.WellKnownTypes.Object);
+                BindingBase, cfg.WellKnownTypes.Object);
             UnsetValueType = cfg.TypeSystem.GetType("Avalonia.UnsetValueType");
             StyledElement = cfg.TypeSystem.GetType("Avalonia.StyledElement");
             INameScope = cfg.TypeSystem.GetType("Avalonia.Controls.INameScope");
@@ -299,7 +299,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
             ClassesBindMethod = cfg.TypeSystem.GetType("Avalonia.StyledElementExtensions")
                 .GetMethod("BindClass", IDisposable, false, StyledElement,
                 cfg.WellKnownTypes.String,
-                IBinding, cfg.WellKnownTypes.Object);
+                BindingBase, cfg.WellKnownTypes.Object);
 
             IBrush = cfg.TypeSystem.GetType("Avalonia.Media.IBrush");
             ImmutableSolidColorBrush = cfg.TypeSystem.GetType("Avalonia.Media.Immutable.ImmutableSolidColorBrush");

+ 4 - 4
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs

@@ -287,13 +287,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
                 AvaloniaXamlIlWellKnownTypes types,
                 IXamlType declaringType,
                 IXamlField avaloniaProperty)
-                : base(types, declaringType, avaloniaProperty, false, [types.IBinding])
+                : base(types, declaringType, avaloniaProperty, false, [types.BindingBase])
             {
             }
 
             public override void Emit(IXamlILEmitter emitter)
             {
-                using (var bloc = emitter.LocalsPool.GetLocal(Types.IBinding))
+                using (var bloc = emitter.LocalsPool.GetLocal(Types.BindingBase))
                     emitter
                         .Stloc(bloc.Local)
                         .Ldsfld(AvaloniaProperty)
@@ -325,13 +325,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
                 AvaloniaXamlIlWellKnownTypes types,
                 IXamlType declaringType,
                 IXamlField avaloniaProperty)
-                : base(types, declaringType, avaloniaProperty, false, [types.BindingPriority, types.IBinding])
+                : base(types, declaringType, avaloniaProperty, false, [types.BindingPriority, types.BindingBase])
             {
             }
 
             public override void Emit(IXamlILEmitter emitter)
             {
-                using (var bloc = emitter.LocalsPool.GetLocal(Types.IBinding))
+                using (var bloc = emitter.LocalsPool.GetLocal(Types.BindingBase))
                     emitter
                         .Stloc(bloc.Local)
                         .Pop() // ignore priority

+ 4 - 6
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs

@@ -3,18 +3,16 @@ using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Reflection.Emit;
-using Avalonia.Markup.Parsers;
+using Avalonia.Data.Core.Parsers;
 using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
+using XamlX;
 using XamlX.Ast;
+using XamlX.Emit;
+using XamlX.IL;
 using XamlX.Transform;
 using XamlX.Transform.Transformers;
 using XamlX.TypeSystem;
-using XamlX;
-using XamlX.Emit;
-using XamlX.IL;
-
 using XamlIlEmitContext = XamlX.Emit.XamlEmitContextWithLocals<XamlX.IL.IXamlILEmitter, XamlX.IL.XamlILNodeEmitResult>;
-using System.Xml.Linq;
 
 namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
 {

+ 92 - 15
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs

@@ -1,15 +1,17 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
 using Avalonia.Data;
+using Avalonia.Data.Converters;
 using Avalonia.Data.Core;
 using Avalonia.Data.Core.ExpressionNodes;
-using Avalonia.Diagnostics;
-using Avalonia.Markup.Parsers;
+using Avalonia.Data.Core.Parsers;
 using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
 
 namespace Avalonia.Markup.Xaml.MarkupExtensions
 {
-    public class CompiledBindingExtension : BindingBase
+    public sealed class CompiledBindingExtension : BindingBase
     {
         public CompiledBindingExtension()
         {
@@ -41,25 +43,81 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
             };
         }
 
+        /// <summary>
+        /// Gets or sets the amount of time, in milliseconds, to wait before updating the binding 
+        /// source after the value on the target changes.
+        /// </summary>
+        /// <remarks>
+        /// There is no delay when the source is updated via <see cref="UpdateSourceTrigger.LostFocus"/> 
+        /// or <see cref="BindingExpressionBase.UpdateSource"/>. Nor is there a delay when 
+        /// <see cref="BindingMode.OneWayToSource"/> is active and a new source object is provided.
+        /// </remarks>
+        public int Delay { get; set; }
+
+        /// <summary>
+        /// Gets or sets the <see cref="IValueConverter"/> to use.
+        /// </summary>
+        public IValueConverter? Converter { get; set; }
+
+        /// <summary>
+        /// Gets or sets the culture in which to evaluate the converter.
+        /// </summary>
+        /// <value>The default value is null.</value>
+        /// <remarks>
+        /// If this property is not set then <see cref="CultureInfo.CurrentCulture"/> will be used.
+        /// </remarks>
+        [TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
+        public CultureInfo? ConverterCulture { get; set; }
+
+        /// <summary>
+        /// Gets or sets a parameter to pass to <see cref="Converter"/>.
+        /// </summary>
+        public object? ConverterParameter { get; set; }
+
+        public Type? DataType { get; set; }
+
+        /// <summary>
+        /// Gets or sets the value to use when the binding is unable to produce a value.
+        /// </summary>
+        public object? FallbackValue { get; set; } = AvaloniaProperty.UnsetValue;
+
+        /// <summary>
+        /// Gets or sets the binding mode.
+        /// </summary>
+        public BindingMode Mode { get; set; }
+
         [ConstructorArgument("path")]
         public CompiledBindingPath Path { get; set; }
 
+        /// <summary>
+        /// Gets or sets the binding priority.
+        /// </summary>
+        public BindingPriority Priority { get; set; }
+
+        /// <summary>
+        /// Gets or sets the source for the binding.
+        /// </summary>
         public object? Source { get; set; } = AvaloniaProperty.UnsetValue;
 
-        public Type? DataType { get; set; }
+        /// <summary>
+        /// Gets or sets the string format.
+        /// </summary>
+        public string? StringFormat { get; set; }
 
-        [Obsolete(ObsoletionMessages.MayBeRemovedInAvalonia12)]
-        public override InstancedBinding? Initiate(
-            AvaloniaObject target,
-            AvaloniaProperty? targetProperty,
-            object? anchor = null,
-            bool enableDataValidation = false)
-        {
-            var expression = InstanceCore(target, targetProperty, anchor, enableDataValidation);
-            return new InstancedBinding(target, expression, Mode, Priority);
-        }
+        /// <summary>
+        /// Gets or sets the value to use when the binding result is null.
+        /// </summary>
+        public object? TargetNullValue { get; set; } = AvaloniaProperty.UnsetValue;
+
+        /// <summary>
+        /// Gets or sets a value that determines the timing of binding source updates for
+        /// <see cref="BindingMode.TwoWay"/> and <see cref="BindingMode.OneWayToSource"/> bindings.
+        /// </summary>
+        public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
+
+        internal WeakReference? DefaultAnchor { get; set; }
 
-        private protected override BindingExpressionBase Instance(
+        internal override BindingExpressionBase CreateInstance(
             AvaloniaObject target,
             AvaloniaProperty? targetProperty,
             object? anchor)
@@ -140,5 +198,24 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
                 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);
+        }
     }
 }

+ 0 - 3
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs

@@ -94,9 +94,6 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
         internal IEnumerable<ICompiledBindingPathElement> Elements => _elements;
 
-        internal SourceMode SourceMode => Array.Exists(_elements, e => e is IControlSourceBindingPathElement)
-            ? SourceMode.Control : SourceMode.Data;
-
         /// <inheritdoc />
         public override string ToString()
             => string.Concat((IEnumerable<ICompiledBindingPathElement>) _elements);

+ 3 - 15
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs

@@ -7,7 +7,7 @@ using Avalonia.Styling;
 
 namespace Avalonia.Markup.Xaml.MarkupExtensions
 {
-    public class DynamicResourceExtension : IBinding2
+    public sealed class DynamicResourceExtension : BindingBase
     {
         private object? _anchor;
         private BindingPriority _priority;
@@ -24,7 +24,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
 
         public object? ResourceKey { get; set; }
 
-        public IBinding ProvideValue(IServiceProvider serviceProvider)
+        public BindingBase ProvideValue(IServiceProvider serviceProvider)
         {
             if (serviceProvider.IsInControlTemplate())
                 _priority = BindingPriority.Template;
@@ -44,19 +44,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
             return this;
         }
 
-        InstancedBinding? IBinding.Initiate(
-            AvaloniaObject target,
-            AvaloniaProperty? targetProperty,
-            object? anchor,
-            bool enableDataValidation)
-        {
-            if (ResourceKey is null)
-                return null;
-            var expression = new DynamicResourceExpression(ResourceKey, _anchor, _themeVariant, _priority);
-            return new InstancedBinding(target, expression, BindingMode.OneWay, _priority);
-        }
-
-        BindingExpressionBase IBinding2.Instance(AvaloniaObject target, AvaloniaProperty? targetProperty, object? anchor)
+        internal override BindingExpressionBase CreateInstance(AvaloniaObject target, AvaloniaProperty? targetProperty, object? anchor)
         {
             if (ResourceKey is null)
                 throw new InvalidOperationException("DynamicResource must have a ResourceKey.");

+ 21 - 50
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs

@@ -1,10 +1,7 @@
-using Avalonia.Data;
 using System;
-using Avalonia.Controls;
-using Avalonia.Data.Converters;
 using System.Diagnostics.CodeAnalysis;
-using System.ComponentModel;
-using System.Globalization;
+using Avalonia.Controls;
+using Avalonia.Data;
 
 namespace Avalonia.Markup.Xaml.MarkupExtensions
 {
@@ -12,20 +9,29 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
 #if NET8_0_OR_GREATER
     [RequiresDynamicCode(TrimmingMessages.ReflectionBindingRequiresDynamicCodeMessage)]
 #endif
-    public class ReflectionBindingExtension
+    public sealed class ReflectionBindingExtension : ReflectionBinding
     {
-        public ReflectionBindingExtension()
-        {
-        }
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ReflectionBinding"/> class.
+        /// </summary>
+        public ReflectionBindingExtension() { }
 
-        public ReflectionBindingExtension(string path)
-        {
-            Path = path;
-        }
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ReflectionBinding"/> class.
+        /// </summary>
+        /// <param name="path">The binding path.</param>
+        public ReflectionBindingExtension(string path) : base(path) { }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ReflectionBinding"/> class.
+        /// </summary>
+        /// <param name="path">The binding path.</param>
+        /// <param name="mode">The binding mode.</param>
+        public ReflectionBindingExtension(string path, BindingMode mode) : base(path, mode) { }
 
-        public Binding ProvideValue(IServiceProvider serviceProvider)
+        public ReflectionBinding ProvideValue(IServiceProvider serviceProvider)
         {
-            return new Binding
+            return new ReflectionBinding
             {
                 TypeResolver = serviceProvider.ResolveType,
                 Converter = Converter,
@@ -46,40 +52,5 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
                 UpdateSourceTrigger = UpdateSourceTrigger,
             };
         }
-
-        /// <inheritdoc cref="BindingBase.Delay"/>
-        public int Delay { get; set; }
-
-        public IValueConverter? Converter { get; set; }
-
-        [TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
-        public CultureInfo? ConverterCulture { get; set; }
-
-        public object? ConverterParameter { get; set; }
-
-        public string? ElementName { get; set; }
-
-        public object? FallbackValue { get; set; } = AvaloniaProperty.UnsetValue;
-
-        public BindingMode Mode { get; set; }
-
-        [ConstructorArgument("path")]
-        public string Path { get; set; } = "";
-
-        public BindingPriority Priority { get; set; } = BindingPriority.LocalValue;
-
-        public object? Source { get; set; } = AvaloniaProperty.UnsetValue;
-
-        public string? StringFormat { get; set; }
-
-        public RelativeSource? RelativeSource { get; set; }
-
-        public object? TargetNullValue { get; set; } = AvaloniaProperty.UnsetValue;
-        
-        /// <summary>
-        /// Gets or sets a value that determines the timing of binding source updates for
-        /// <see cref="BindingMode.TwoWay"/> and <see cref="BindingMode.OneWayToSource"/> bindings.
-        /// </summary>
-        public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
     }
 }

+ 5 - 20
src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs

@@ -1,12 +1,9 @@
 using System;
-using System.Diagnostics.CodeAnalysis;
 using Avalonia.Controls;
 using Avalonia.Controls.Templates;
 using Avalonia.Data;
-using Avalonia.Data.Core;
-using Avalonia.Markup.Parsers;
-using Avalonia.Markup.Xaml.MarkupExtensions;
 using Avalonia.Metadata;
+using Avalonia.Reactive;
 
 namespace Avalonia.Markup.Xaml.Templates
 {
@@ -34,23 +31,11 @@ namespace Avalonia.Markup.Xaml.Templates
             }
         }
 
-        [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "If ItemsSource is a CompiledBinding, then path members will be preserved")]
-        [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "Dynamic code should be available if reflection bindings are used")]
-        public InstancedBinding? ItemsSelector(object item)
+        public IDisposable BindChildren(AvaloniaObject target, AvaloniaProperty targetProperty, object item)
         {
-            if (ItemsSource != null)
-            {
-                var expression = ItemsSource switch
-                {
-                    Binding reflection => reflection.CreateObservableForTreeDataTemplate(item),
-                    CompiledBindingExtension compiled => compiled.CreateObservableForTreeDataTemplate(item),
-                    _ => throw new InvalidOperationException("TreeDataTemplate currently only supports Binding and CompiledBindingExtension!")
-                };
-
-                return new InstancedBinding(null, expression, BindingMode.OneWay, BindingPriority.Style);
-            }
-
-            return null;
+            return ItemsSource is not null ?
+                target.Bind(targetProperty, ItemsSource) :
+                Disposable.Empty;
         }
 
         public Control? Build(object? data)

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs

@@ -283,7 +283,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
         public static void ApplyNonMatchingMarkupExtensionV1(object target, object property, IServiceProvider prov,
             object value)
         {
-            if (value is IBinding b)
+            if (value is BindingBase b)
             {
                 if (property is AvaloniaProperty p)
                     ((AvaloniaObject)target).Bind(p, b);

+ 0 - 1
src/Markup/Avalonia.Markup/Avalonia.Markup.csproj

@@ -5,7 +5,6 @@
   </PropertyGroup>
   <ItemGroup>
     <None Remove="Markup\Parsers\Nodes\ExpressionGrammer" />
-    <None Include="Markup\Parsers\BindingExpressionGrammar.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />

+ 13 - 206
src/Markup/Avalonia.Markup/Data/Binding.cs

@@ -1,214 +1,21 @@
-using System;
-using System.Collections.Generic;
+using System;
 using System.Diagnostics.CodeAnalysis;
-using Avalonia.Collections.Pooled;
-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;
 
-namespace Avalonia.Data
-{
-    /// <summary>
-    /// A XAML binding.
-    /// </summary>
-    [RequiresUnreferencedCode(TrimmingMessages.ReflectionBindingRequiresUnreferencedCodeMessage)]
-#if NET8_0_OR_GREATER
-    [RequiresDynamicCode(TrimmingMessages.ReflectionBindingRequiresDynamicCodeMessage)]
-#endif
-    public class Binding : BindingBase
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="Binding"/> class.
-        /// </summary>
-        public Binding()
-        {
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="Binding"/> class.
-        /// </summary>
-        /// <param name="path">The binding path.</param>
-        /// <param name="mode">The binding mode.</param>
-        public Binding(string path, BindingMode mode = BindingMode.Default)
-            : base(mode)
-        {
-            Path = path;
-        }
-
-        /// <summary>
-        /// Gets or sets the name of the element to use as the binding source.
-        /// </summary>
-        public string? ElementName { get; set; }
-
-        /// <summary>
-        /// Gets or sets the relative source for the binding.
-        /// </summary>
-        public RelativeSource? RelativeSource { get; set; }
+namespace Avalonia.Data;
 
-        /// <summary>
-        /// Gets or sets the source for the binding.
-        /// </summary>
-        public object? Source { get; set; } = AvaloniaProperty.UnsetValue;
-
-        /// <summary>
-        /// Gets or sets the binding path.
-        /// </summary>
-        public string Path { get; set; } = "";
-
-        /// <summary>
-        /// Gets or sets a function used to resolve types from names in the binding path.
-        /// </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, anchor, enableDataValidation);
-            return new InstancedBinding(target, expression, Mode, Priority);
-        }
-
-        private protected override BindingExpressionBase Instance(
-            AvaloniaObject target,
-            AvaloniaProperty? targetProperty,
-            object? anchor)
-        {
-            var enableDataValidation = targetProperty?.GetMetadata(target).EnableDataValidation ?? false;
-            return InstanceCore(targetProperty, target, anchor, 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
+/// <summary>
+/// Provides limited compatibility with the 11.x Binding class. Use <see cref="ReflectionBinding"/>
+/// for new code.
+/// </summary>
+[RequiresUnreferencedCode(TrimmingMessages.ReflectionBindingRequiresUnreferencedCodeMessage)]
 #if NET8_0_OR_GREATER
-    [RequiresDynamicCode(TrimmingMessages.ExpressionNodeRequiresDynamicCodeMessage)]
+    [RequiresDynamicCode(TrimmingMessages.ReflectionBindingRequiresDynamicCodeMessage)]
 #endif
-        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.");
-
-            List<ExpressionNode>? nodes = null;
-            var isRooted = false;
-
-            if (!string.IsNullOrEmpty(Path))
-            {
-                var reader = new CharacterReader(Path.AsSpan());
-                var (astNodes, sourceMode) = BindingExpressionGrammar.ParseToPooledList(ref reader);
-                nodes = ExpressionNodeFactory.CreateFromAst(
-                    astNodes,
-                    TypeResolver,
-                    GetNameScope(),
-                    out 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 UntypedBindingExpressionBase InstanceCore(
-            AvaloniaProperty? targetProperty, 
-            AvaloniaObject target,
-            object? anchor,
-            bool enableDataValidation)
-        {
-            List<ExpressionNode>? nodes = null;
-            var isRooted = false;
-
-            // Build the expression nodes from the binding path.
-            if (!string.IsNullOrEmpty(Path))
-            {
-                var reader = new CharacterReader(Path.AsSpan());
-                var (astPool, sourceMode) = BindingExpressionGrammar.ParseToPooledList(ref reader);
-                nodes = ExpressionNodeFactory.CreateFromAst(
-                    astPool,
-                    TypeResolver,
-                    GetNameScope(),
-                    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 ??= new();
-                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;
-
-            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,
-                targetProperty: targetProperty,
-                targetNullValue: TargetNullValue,
-                targetTypeConverter: TargetTypeConverter.GetReflectionConverter(),
-                updateSourceTrigger: trigger);
-        }
-
-        private INameScope? GetNameScope()
-        {
-            INameScope? result = null;
-            NameScope?.TryGetTarget(out result);
-            return result;
-        }
-
-        private ExpressionNode? CreateSourceNode(AvaloniaProperty? targetProperty)
-        {
-            if (!string.IsNullOrEmpty(ElementName))
-            {
-                var nameScope = GetNameScope() ?? throw new InvalidOperationException(
-                    "Cannot create ElementName binding when NameScope is null");
-                return new NamedElementNode(nameScope, ElementName);
-            }
+public class Binding : ReflectionBinding
+{
+    public Binding() { }
 
-            if (RelativeSource is not null)
-                return ExpressionNodeFactory.CreateRelativeSource(RelativeSource);
+    public Binding(string path) : base(path) { }
 
-            return ExpressionNodeFactory.CreateDataContext(targetProperty);
-        }
-    }
+    public Binding(string path, BindingMode mode) : base(path, mode) { }
 }

+ 0 - 137
src/Markup/Avalonia.Markup/Data/BindingBase.cs

@@ -1,137 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
-using Avalonia.Controls;
-using Avalonia.Data.Converters;
-using Avalonia.Data.Core;
-using Avalonia.Diagnostics;
-
-namespace Avalonia.Data
-{
-    public abstract class BindingBase : IBinding, IBinding2
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="Binding"/> class.
-        /// </summary>
-        public BindingBase()
-        {
-            FallbackValue = AvaloniaProperty.UnsetValue;
-            TargetNullValue = AvaloniaProperty.UnsetValue;
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="Binding"/> class.
-        /// </summary>
-        /// <param name="mode">The binding mode.</param>
-        public BindingBase(BindingMode mode = BindingMode.Default)
-            :this()
-        {
-            Mode = mode;
-        }
-
-        /// <summary>
-        /// Gets or sets the amount of time, in milliseconds, to wait before updating the binding 
-        /// source after the value on the target changes.
-        /// </summary>
-        /// <remarks>
-        /// There is no delay when the source is updated via <see cref="UpdateSourceTrigger.LostFocus"/> 
-        /// or <see cref="BindingExpressionBase.UpdateSource"/>. Nor is there a delay when 
-        /// <see cref="BindingMode.OneWayToSource"/> is active and a new source object is provided.
-        /// </remarks>
-        public int Delay { get; set; }
-
-        /// <summary>
-        /// Gets or sets the <see cref="IValueConverter"/> to use.
-        /// </summary>
-        public IValueConverter? Converter { get; set; }
-
-        /// <summary>
-        /// Gets or sets the culture in which to evaluate the converter.
-        /// </summary>
-        /// <value>The default value is null.</value>
-        /// <remarks>
-        /// If this property is not set then <see cref="CultureInfo.CurrentCulture"/> will be used.
-        /// </remarks>
-        [TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
-        public CultureInfo? ConverterCulture { get; set; }
-
-        /// <summary>
-        /// Gets or sets a parameter to pass to <see cref="Converter"/>.
-        /// </summary>
-        public object? ConverterParameter { get; set; }
-
-        /// <summary>
-        /// Gets or sets the value to use when the binding is unable to produce a value.
-        /// </summary>
-        public object? FallbackValue { get; set; }
-
-        /// <summary>
-        /// Gets or sets the value to use when the binding result is null.
-        /// </summary>
-        public object? TargetNullValue { get; set; }
-
-        /// <summary>
-        /// Gets or sets the binding mode.
-        /// </summary>
-        public BindingMode Mode { get; set; }
-
-        /// <summary>
-        /// Gets or sets the binding priority.
-        /// </summary>
-        public BindingPriority Priority { get; set; }
-
-        /// <summary>
-        /// Gets or sets the string format.
-        /// </summary>
-        public string? StringFormat { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value that determines the timing of binding source updates for
-        /// <see cref="BindingMode.TwoWay"/> and <see cref="BindingMode.OneWayToSource"/> bindings.
-        /// </summary>
-        public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
-
-        public WeakReference? DefaultAnchor { get; set; }
-
-        public WeakReference<INameScope?>? NameScope { get; set; }
-
-        /// <inheritdoc/>
-        [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.TypeConversionSupressWarningMessage)]
-        [Obsolete(ObsoletionMessages.MayBeRemovedInAvalonia12)]
-        public abstract InstancedBinding? Initiate(
-            AvaloniaObject target,
-            AvaloniaProperty? targetProperty,
-            object? anchor = null,
-            bool enableDataValidation = false);
-
-        private protected abstract BindingExpressionBase Instance(
-            AvaloniaObject target,
-            AvaloniaProperty? targetProperty,
-            object? anchor);
-
-        private protected (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);
-        }
-
-        BindingExpressionBase IBinding2.Instance(AvaloniaObject target, AvaloniaProperty? property, object? anchor)
-        {
-            return Instance(target, property, anchor);
-        }
-    }
-}

+ 3 - 3
src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs

@@ -29,7 +29,7 @@ namespace Avalonia.Markup.Data
         /// <param name="target">The control.</param>
         /// <param name="property">The property on the control to bind to.</param>
         /// <param name="binding">The binding.</param>
-        public static void Add(StyledElement target, AvaloniaProperty property, IBinding binding)
+        public static void Add(StyledElement target, AvaloniaProperty property, BindingBase binding)
         {
             if (target.IsInitialized)
             {
@@ -109,13 +109,13 @@ namespace Avalonia.Markup.Data
 
         private class BindingEntry : Entry
         {
-            public BindingEntry(AvaloniaProperty property, IBinding binding)
+            public BindingEntry(AvaloniaProperty property, BindingBase binding)
             {
                 Binding = binding;
                 Property = property;
             }
 
-            public IBinding Binding { get; }
+            public BindingBase Binding { get; }
             public AvaloniaProperty Property { get; }
 
             public override void Apply(StyledElement control)

+ 2 - 0
src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs

@@ -6,3 +6,5 @@ using Avalonia.Metadata;
 [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Data")]
 
 [assembly: TypeForwardedTo(typeof(CultureInfoIetfLanguageTagConverter))]
+[assembly: TypeForwardedTo(typeof(MultiBinding))]
+[assembly: TypeForwardedTo(typeof(RelativeSource))]

+ 4 - 0
src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj

@@ -16,6 +16,10 @@
     <Compile Include="..\..\..\src\Markup\Avalonia.Markup.Xaml.Loader\CompilerDynamicDependencies.cs" />
     <Compile Include="..\..\..\src\Avalonia.Base\Compatibility\NullableAttributes.cs" Link="Compatibility\NullableAttributes.cs" />
     <Compile Include="..\..\..\src\Avalonia.Base\Compatibility\TrimmingAttributes.cs" Link="Compatibility\TrimmingAttributes.cs" />
+    <Compile Include="..\..\..\src\Avalonia.Base\Data\Core\Parsers\ArgumentListParser.cs" Link="Parsers\ArgumentListParser.cs" />
+    <Compile Include="..\..\..\src\Avalonia.Base\Data\Core\Parsers\BindingExpressionGrammar.cs" Link="Parsers\BindingExpressionGrammar.cs" />
+    <Compile Include="..\..\..\src\Avalonia.Base\Utilities\CharacterReader.cs" Link="Utilities\CharacterReader.cs" />
+    <Compile Include="..\..\..\src\Avalonia.Base\Utilities\IdentifierParser.cs" Link="Utilities\IdentifierParser.cs" />
     <Compile Include="..\..\..\src\Avalonia.Base\Diagnostics\TrimmingMessages.cs" Link="Diagnostics/AvaloniaTrimmingMessages.cs" />
     <Compile Include="..\..\Shared\StringCompatibilityExtensions.cs" Link="Compatibility\StringCompatibilityExtensions.cs" />
     <Compile Include="..\..\..\src\Shared\IsExternalInit.cs" Link="Compatibility\IsExternalInit.cs" />

+ 0 - 19
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

@@ -1299,25 +1299,6 @@ namespace Avalonia.Base.UnitTests
                 AvaloniaProperty.Register<Class2, string>("Bar", "bardefault");
         }
 
-        private class TestOneTimeBinding : IBinding
-        {
-            private IObservable<object> _source;
-
-            public TestOneTimeBinding(IObservable<object> source)
-            {
-                _source = source;
-            }
-
-            public InstancedBinding Initiate(
-                AvaloniaObject target,
-                AvaloniaProperty? targetProperty,
-                object? anchor = null,
-                bool enableDataValidation = false)
-            {
-                return InstancedBinding.OneTime(_source);
-            }
-        }
-
         private class TestStackOverflowViewModel : INotifyPropertyChanged
         {
             public int SetterInvokedCount { get; private set; }

+ 0 - 51
tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Obsolete.cs

@@ -1,51 +0,0 @@
-using System.Reactive.Linq;
-using Avalonia.Data;
-using Avalonia.Markup.Xaml.MarkupExtensions;
-using Xunit;
-
-#nullable enable
-#pragma warning disable CS0618 // Type or member is obsolete
-
-namespace Avalonia.Base.UnitTests.Data.Core;
-
-public abstract partial class BindingExpressionTests
-{
-    public partial class Reflection
-    {
-        [Fact]
-        public void Obsolete_Initiate_Method_Produces_Observable_With_Correct_Target_Type()
-        {
-            // Issue #15081
-            var viewModel = new ViewModel { DoubleValue = 42.5 };
-            var target = new TargetClass { DataContext = viewModel };
-            var binding = new Binding(nameof(viewModel.DoubleValue));
-            var instanced = binding.Initiate(target, TargetClass.StringProperty);
-
-            Assert.NotNull(instanced);
-
-            var value = instanced.Observable.First();
-
-            Assert.Equal("42.5", value);
-        }
-    }
-
-    public partial class Compiled
-    {
-        [Fact]
-        public void Obsolete_Initiate_Method_Produces_Observable_With_Correct_Target_Type()
-        {
-            // Issue #15081
-            var viewModel = new ViewModel { DoubleValue = 42.5 };
-            var target = new TargetClass { DataContext = viewModel };
-            var path = CompiledBindingPathFromExpressionBuilder.Build<ViewModel, double>(x => x.DoubleValue, true);
-            var binding = new CompiledBindingExtension(path);
-            var instanced = binding.Initiate(target, TargetClass.StringProperty);
-
-            Assert.NotNull(instanced);
-
-            var value = instanced.Observable.First();
-
-            Assert.Equal("42.5", value);
-        }
-    }
-}

+ 1 - 1
tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs

@@ -8,7 +8,7 @@ using Avalonia.Data;
 using Avalonia.Data.Converters;
 using Avalonia.Data.Core;
 using Avalonia.Data.Core.ExpressionNodes;
-using Avalonia.Markup.Parsers;
+using Avalonia.Data.Core.Parsers;
 using Avalonia.UnitTests;
 using Avalonia.Utilities;
 

+ 2 - 2
tests/Avalonia.Markup.UnitTests/Parsers/BindingExpressionGrammarTests.cs → tests/Avalonia.Base.UnitTests/Data/Core/Parsers/BindingExpressionGrammarTests.cs

@@ -1,10 +1,10 @@
 using System.Collections.Generic;
-using Avalonia.Markup.Parsers;
+using Avalonia.Data.Core.Parsers;
 using Avalonia.UnitTests;
 using Avalonia.Utilities;
 using Xunit;
 
-namespace Avalonia.Markup.UnitTests.Parsers
+namespace Avalonia.Base.UnitTests.Data.Core.Parsers
 {
     public partial class BindingExpressionGrammarTests : ScopedTestBase
     {

+ 1 - 1
tests/Avalonia.Markup.UnitTests/Parsers/BindingExpressionGrammarTests_Errors.cs → tests/Avalonia.Base.UnitTests/Data/Core/Parsers/BindingExpressionGrammarTests_Errors.cs

@@ -1,7 +1,7 @@
 using Avalonia.Data.Core;
 using Xunit;
 
-namespace Avalonia.Markup.UnitTests.Parsers
+namespace Avalonia.Base.UnitTests.Data.Core.Parsers
 {
     public partial class BindingExpressionGrammarTests
     {

+ 2 - 2
tests/Avalonia.Benchmarks/Data/TemplateBinding_Setup.cs

@@ -26,7 +26,7 @@ public class TemplateBinding_Setup
         {
             // Explicit cast to IBinding is required to prevent the IObservable<object?>
             // overload being selected.
-            using var d = target.Bind(Control.TagProperty, (IBinding)binding);
+            using var d = target.Bind(Control.TagProperty, (BindingBase)binding);
         }
     }
 
@@ -40,7 +40,7 @@ public class TemplateBinding_Setup
         {
             // Explicit cast to IBinding is required to prevent the IObservable<object?>
             // overload being selected.
-            using var d = target.Bind(Control.TagProperty, (IBinding)binding);
+            using var d = target.Bind(Control.TagProperty, (BindingBase)binding);
         }
     }
 }

+ 2 - 2
tests/Avalonia.Benchmarks/Data/TemplateBinding_Values.cs

@@ -23,7 +23,7 @@ public class TemplateBinding_Values
 
         // Explicit cast to IBinding is required to prevent the IObservable<object?>
         // overload being selected.
-        using var d = target.Bind(Control.TagProperty, (IBinding)binding);
+        using var d = target.Bind(Control.TagProperty, (BindingBase)binding);
 
         for (var i = 0; i < 100; ++i)
         {
@@ -39,7 +39,7 @@ public class TemplateBinding_Values
 
         // Explicit cast to IBinding is required to prevent the IObservable<object?>
         // overload being selected.
-        using var d = target.Bind(Control.TagProperty, (IBinding)binding);
+        using var d = target.Bind(Control.TagProperty, (BindingBase)binding);
 
         for (var i = 0; i < 100; ++i)
         {

+ 2 - 2
tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs

@@ -1055,7 +1055,7 @@ namespace Avalonia.Controls.UnitTests
 
         private static ItemsControl CreateTarget(
             object? dataContext = null,
-            IBinding? displayMemberBinding = null,
+            BindingBase? displayMemberBinding = null,
             IList? items = null,
             IList? itemsSource = null,
             ControlTheme? itemContainerTheme = null,
@@ -1078,7 +1078,7 @@ namespace Avalonia.Controls.UnitTests
 
         private static T CreateTarget<T>(
             object? dataContext = null,
-            IBinding? displayMemberBinding = null,
+            BindingBase? displayMemberBinding = null,
             IList? items = null,
             IList? itemsSource = null,
             ControlTheme? itemContainerTheme = null,

+ 1 - 1
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs

@@ -293,7 +293,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
             var viewModel = new TestViewModel { Content = "foo" };
             var dataContexts = new List<object?>();
 
-            target.Bind(ContentPresenter.ContentProperty, (IBinding)new TemplateBinding(ContentControl.ContentProperty));
+            target.Bind(ContentPresenter.ContentProperty, (BindingBase)new TemplateBinding(ContentControl.ContentProperty));
             canvas.GetObservable(ContentPresenter.DataContextProperty).Subscribe(x => dataContexts.Add(x));
 
             host.DataTemplates.Add(new FuncDataTemplate<string>((_, __) => canvas));

+ 11 - 21
tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

@@ -1613,7 +1613,17 @@ namespace Avalonia.Controls.UnitTests
                 },
                 DataTemplates =
                 {
-                    new TestTreeDataTemplate(),
+                    new TreeDataTemplate
+                    {
+                        DataType = typeof(Node),
+                        ItemsSource = new Binding(nameof(Node.Children)),
+                        Content = (IServiceProvider? _) => new TemplateResult<Control>(
+                            new TextBlock
+                            {
+                                [!TextBlock.TextProperty] = new Binding(nameof(Node.Value)),
+                            },
+                            new NameScope())
+                    },
                 },
                 Child = child,
             };
@@ -1879,26 +1889,6 @@ namespace Avalonia.Controls.UnitTests
             public override string ToString() => Value ?? string.Empty;
         }
 
-        private class TestTreeDataTemplate : ITreeDataTemplate
-        {
-            public Control Build(object? param)
-            {
-                var node = (Node)param!;
-                return new TextBlock { Text = node.Value };
-            }
-
-            public InstancedBinding ItemsSelector(object item)
-            {
-                var obs = BindingExpression.Create(item, o => ((Node)o).Children);
-                return new InstancedBinding(obs, BindingMode.OneWay, BindingPriority.LocalValue);
-            }
-
-            public bool Match(object? data)
-            {
-                return data is Node;
-            }
-        }
-
         private class DerivedTreeView : TreeView
         {
         }

+ 5 - 4
tests/Avalonia.Markup.UnitTests/Data/BindingTests_Converters.cs

@@ -25,11 +25,12 @@ namespace Avalonia.Markup.UnitTests.Data
                 Converter = StringConverters.IsNullOrEmpty,
             };
 
-            var instancedBinding = target.Initiate(textBlock, TextBlock.TextProperty);
-            Assert.NotNull(instancedBinding);
-            var expressionObserver = Assert.IsType<BindingExpression>(instancedBinding.Expression);
+            var expression = (BindingExpression)target.CreateInstance(
+                textBlock,
+                TextBlock.TextProperty,
+                null);
 
-            Assert.Same(StringConverters.IsNullOrEmpty, expressionObserver.Converter);
+            Assert.Same(StringConverters.IsNullOrEmpty, expression.Converter);
         }
 
         [Fact]

+ 1 - 1
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionNodeFactoryTests.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using Avalonia.Data.Core.ExpressionNodes;
 using Avalonia.Data.Core.ExpressionNodes.Reflection;
-using Avalonia.Markup.Parsers;
+using Avalonia.Data.Core.Parsers;
 using Avalonia.UnitTests;
 using Avalonia.Utilities;
 using Xunit;

+ 1 - 1
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs

@@ -5,10 +5,10 @@ using System.Threading.Tasks;
 using Avalonia.Diagnostics;
 using Avalonia.Data.Core;
 using Xunit;
-using Avalonia.Markup.Parsers;
 using Avalonia.Utilities;
 using Avalonia.Data.Core.ExpressionNodes;
 using Avalonia.UnitTests;
+using Avalonia.Data.Core.Parsers;
 
 namespace Avalonia.Markup.UnitTests.Parsers
 {

+ 1 - 1
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AvaloniaProperty.cs

@@ -5,10 +5,10 @@ using System.Threading.Tasks;
 using Avalonia.Diagnostics;
 using Avalonia.Data.Core;
 using Xunit;
-using Avalonia.Markup.Parsers;
 using Avalonia.Utilities;
 using Avalonia.Data.Core.ExpressionNodes;
 using Avalonia.UnitTests;
+using Avalonia.Data.Core.Parsers;
 
 namespace Avalonia.Markup.UnitTests.Parsers
 {

+ 1 - 1
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Indexer.cs

@@ -7,8 +7,8 @@ using Avalonia.Collections;
 using Avalonia.Data;
 using Avalonia.Data.Core;
 using Avalonia.Data.Core.ExpressionNodes;
+using Avalonia.Data.Core.Parsers;
 using Avalonia.Diagnostics;
-using Avalonia.Markup.Parsers;
 using Avalonia.Threading;
 using Avalonia.UnitTests;
 using Avalonia.Utilities;

+ 1 - 1
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Method.cs

@@ -1,7 +1,6 @@
 using Avalonia.Data;
 using Avalonia.Data.Core;
 using Avalonia.Data.Core.ExpressionNodes;
-using Avalonia.Markup.Parsers;
 using Avalonia.Utilities;
 using System;
 using System.Collections.Generic;
@@ -11,6 +10,7 @@ using System.Text;
 using System.Threading.Tasks;
 using Avalonia.UnitTests;
 using Xunit;
+using Avalonia.Data.Core.Parsers;
 
 namespace Avalonia.Markup.UnitTests.Parsers
 {

+ 1 - 1
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
 using Avalonia.Data;
 using Avalonia.Data.Core;
 using Avalonia.Data.Core.ExpressionNodes;
-using Avalonia.Markup.Parsers;
+using Avalonia.Data.Core.Parsers;
 using Avalonia.UnitTests;
 using Avalonia.Utilities;
 using Xunit;

+ 1 - 1
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Property.cs

@@ -5,7 +5,7 @@ using System.Threading.Tasks;
 using Avalonia.Data;
 using Avalonia.Data.Core;
 using Avalonia.Data.Core.ExpressionNodes;
-using Avalonia.Markup.Parsers;
+using Avalonia.Data.Core.Parsers;
 using Avalonia.UnitTests;
 using Avalonia.Utilities;
 using Xunit;

+ 2 - 2
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@@ -2596,7 +2596,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
     public class AssignBindingControl : Control
     {
-        [AssignBinding] public IBinding? X { get; set; }
+        [AssignBinding] public BindingBase? X { get; set; }
     }
 
     public class DataGridLikeControl : Control
@@ -2622,7 +2622,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     {
         [AssignBinding]
         [InheritDataTypeFromItems(nameof(DataGridLikeControl.Items), AncestorType = typeof(DataGridLikeControl))]
-        public IBinding? Binding { get; set; }
+        public BindingBase? Binding { get; set; }
         
         [InheritDataTypeFromItems(nameof(DataGridLikeControl.Items), AncestorType = typeof(DataGridLikeControl))]
         public IDataTemplate? Template { get; set; }

+ 2 - 2
tests/Avalonia.Markup.Xaml.UnitTests/StyleTests.cs

@@ -19,7 +19,7 @@ namespace Avalonia.Markup.Xaml.UnitTests
                 var style = (Style)AvaloniaRuntimeXamlLoader.Load(xaml);
                 var setter = (Setter)(style.Setters.First());
 
-                Assert.IsType<Binding>(setter.Value);
+                Assert.IsType<ReflectionBinding>(setter.Value);
             }
         }
 
@@ -40,7 +40,7 @@ namespace Avalonia.Markup.Xaml.UnitTests
                      """);
                 var setter = (Setter)style.Setters.First();
 
-                Assert.IsType<Binding>(setter.Value);
+                Assert.IsType<ReflectionBinding>(setter.Value);
             }
         }
 

+ 5 - 5
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/AssignBindingTests.cs

@@ -48,15 +48,15 @@ public class AssignBindingTests : XamlTestBase
 public sealed class AssignBindingTestControl : Control
 {
     [AssignBinding]
-    public IBinding? ClrBinding { get; set; }
+    public BindingBase? ClrBinding { get; set; }
 
-    public static readonly AttachedProperty<IBinding?> AttachedBindingProperty =
-        AvaloniaProperty.RegisterAttached<AssignBindingTestControl, Control, IBinding?>("AttachedBinding");
+    public static readonly AttachedProperty<BindingBase?> AttachedBindingProperty =
+        AvaloniaProperty.RegisterAttached<AssignBindingTestControl, Control, BindingBase?>("AttachedBinding");
 
     [AssignBinding]
-    public static IBinding? GetAttachedBinding(Control obj)
+    public static BindingBase? GetAttachedBinding(Control obj)
         => obj.GetValue(AttachedBindingProperty);
 
-    public static void SetAttachedBinding(Control obj, IBinding? value)
+    public static void SetAttachedBinding(Control obj, BindingBase? value)
         => obj.SetValue(AttachedBindingProperty, value);
 }

+ 1 - 1
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@@ -568,7 +568,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
 
             Assert.Equal(BoolConverters.And, target.Converter);
 
-            var bindings = target.Bindings.Cast<Binding>().ToArray();
+            var bindings = target.Bindings.Cast<ReflectionBinding>().ToArray();
 
             Assert.Equal("Foo", bindings[0].Path);
             Assert.Equal("Bar", bindings[1].Path);

+ 1 - 1
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs

@@ -18,7 +18,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
                 var templates = (DataTemplates)AvaloniaRuntimeXamlLoader.Load(xaml);
                 var template = (TreeDataTemplate)(templates.First());
 
-                Assert.IsType<Binding>(template.ItemsSource);
+                Assert.IsType<ReflectionBinding>(template.ItemsSource);
             }                
         }