diff --git a/.ncrunch/Avalonia.Diagnostics.net6.0.v3.ncrunchproject b/.ncrunch/Avalonia.Diagnostics.net6.0.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.Diagnostics.net6.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Diagnostics.net8.0.v3.ncrunchproject b/.ncrunch/Avalonia.Diagnostics.net8.0.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.Diagnostics.net8.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Diagnostics.netstandard2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Diagnostics.netstandard2.0.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.Diagnostics.netstandard2.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index 84a9f4b163..58654f7d37 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -1,24 +1,84 @@ - + + + CP0001 + T:Avalonia.Data.IBinding + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0001 + T:Avalonia.Data.InstancedBinding + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + CP0001 T:Avalonia.Media.Fonts.FontFamilyLoader baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0001 + T:Avalonia.Data.BindingBase + baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.dll + + + CP0001 + T:Avalonia.Data.RelativeSourceMode + baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.dll + + + CP0001 + T:Avalonia.Data.TreeType + baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.dll + CP0001 T:Avalonia.Media.Fonts.FontFamilyLoader baseline/Avalonia/lib/net6.0/Avalonia.Base.dll current/Avalonia/lib/net6.0/Avalonia.Base.dll + + CP0001 + T:Avalonia.Data.IBinding + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0001 + T:Avalonia.Data.InstancedBinding + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0001 T:Avalonia.Media.Fonts.FontFamilyLoader baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0001 + T:Avalonia.Data.BindingBase + baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.dll + + + CP0001 + T:Avalonia.Data.RelativeSourceMode + baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.dll + + + CP0001 + T:Avalonia.Data.TreeType + baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.dll + CP0001 T:Avalonia.Media.Fonts.FontFamilyLoader @@ -31,6 +91,60 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.AvaloniaObject.Bind(Avalonia.AvaloniaProperty,Avalonia.Data.IBinding) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.AvaloniaObject.get_Item(Avalonia.Data.IndexerDescriptor) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.AvaloniaObjectExtensions.Bind(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,Avalonia.Data.IBinding,System.Object) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.AvaloniaObjectExtensions.ToBinding``1(System.IObservable{``0}) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.BindingOperations.Apply(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,Avalonia.Data.InstancedBinding,System.Object) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.BindingOperations.Apply(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,Avalonia.Data.InstancedBinding) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.TemplateBinding.Initiate(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,System.Object,System.Boolean) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.TemplateBinding.ProvideValue + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.TemplateBinding.Subscribe(System.IObserver{System.Object}) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + CP0002 M:Avalonia.Media.Fonts.FontCollectionBase.Initialize(Avalonia.Platform.IFontManagerImpl) @@ -55,6 +169,30 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.StyledElementExtensions.BindClass(Avalonia.StyledElement,System.String,Avalonia.Data.IBinding,System.Object) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + F:Avalonia.Controls.ItemsControl.DisplayMemberBindingProperty + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + F:Avalonia.Controls.Primitives.SelectingItemsControl.SelectedValueBindingProperty + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + F:Avalonia.Controls.Primitives.TextSearch.TextBindingProperty + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + CP0002 F:Avalonia.Controls.TextBlock.LetterSpacingProperty @@ -67,6 +205,24 @@ baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + CP0002 + M:Avalonia.Controls.AutoCompleteBox.BindingEvaluator`1.#ctor(Avalonia.Data.IBinding) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.AutoCompleteBox.BindingEvaluator`1.get_ValueBinding + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.AutoCompleteBox.get_ValueMemberBinding + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + CP0002 M:Avalonia.Controls.Design.CreatePreviewWithControl(System.Object) @@ -133,6 +289,72 @@ baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + CP0002 + M:Avalonia.Controls.ItemsControl.get_DisplayMemberBinding + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Primitives.SelectingItemsControl.get_SelectedValueBinding + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Primitives.TextSearch.GetTextBinding(Avalonia.Interactivity.Interactive) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Primitives.TextSearch.SetTextBinding(Avalonia.Interactivity.Interactive,Avalonia.Data.IBinding) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Templates.FuncTreeDataTemplate.ItemsSelector(System.Object) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Templates.ITreeDataTemplate.ItemsSelector(System.Object) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Data.MultiBinding.get_Bindings + baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.dll + + + CP0002 + M:Avalonia.Data.MultiBinding.Initiate(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,System.Object,System.Boolean) + baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.dll + + + CP0002 + M:Avalonia.Markup.Xaml.MarkupExtensions.DynamicResourceExtension.ProvideValue(System.IServiceProvider) + baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + + + CP0002 + M:Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension.ProvideValue(System.IServiceProvider) + baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + + + CP0002 + M:Avalonia.Markup.Xaml.Templates.TreeDataTemplate.ItemsSelector(System.Object) + baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + CP0002 F:Avalonia.Media.Fonts.FontCollectionBase._glyphTypefaceCache @@ -169,6 +391,60 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.AvaloniaObject.Bind(Avalonia.AvaloniaProperty,Avalonia.Data.IBinding) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.AvaloniaObject.get_Item(Avalonia.Data.IndexerDescriptor) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.AvaloniaObjectExtensions.Bind(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,Avalonia.Data.IBinding,System.Object) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.AvaloniaObjectExtensions.ToBinding``1(System.IObservable{``0}) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.BindingOperations.Apply(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,Avalonia.Data.InstancedBinding,System.Object) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.BindingOperations.Apply(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,Avalonia.Data.InstancedBinding) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.TemplateBinding.Initiate(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,System.Object,System.Boolean) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.TemplateBinding.ProvideValue + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.TemplateBinding.Subscribe(System.IObserver{System.Object}) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0002 M:Avalonia.Media.Fonts.FontCollectionBase.Initialize(Avalonia.Platform.IFontManagerImpl) @@ -193,6 +469,30 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.StyledElementExtensions.BindClass(Avalonia.StyledElement,System.String,Avalonia.Data.IBinding,System.Object) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + F:Avalonia.Controls.ItemsControl.DisplayMemberBindingProperty + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + F:Avalonia.Controls.Primitives.SelectingItemsControl.SelectedValueBindingProperty + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + F:Avalonia.Controls.Primitives.TextSearch.TextBindingProperty + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + CP0002 F:Avalonia.Controls.TextBlock.LetterSpacingProperty @@ -205,6 +505,24 @@ baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + CP0002 + M:Avalonia.Controls.AutoCompleteBox.BindingEvaluator`1.#ctor(Avalonia.Data.IBinding) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.AutoCompleteBox.BindingEvaluator`1.get_ValueBinding + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.AutoCompleteBox.get_ValueMemberBinding + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + CP0002 M:Avalonia.Controls.Design.CreatePreviewWithControl(System.Object) @@ -271,12 +589,78 @@ baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + CP0002 + M:Avalonia.Controls.ItemsControl.get_DisplayMemberBinding + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Primitives.SelectingItemsControl.get_SelectedValueBinding + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Primitives.TextSearch.GetTextBinding(Avalonia.Interactivity.Interactive) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Primitives.TextSearch.SetTextBinding(Avalonia.Interactivity.Interactive,Avalonia.Data.IBinding) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Templates.FuncTreeDataTemplate.ItemsSelector(System.Object) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Templates.ITreeDataTemplate.ItemsSelector(System.Object) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + CP0002 M:Avalonia.Dialogs.Internal.ManagedFileChooserFilterViewModel.#ctor(Avalonia.Platform.Storage.FilePickerFileType) baseline/Avalonia/lib/net8.0/Avalonia.Dialogs.dll current/Avalonia/lib/net8.0/Avalonia.Dialogs.dll + + CP0002 + M:Avalonia.Data.MultiBinding.get_Bindings + baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.dll + + + CP0002 + M:Avalonia.Data.MultiBinding.Initiate(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,System.Object,System.Boolean) + baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.dll + + + CP0002 + M:Avalonia.Markup.Xaml.MarkupExtensions.DynamicResourceExtension.ProvideValue(System.IServiceProvider) + baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + + + CP0002 + M:Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension.ProvideValue(System.IServiceProvider) + baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + + + CP0002 + M:Avalonia.Markup.Xaml.Templates.TreeDataTemplate.ItemsSelector(System.Object) + baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + CP0002 F:Avalonia.Media.Fonts.FontCollectionBase._glyphTypefaceCache @@ -331,6 +715,12 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0006 + M:Avalonia.Controls.Templates.ITreeDataTemplate.BindChildren(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,System.Object) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + CP0006 M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer) @@ -439,6 +829,12 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0006 + M:Avalonia.Controls.Templates.ITreeDataTemplate.BindChildren(Avalonia.AvaloniaObject,Avalonia.AvaloniaProperty,System.Object) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + CP0006 M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64) @@ -517,6 +913,114 @@ baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll + + CP0007 + T:Avalonia.Data.TemplateBinding + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0007 + T:Avalonia.Data.TemplateBinding + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0008 + T:Avalonia.Data.TemplateBinding + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0008 + T:Avalonia.Data.Binding + baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.dll + + + CP0008 + T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension + baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + + + CP0008 + T:Avalonia.Data.TemplateBinding + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0008 + T:Avalonia.Data.Binding + baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.dll + + + CP0008 + T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension + baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + + + CP0009 + T:Avalonia.Data.TemplateBinding + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0009 + T:Avalonia.Data.MultiBinding + baseline/Avalonia/lib/net10.0/Avalonia.Markup.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.dll + + + CP0009 + T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension + baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + + + CP0009 + T:Avalonia.Markup.Xaml.MarkupExtensions.DynamicResourceExtension + baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + + + CP0009 + T:Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension + baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + + + CP0009 + T:Avalonia.Data.TemplateBinding + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0009 + T:Avalonia.Data.MultiBinding + baseline/Avalonia/lib/net8.0/Avalonia.Markup.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.dll + + + CP0009 + T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension + baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + + + CP0009 + T:Avalonia.Markup.Xaml.MarkupExtensions.DynamicResourceExtension + baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + + + CP0009 + T:Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension + baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + CP0012 M:Avalonia.Media.Fonts.FontCollectionBase.get_Count @@ -637,4 +1141,4 @@ baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - \ No newline at end of file + diff --git a/src/Avalonia.Base/Animation/AnimatorKeyFrame.cs b/src/Avalonia.Base/Animation/AnimatorKeyFrame.cs index b7f1f08e89..5786119fb9 100644 --- a/src/Avalonia.Base/Animation/AnimatorKeyFrame.cs +++ b/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); } diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index cc93208e51..3303ed276e 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -98,7 +98,7 @@ namespace Avalonia /// Gets or sets a binding for a . /// /// The binding information. - 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 } /// - /// Binds a to an . + /// Binds a to an . /// /// The property. /// The binding. /// /// The binding expression which represents the binding instance on this object. /// - 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); /// - /// Binds a to an . + /// Binds a to an . /// /// The property. /// The binding. @@ -656,11 +656,9 @@ namespace Avalonia /// /// The binding expression which represents the binding instance on this object. /// - 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); diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs index 76138beb4d..7c4e67b5cc 100644 --- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs +++ b/src/Avalonia.Base/AvaloniaObjectExtensions.cs @@ -11,12 +11,12 @@ namespace Avalonia public static class AvaloniaObjectExtensions { /// - /// Converts an to an . + /// Converts an to an . /// /// The type produced by the observable. /// The observable - /// An . - public static IBinding ToBinding(this IObservable source) + /// An . + public static BindingBase ToBinding(this IObservable source) { return new BindingAdaptor( typeof(T).IsValueType @@ -228,7 +228,7 @@ namespace Avalonia } /// - /// Binds a property on an to an . + /// Binds a property on an to an . /// /// The object. /// The property to bind. @@ -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(action)); } - private class BindingAdaptor : IBinding2 + private class BindingAdaptor : BindingBase { private readonly IObservable _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); } diff --git a/src/Avalonia.Base/ClassBindingManager.cs b/src/Avalonia.Base/ClassBindingManager.cs index 35b5bc8863..51dac66bb0 100644 --- a/src/Avalonia.Base/ClassBindingManager.cs +++ b/src/Avalonia.Base/ClassBindingManager.cs @@ -12,7 +12,7 @@ namespace Avalonia private static readonly Dictionary s_RegisteredProperties = new Dictionary(); - 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); diff --git a/src/Avalonia.Base/Data/BindingBase.cs b/src/Avalonia.Base/Data/BindingBase.cs new file mode 100644 index 0000000000..c52ab34e88 --- /dev/null +++ b/src/Avalonia.Base/Data/BindingBase.cs @@ -0,0 +1,28 @@ +namespace Avalonia.Data; + +/// +/// Base class for the various types of binding supported by Avalonia. +/// +public abstract class BindingBase +{ + /// + /// Creates a from a binding. + /// + /// The target of the binding. + /// The target property of the binding. + /// + /// If is not a control, provides an anchor object from which to + /// locate a data context or other controls. + /// + /// + /// A newly instantiated . + /// + /// + /// This is a low-level method which returns a binding expression that is not yet connected to + /// a binding sink, and so is inactive. + /// + internal abstract BindingExpressionBase CreateInstance( + AvaloniaObject target, + AvaloniaProperty? targetProperty, + object? anchor); +} diff --git a/src/Avalonia.Base/Data/BindingExpressionBase.cs b/src/Avalonia.Base/Data/BindingExpressionBase.cs index 616f64d9d3..7e9ad9c370 100644 --- a/src/Avalonia.Base/Data/BindingExpressionBase.cs +++ b/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); diff --git a/src/Avalonia.Base/Data/BindingOperations.cs b/src/Avalonia.Base/Data/BindingOperations.cs index b0a97e5c00..fd28e53d4a 100644 --- a/src/Avalonia.Base/Data/BindingOperations.cs +++ b/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(); - /// - /// Applies an a property on an . - /// - /// The target object. - /// The property to bind. - /// The instanced binding. - /// An which can be used to cancel the binding. - [Obsolete(ObsoletionMessages.MayBeRemovedInAvalonia12)] - public static IDisposable Apply( - AvaloniaObject target, - AvaloniaProperty property, - 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 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 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."); - } - } - - /// - /// Applies an a property on an . - /// - /// The target object. - /// The property to bind. - /// The instanced binding. - /// Obsolete, unused. - /// An which can be used to cancel the binding. - [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); - } - /// /// Retrieves the 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 diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs index a51ee5bf8a..dc55184404 100644 --- a/src/Avalonia.Base/Data/Core/BindingExpression.cs +++ b/src/Avalonia.Base/Data/Core/BindingExpression.cs @@ -23,7 +23,7 @@ namespace Avalonia.Data.Core; /// A represents a untyped binding which has been /// instantiated on an object. /// -internal partial class BindingExpression : UntypedBindingExpressionBase, IDescription, IDisposable +internal class BindingExpression : UntypedBindingExpressionBase, IDescription, IDisposable { private static readonly List s_emptyExpressionNodes = new(); private readonly WeakReference? _source; diff --git a/src/Avalonia.Base/Data/Core/IBinding2.cs b/src/Avalonia.Base/Data/Core/IBinding2.cs deleted file mode 100644 index 1dcbc15b0c..0000000000 --- a/src/Avalonia.Base/Data/Core/IBinding2.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Avalonia.Data.Core; - -/// -/// Internal interface for instancing bindings on an . -/// -/// -/// 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. -/// -internal interface IBinding2 : IBinding -{ - BindingExpressionBase Instance( - AvaloniaObject target, - AvaloniaProperty? targetProperty, - object? anchor); -} diff --git a/src/Avalonia.Base/Data/Core/MultiBindingExpression.cs b/src/Avalonia.Base/Data/Core/MultiBindingExpression.cs index 4d0a612a7e..2e9cb0b9cf 100644 --- a/src/Avalonia.Base/Data/Core/MultiBindingExpression.cs +++ b/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 bindings, + IList 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}'."); diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ArgumentListParser.cs b/src/Avalonia.Base/Data/Core/Parsers/ArgumentListParser.cs similarity index 96% rename from src/Markup/Avalonia.Markup/Markup/Parsers/ArgumentListParser.cs rename to src/Avalonia.Base/Data/Core/Parsers/ArgumentListParser.cs index c3651594e9..a3ffdb4aa6 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/ArgumentListParser.cs +++ b/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 { diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs b/src/Avalonia.Base/Data/Core/Parsers/BindingExpressionGrammar.cs similarity index 99% rename from src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs rename to src/Avalonia.Base/Data/Core/Parsers/BindingExpressionGrammar.cs index 97ae40931e..c49468f1cd 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs +++ b/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 { diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionNodeFactory.cs b/src/Avalonia.Base/Data/Core/Parsers/ExpressionNodeFactory.cs similarity index 99% rename from src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionNodeFactory.cs rename to src/Avalonia.Base/Data/Core/Parsers/ExpressionNodeFactory.cs index 682e1a7067..f7eb2d537d 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionNodeFactory.cs +++ b/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 { /// /// Creates s from a . diff --git a/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs b/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs index 6b52bbe259..e45c88c457 100644 --- a/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs +++ b/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. /// /// - /// This method is mostly here for backwards compatibility with - /// 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 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 instantiated in a /// . 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 . diff --git a/src/Avalonia.Base/Data/IBinding.cs b/src/Avalonia.Base/Data/IBinding.cs deleted file mode 100644 index e000da18ff..0000000000 --- a/src/Avalonia.Base/Data/IBinding.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using Avalonia.Diagnostics; -using Avalonia.Metadata; - -namespace Avalonia.Data -{ - /// - /// Holds a binding that can be applied to a property on an object. - /// - [NotClientImplementable] - public interface IBinding - { - /// - /// Initiates the binding on a target object. - /// - /// The target instance. - /// The target property. May be null. - /// - /// An optional anchor from which to locate required context. When binding to objects that - /// are not in the logical tree, certain types of binding need an anchor into the tree in - /// order to locate named controls or resources. The parameter - /// can be used to provide this context. - /// - /// Whether data validation should be enabled. - /// - /// A or null if the binding could not be resolved. - /// - [Obsolete(ObsoletionMessages.MayBeRemovedInAvalonia12)] - InstancedBinding? Initiate( - AvaloniaObject target, - AvaloniaProperty? targetProperty, - object? anchor = null, - bool enableDataValidation = false); - } -} diff --git a/src/Avalonia.Base/Data/IndexerBinding.cs b/src/Avalonia.Base/Data/IndexerBinding.cs index d12beb114b..c1cfb5a347 100644 --- a/src/Avalonia.Base/Data/IndexerBinding.cs +++ b/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); } diff --git a/src/Avalonia.Base/Data/InstancedBinding.cs b/src/Avalonia.Base/Data/InstancedBinding.cs deleted file mode 100644 index f174be7f40..0000000000 --- a/src/Avalonia.Base/Data/InstancedBinding.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System; -using System.ComponentModel; -using Avalonia.Data.Core; -using Avalonia.Reactive; -using ObservableEx = Avalonia.Reactive.Observable; - -namespace Avalonia.Data -{ - /// - /// Holds the result of calling . - /// - /// - /// Whereas an 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 - /// on a target object. - /// - public sealed class InstancedBinding - { - private readonly AvaloniaObject? _target; - private readonly UntypedBindingExpressionBase? _expression; - private IObservable? _observable; - - /// - /// Initializes a new instance of the class. - /// - /// The binding source. - /// The binding mode. - /// The priority of the binding. - /// - /// This constructor can be used to create any type of binding and as such requires an - /// 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. - /// - internal InstancedBinding(IObservable 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; - } - - /// - /// Gets the binding mode with which the binding was initiated. - /// - public BindingMode Mode { get; } - - /// - /// Gets the binding priority. - /// - public BindingPriority Priority { get; } - - /// - /// Gets the binding source observable. - /// - public IObservable Source => _observable ??= _expression!.ToObservable(_target); - - [Obsolete("Use Source property"), EditorBrowsable(EditorBrowsableState.Never)] - public IObservable Observable => Source; - - internal UntypedBindingExpressionBase? Expression => _expression; - - /// - /// Creates a new one-time binding with a fixed value. - /// - /// The value. - /// The priority of the binding. - /// An instance. - public static InstancedBinding OneTime( - object value, - BindingPriority priority = BindingPriority.LocalValue) - { - return new InstancedBinding(ObservableEx.SingleValue(value), BindingMode.OneTime, priority); - } - - /// - /// Creates a new one-time binding. - /// - /// The source observable. - /// The priority of the binding. - /// An instance. - public static InstancedBinding OneTime( - IObservable observable, - BindingPriority priority = BindingPriority.LocalValue) - { - _ = observable ?? throw new ArgumentNullException(nameof(observable)); - - return new InstancedBinding(observable, BindingMode.OneTime, priority); - } - - /// - /// Creates a new one-way binding. - /// - /// The source observable. - /// The priority of the binding. - /// An instance. - public static InstancedBinding OneWay( - IObservable observable, - BindingPriority priority = BindingPriority.LocalValue) - { - _ = observable ?? throw new ArgumentNullException(nameof(observable)); - - return new InstancedBinding(observable, BindingMode.OneWay, priority); - } - - /// - /// Creates a new one-way to source binding. - /// - /// The binding source. - /// The priority of the binding. - /// An instance. - public static InstancedBinding OneWayToSource( - IObserver observer, - BindingPriority priority = BindingPriority.LocalValue) - { - _ = observer ?? throw new ArgumentNullException(nameof(observer)); - - return new InstancedBinding((IObservable)observer, BindingMode.OneWayToSource, priority); - } - - /// - /// Creates a new two-way binding. - /// - /// The binding source. - /// The binding source. - /// The priority of the binding. - /// An instance. - public static InstancedBinding TwoWay( - IObservable observable, - IObserver observer, - BindingPriority priority = BindingPriority.LocalValue) - { - _ = observable ?? throw new ArgumentNullException(nameof(observable)); - _ = observer ?? throw new ArgumentNullException(nameof(observer)); - - var subject = observable == observer ? observable : new CombinedSubject(observer, observable); - return new InstancedBinding(subject, BindingMode.TwoWay, priority); - } - - /// - /// Creates a copy of the with a different priority. - /// - /// The priority of the binding. - /// An instance. - public InstancedBinding WithPriority(BindingPriority priority) - { - return new InstancedBinding(Source, Mode, priority); - } - } -} diff --git a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs b/src/Avalonia.Base/Data/MultiBinding.cs similarity index 85% rename from src/Markup/Avalonia.Markup/Data/MultiBinding.cs rename to src/Avalonia.Base/Data/MultiBinding.cs index f894300a07..37ea8ec368 100644 --- a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs +++ b/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 { /// /// A XAML binding that calculates an aggregate value from multiple child . /// - public class MultiBinding : IBinding2 + public sealed class MultiBinding : BindingBase { /// /// Gets the collection of child bindings. /// [Content, AssignBinding] - public IList Bindings { get; set; } = new List(); + public IList Bindings { get; set; } = new List(); /// /// Gets or sets the to use. @@ -77,18 +75,7 @@ namespace Avalonia.Data TargetNullValue = AvaloniaProperty.UnsetValue; } - /// - 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) diff --git a/src/Avalonia.Base/Data/ReflectionBinding.cs b/src/Avalonia.Base/Data/ReflectionBinding.cs new file mode 100644 index 0000000000..1209d47a2e --- /dev/null +++ b/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 +{ + /// + /// A binding that uses reflection to access members. + /// + [RequiresUnreferencedCode(TrimmingMessages.ReflectionBindingRequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(TrimmingMessages.ReflectionBindingRequiresDynamicCodeMessage)] + public class ReflectionBinding : BindingBase + { + /// + /// Initializes a new instance of the class. + /// + public ReflectionBinding() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The binding path. + public ReflectionBinding(string path) + { + Path = path; + } + + /// + /// Initializes a new instance of the class. + /// + /// The binding path. + /// The binding mode. + public ReflectionBinding(string path, BindingMode mode) + { + Path = path; + Mode = mode; + } + + /// + /// Gets or sets the amount of time, in milliseconds, to wait before updating the binding + /// source after the value on the target changes. + /// + /// + /// There is no delay when the source is updated via + /// or . Nor is there a delay when + /// is active and a new source object is provided. + /// + public int Delay { get; set; } + + /// + /// Gets or sets the to use. + /// + public IValueConverter? Converter { get; set; } + + /// + /// Gets or sets the culture in which to evaluate the converter. + /// + /// The default value is null. + /// + /// If this property is not set then will be used. + /// + [TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))] + public CultureInfo? ConverterCulture { get; set; } + + /// + /// Gets or sets a parameter to pass to . + /// + public object? ConverterParameter { get; set; } + + /// + /// Gets or sets the name of the element to use as the binding source. + /// + public string? ElementName { get; set; } + + /// + /// Gets or sets the value to use when the binding is unable to produce a value. + /// + public object? FallbackValue { get; set; } = AvaloniaProperty.UnsetValue; + + /// + /// Gets or sets the binding mode. + /// + public BindingMode Mode { get; set; } + + /// + /// Gets or sets the binding path. + /// + public string Path { get; set; } = ""; + + /// + /// Gets or sets the binding priority. + /// + public BindingPriority Priority { get; set; } + + /// + /// Gets or sets the relative source for the binding. + /// + public RelativeSource? RelativeSource { get; set; } + + /// + /// Gets or sets the source for the binding. + /// + public object? Source { get; set; } = AvaloniaProperty.UnsetValue; + + /// + /// Gets or sets the string format. + /// + public string? StringFormat { get; set; } + + /// + /// Gets or sets the value to use when the binding result is null. + /// + public object? TargetNullValue { get; set; } = AvaloniaProperty.UnsetValue; + + /// + /// Gets or sets a value that determines the timing of binding source updates for + /// and bindings. + /// + public UpdateSourceTrigger UpdateSourceTrigger { get; set; } + + /// + /// Gets or sets a function used to resolve types from names in the binding path. + /// + public Func? TypeResolver { get; set; } + + internal WeakReference? DefaultAnchor { get; set; } + internal WeakReference? NameScope { get; set; } + + internal override BindingExpressionBase CreateInstance( + AvaloniaObject target, + AvaloniaProperty? targetProperty, + object? anchor) + { + List? 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); + } + } +} diff --git a/src/Markup/Avalonia.Markup/Data/RelativeSource.cs b/src/Avalonia.Base/Data/RelativeSource.cs similarity index 100% rename from src/Markup/Avalonia.Markup/Data/RelativeSource.cs rename to src/Avalonia.Base/Data/RelativeSource.cs diff --git a/src/Avalonia.Base/Data/TemplateBinding.Observable.cs b/src/Avalonia.Base/Data/TemplateBinding.Observable.cs deleted file mode 100644 index 21bf4430b6..0000000000 --- a/src/Avalonia.Base/Data/TemplateBinding.Observable.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Avalonia.Reactive; - -namespace Avalonia.Data -{ - // TODO12: Remove IAvaloniaSubject support from TemplateBinding. - public partial class TemplateBinding : IAvaloniaSubject - { - private IAvaloniaSubject? _observableAdapter; - - public IDisposable Subscribe(IObserver observer) - { - _observableAdapter ??= ToObservable(); - return _observableAdapter.Subscribe(observer); - } - - void IObserver.OnCompleted() => _observableAdapter?.OnCompleted(); - void IObserver.OnError(Exception error) => _observableAdapter?.OnError(error); - void IObserver.OnNext(object? value) => _observableAdapter?.OnNext(value); - } -} diff --git a/src/Avalonia.Base/Data/TemplateBinding.cs b/src/Avalonia.Base/Data/TemplateBinding.cs index 2b0e054c07..428f9c4a11 100644 --- a/src/Avalonia.Base/Data/TemplateBinding.cs +++ b/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 { /// /// A XAML binding to a property on a control's templated parent. /// - 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 /// /// Gets or sets the binding mode. /// - public new BindingMode Mode - { - get => base.Mode; - set => base.Mode = value; - } + public BindingMode Mode { get; set; } /// /// 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; } - /// - 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; - } - - /// - 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); } } } diff --git a/src/Avalonia.Base/Data/TemplateBindingExpression.cs b/src/Avalonia.Base/Data/TemplateBindingExpression.cs new file mode 100644 index 0000000000..de6b373eec --- /dev/null +++ b/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; + } +} diff --git a/src/Avalonia.Base/StyledElementExtensions.cs b/src/Avalonia.Base/StyledElementExtensions.cs index e68664a503..beea29d7f7 100644 --- a/src/Avalonia.Base/StyledElementExtensions.cs +++ b/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) => diff --git a/src/Avalonia.Base/Styling/Setter.cs b/src/Avalonia.Base/Styling/Setter.cs index f2f01945e2..9beba10ed6 100644 --- a/src/Avalonia.Base/Styling/Setter.cs +++ b/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; } diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index 9d89eaf71c..65929f1357 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -71,13 +71,13 @@ Markup\AssetLoader.cs - + Markup/%(RecursiveDir)%(FileName)%(Extension) Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs index a667087c37..c6eab7901e 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs @@ -320,7 +320,7 @@ namespace Avalonia.Controls /// when binding to a collection property. [AssignBinding] [InheritDataTypeFromItems(nameof(ItemsSource))] - public IBinding? ValueMemberBinding + public BindingBase? ValueMemberBinding { get => _valueBindingEvaluator?.ValueBinding; set diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs index d2fa2bea2b..ffa95285e5 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs +++ b/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 /// /// Gets or sets the string value binding used by the control. /// - private IBinding? _binding; + private BindingBase? _binding; /// /// Identifies the Value dependency property. @@ -2054,7 +2054,7 @@ namespace Avalonia.Controls /// /// Gets or sets the value binding. /// - public IBinding? ValueBinding + public BindingBase? ValueBinding { get => _binding; set @@ -2076,7 +2076,7 @@ namespace Avalonia.Controls /// setting the initial binding to the provided parameter. /// /// The initial string value binding. - public BindingEvaluator(IBinding? binding) + public BindingEvaluator(BindingBase? binding) : this() { ValueBinding = binding; diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index 481eb7f108..c4af8467c0 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/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 diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index b5f3ba57ce..86afcd06b9 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -162,7 +162,7 @@ namespace Avalonia.Controls get => GetValue(TagProperty); set => SetValue(TagProperty, value); } - + /// /// Occurs when the user has completed a context input gesture, such as a right-click. /// diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index acc6f4762d..0c3e908bbf 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -64,18 +64,18 @@ namespace Avalonia.Controls /// /// Defines the property /// - public static readonly StyledProperty DisplayMemberBindingProperty = - AvaloniaProperty.Register(nameof(DisplayMemberBinding)); + public static readonly StyledProperty DisplayMemberBindingProperty = + AvaloniaProperty.Register(nameof(DisplayMemberBinding)); private static readonly AttachedProperty AppliedItemContainerTheme = AvaloniaProperty.RegisterAttached("AppliedItemContainerTheme"); /// - /// Gets or sets the to use for binding to the display member of each item. + /// Gets or sets the to use for binding to the display member of each item. /// [AssignBinding] [InheritDataTypeFromItems(nameof(ItemsSource))] - public IBinding? DisplayMemberBinding + public BindingBase? DisplayMemberBinding { get => GetValue(DisplayMemberBindingProperty); set => SetValue(DisplayMemberBindingProperty, value); diff --git a/src/Avalonia.Controls/NativeMenuBarPresenter.cs b/src/Avalonia.Controls/NativeMenuBarPresenter.cs index f071afcc3b..286f80d699 100644 --- a/src/Avalonia.Controls/NativeMenuBarPresenter.cs +++ b/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(v => nativeItem.SetValue(NativeMenuItem.IsCheckedProperty, v)))); - newItem.Click += MenuItemOnClick; return newItem; diff --git a/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs b/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs index a95280e640..3bdcdfd24e 100644 --- a/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs +++ b/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); } } diff --git a/src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs index 210cbdff19..af2f4178be 100644 --- a/src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs +++ b/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); } } diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index ce82d08671..d78d6e07c2 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -75,8 +75,8 @@ namespace Avalonia.Controls.Primitives /// /// Defines the property /// - public static readonly StyledProperty SelectedValueBindingProperty = - AvaloniaProperty.Register(nameof(SelectedValueBinding)); + public static readonly StyledProperty SelectedValueBindingProperty = + AvaloniaProperty.Register(nameof(SelectedValueBinding)); /// /// Defines the property. @@ -246,12 +246,12 @@ namespace Avalonia.Controls.Primitives } /// - /// Gets the instance used to obtain the + /// Gets the instance used to obtain the /// property /// [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(); + var value = change.GetNewValue(); if (value is null) { // Clearing SelectedValueBinding makes the SelectedValue the item itself @@ -1413,7 +1413,7 @@ namespace Avalonia.Controls.Primitives return -1; } - private BindingEvaluator GetSelectedValueBindingEvaluator(IBinding binding) + private BindingEvaluator GetSelectedValueBindingEvaluator(BindingBase binding) { _selectedValueBindingEvaluator ??= new(); _selectedValueBindingEvaluator.UpdateBinding(binding); diff --git a/src/Avalonia.Controls/Primitives/TextSearch.cs b/src/Avalonia.Controls/Primitives/TextSearch.cs index d7dd4d9341..5099567630 100644 --- a/src/Avalonia.Controls/Primitives/TextSearch.cs +++ b/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 (such as ). /// - public static readonly AttachedProperty TextBindingProperty - = AvaloniaProperty.RegisterAttached("TextBinding", typeof(TextSearch)); + public static readonly AttachedProperty TextBindingProperty + = AvaloniaProperty.RegisterAttached("TextBinding", typeof(TextSearch)); // TODO12: Control should be Interactive to match the property definition. /// @@ -47,7 +47,7 @@ namespace Avalonia.Controls.Primitives /// /// The interactive element. /// The search text binding to set. - public static void SetTextBinding(Interactive interactive, IBinding? value) + public static void SetTextBinding(Interactive interactive, BindingBase? value) => interactive.SetValue(TextBindingProperty, value); /// @@ -56,7 +56,7 @@ namespace Avalonia.Controls.Primitives /// The interactive element. /// The search text binding. [AssignBinding] - public static IBinding? GetTextBinding(Interactive interactive) + public static BindingBase? GetTextBinding(Interactive interactive) => interactive.GetValue(TextBindingProperty); /// diff --git a/src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs b/src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs index 18a5d0a62f..b6c4c1b51e 100644 --- a/src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs +++ b/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; } - /// - /// Selects the child items of an item. - /// - /// The item. - /// The child items, or null if no child items. - 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; } /// diff --git a/src/Avalonia.Controls/Templates/ITreeDataTemplate.cs b/src/Avalonia.Controls/Templates/ITreeDataTemplate.cs index 22d332b6d8..5d57370025 100644 --- a/src/Avalonia.Controls/Templates/ITreeDataTemplate.cs +++ b/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 { /// - /// Selects the child items of an item. + /// Binds the children of the specified item to a property on a target object. /// - /// The item. + /// The target object. + /// The target property. + /// The item whose children should be bound. /// - /// An holding the items, or an observable that tracks the - /// items. May return null if no child items. + /// An that can be used to remove the binding. /// - InstancedBinding? ItemsSelector(object item); + IDisposable BindChildren(AvaloniaObject target, AvaloniaProperty targetProperty, object item); } } diff --git a/src/Avalonia.Controls/Utils/BindingEvaluator.cs b/src/Avalonia.Controls/Utils/BindingEvaluator.cs index 4f1c4f500c..299642ace6 100644 --- a/src/Avalonia.Controls/Utils/BindingEvaluator.cs +++ b/src/Avalonia.Controls/Utils/BindingEvaluator.cs @@ -10,7 +10,7 @@ namespace Avalonia.Controls.Utils; internal sealed class BindingEvaluator : StyledElement, IDisposable { private BindingExpressionBase? _expression; - private IBinding? _lastBinding; + private BindingBase? _lastBinding; [SuppressMessage( "AvaloniaProperty", @@ -37,7 +37,7 @@ internal sealed class BindingEvaluator : 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 : StyledElement, IDisposable } [return: NotNullIfNotNull(nameof(binding))] - public static BindingEvaluator? TryCreate(IBinding? binding) + public static BindingEvaluator? TryCreate(BindingBase? binding) { if (binding is null) return null; diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs index 7294bab29e..fd8276aa80 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs +++ b/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; diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlClassesPropertyResolver.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlClassesPropertyResolver.cs index eccd3d9e09..48118df40c 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlClassesPropertyResolver.cs +++ b/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) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs index 8632ea6ee0..88d708335e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs +++ b/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()); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs index 86812240df..6030ca213e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs +++ b/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())); } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 0aa9814e17..2b7a835fd8 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/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"); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs index 4e5a7bdd12..442c7dd495 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs +++ b/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 diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs index fdf3dffd0e..68dd595ea2 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs +++ b/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; -using System.Xml.Linq; namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs index 5190e3e812..0913903ff3 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs +++ b/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 }; } + /// + /// Gets or sets the amount of time, in milliseconds, to wait before updating the binding + /// source after the value on the target changes. + /// + /// + /// There is no delay when the source is updated via + /// or . Nor is there a delay when + /// is active and a new source object is provided. + /// + public int Delay { get; set; } + + /// + /// Gets or sets the to use. + /// + public IValueConverter? Converter { get; set; } + + /// + /// Gets or sets the culture in which to evaluate the converter. + /// + /// The default value is null. + /// + /// If this property is not set then will be used. + /// + [TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))] + public CultureInfo? ConverterCulture { get; set; } + + /// + /// Gets or sets a parameter to pass to . + /// + public object? ConverterParameter { get; set; } + + public Type? DataType { get; set; } + + /// + /// Gets or sets the value to use when the binding is unable to produce a value. + /// + public object? FallbackValue { get; set; } = AvaloniaProperty.UnsetValue; + + /// + /// Gets or sets the binding mode. + /// + public BindingMode Mode { get; set; } + [ConstructorArgument("path")] public CompiledBindingPath Path { get; set; } + /// + /// Gets or sets the binding priority. + /// + public BindingPriority Priority { get; set; } + + /// + /// Gets or sets the source for the binding. + /// public object? Source { get; set; } = AvaloniaProperty.UnsetValue; - public Type? DataType { get; set; } + /// + /// Gets or sets the string format. + /// + 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); - } + /// + /// Gets or sets the value to use when the binding result is null. + /// + public object? TargetNullValue { get; set; } = AvaloniaProperty.UnsetValue; + + /// + /// Gets or sets a value that determines the timing of binding source updates for + /// and bindings. + /// + public UpdateSourceTrigger UpdateSourceTrigger { get; set; } + + internal WeakReference? DefaultAnchor { get; set; } - 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); + } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs index 89671708b3..20e9c6e886 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs @@ -94,9 +94,6 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings internal IEnumerable Elements => _elements; - internal SourceMode SourceMode => Array.Exists(_elements, e => e is IControlSourceBindingPathElement) - ? SourceMode.Control : SourceMode.Data; - /// public override string ToString() => string.Concat((IEnumerable) _elements); diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs index d6cd23a25d..f602b1a82d 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs +++ b/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."); diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs index c1f2f001f3..311d151ddb 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs +++ b/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() - { - } + /// + /// Initializes a new instance of the class. + /// + public ReflectionBindingExtension() { } - public ReflectionBindingExtension(string path) - { - Path = path; - } + /// + /// Initializes a new instance of the class. + /// + /// The binding path. + public ReflectionBindingExtension(string path) : base(path) { } + + /// + /// Initializes a new instance of the class. + /// + /// The binding path. + /// The binding mode. + 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, }; } - - /// - 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; - - /// - /// Gets or sets a value that determines the timing of binding source updates for - /// and bindings. - /// - public UpdateSourceTrigger UpdateSourceTrigger { get; set; } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs index 96e1bc96f0..33cdcbd4d2 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs +++ b/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) diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs index aff94ae6d5..f98a1cc60b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs +++ b/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); diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj index 46ab163e81..49c5319020 100644 --- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj +++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj @@ -5,7 +5,6 @@ - diff --git a/src/Markup/Avalonia.Markup/Data/Binding.cs b/src/Markup/Avalonia.Markup/Data/Binding.cs index 1578177bfc..3a0f8a482e 100644 --- a/src/Markup/Avalonia.Markup/Data/Binding.cs +++ b/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 -{ - /// - /// A XAML binding. - /// - [RequiresUnreferencedCode(TrimmingMessages.ReflectionBindingRequiresUnreferencedCodeMessage)] -#if NET8_0_OR_GREATER - [RequiresDynamicCode(TrimmingMessages.ReflectionBindingRequiresDynamicCodeMessage)] -#endif - public class Binding : BindingBase - { - /// - /// Initializes a new instance of the class. - /// - public Binding() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The binding path. - /// The binding mode. - public Binding(string path, BindingMode mode = BindingMode.Default) - : base(mode) - { - Path = path; - } - - /// - /// Gets or sets the name of the element to use as the binding source. - /// - public string? ElementName { get; set; } - - /// - /// Gets or sets the relative source for the binding. - /// - public RelativeSource? RelativeSource { get; set; } +namespace Avalonia.Data; - /// - /// Gets or sets the source for the binding. - /// - public object? Source { get; set; } = AvaloniaProperty.UnsetValue; - - /// - /// Gets or sets the binding path. - /// - public string Path { get; set; } = ""; - - /// - /// Gets or sets a function used to resolve types from names in the binding path. - /// - public Func? 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); - } - - /// - /// Hack for TreeDataTemplate to create a binding expression for an item. - /// - /// The item. - /// - /// Ideally we'd do this in a more generic way but didn't have time to refactor - /// ITreeDataTemplate in time for 11.0. We should revisit this in 12.0. - /// - // TODO12: Refactor +/// +/// Provides limited compatibility with the 11.x Binding class. Use +/// for new code. +/// +[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? 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? 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) { } } diff --git a/src/Markup/Avalonia.Markup/Data/BindingBase.cs b/src/Markup/Avalonia.Markup/Data/BindingBase.cs deleted file mode 100644 index fa6f733281..0000000000 --- a/src/Markup/Avalonia.Markup/Data/BindingBase.cs +++ /dev/null @@ -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 - { - /// - /// Initializes a new instance of the class. - /// - public BindingBase() - { - FallbackValue = AvaloniaProperty.UnsetValue; - TargetNullValue = AvaloniaProperty.UnsetValue; - } - - /// - /// Initializes a new instance of the class. - /// - /// The binding mode. - public BindingBase(BindingMode mode = BindingMode.Default) - :this() - { - Mode = mode; - } - - /// - /// Gets or sets the amount of time, in milliseconds, to wait before updating the binding - /// source after the value on the target changes. - /// - /// - /// There is no delay when the source is updated via - /// or . Nor is there a delay when - /// is active and a new source object is provided. - /// - public int Delay { get; set; } - - /// - /// Gets or sets the to use. - /// - public IValueConverter? Converter { get; set; } - - /// - /// Gets or sets the culture in which to evaluate the converter. - /// - /// The default value is null. - /// - /// If this property is not set then will be used. - /// - [TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))] - public CultureInfo? ConverterCulture { get; set; } - - /// - /// Gets or sets a parameter to pass to . - /// - public object? ConverterParameter { get; set; } - - /// - /// Gets or sets the value to use when the binding is unable to produce a value. - /// - public object? FallbackValue { get; set; } - - /// - /// Gets or sets the value to use when the binding result is null. - /// - public object? TargetNullValue { get; set; } - - /// - /// Gets or sets the binding mode. - /// - public BindingMode Mode { get; set; } - - /// - /// Gets or sets the binding priority. - /// - public BindingPriority Priority { get; set; } - - /// - /// Gets or sets the string format. - /// - public string? StringFormat { get; set; } - - /// - /// Gets or sets a value that determines the timing of binding source updates for - /// and bindings. - /// - public UpdateSourceTrigger UpdateSourceTrigger { get; set; } - - public WeakReference? DefaultAnchor { get; set; } - - public WeakReference? NameScope { get; set; } - - /// - [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); - } - } -} diff --git a/src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs b/src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs index 9c0153e7b9..1216f260dc 100644 --- a/src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs +++ b/src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs @@ -29,7 +29,7 @@ namespace Avalonia.Markup.Data /// The control. /// The property on the control to bind to. /// The binding. - 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) diff --git a/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs b/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs index afde09601a..a945be8be8 100644 --- a/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs +++ b/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))] diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj index c1819c2e88..e4b1d0baf7 100644 --- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj +++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj @@ -16,6 +16,10 @@ + + + + diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index 71ce8eec51..07aa864976 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -1299,25 +1299,6 @@ namespace Avalonia.Base.UnitTests AvaloniaProperty.Register("Bar", "bardefault"); } - private class TestOneTimeBinding : IBinding - { - private IObservable _source; - - public TestOneTimeBinding(IObservable 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; } diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Obsolete.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Obsolete.cs deleted file mode 100644 index 2c426cbf09..0000000000 --- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Obsolete.cs +++ /dev/null @@ -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(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); - } - } -} diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs index 559b4d76c0..15b414c407 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs +++ b/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; diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/BindingExpressionGrammarTests.cs b/tests/Avalonia.Base.UnitTests/Data/Core/Parsers/BindingExpressionGrammarTests.cs similarity index 98% rename from tests/Avalonia.Markup.UnitTests/Parsers/BindingExpressionGrammarTests.cs rename to tests/Avalonia.Base.UnitTests/Data/Core/Parsers/BindingExpressionGrammarTests.cs index 3dcb7b1bfe..db78fbc5cd 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/BindingExpressionGrammarTests.cs +++ b/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 { diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/BindingExpressionGrammarTests_Errors.cs b/tests/Avalonia.Base.UnitTests/Data/Core/Parsers/BindingExpressionGrammarTests_Errors.cs similarity index 98% rename from tests/Avalonia.Markup.UnitTests/Parsers/BindingExpressionGrammarTests_Errors.cs rename to tests/Avalonia.Base.UnitTests/Data/Core/Parsers/BindingExpressionGrammarTests_Errors.cs index bc5161f11b..9801bad250 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/BindingExpressionGrammarTests_Errors.cs +++ b/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 { diff --git a/tests/Avalonia.Benchmarks/Data/TemplateBinding_Setup.cs b/tests/Avalonia.Benchmarks/Data/TemplateBinding_Setup.cs index eee61fbb50..18499d85a2 100644 --- a/tests/Avalonia.Benchmarks/Data/TemplateBinding_Setup.cs +++ b/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 // 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 // overload being selected. - using var d = target.Bind(Control.TagProperty, (IBinding)binding); + using var d = target.Bind(Control.TagProperty, (BindingBase)binding); } } } diff --git a/tests/Avalonia.Benchmarks/Data/TemplateBinding_Values.cs b/tests/Avalonia.Benchmarks/Data/TemplateBinding_Values.cs index 4347e2091f..36cfb3f339 100644 --- a/tests/Avalonia.Benchmarks/Data/TemplateBinding_Values.cs +++ b/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 // 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 // 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) { diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs index 4e9af4978e..79e93603d9 100644 --- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs +++ b/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( object? dataContext = null, - IBinding? displayMemberBinding = null, + BindingBase? displayMemberBinding = null, IList? items = null, IList? itemsSource = null, ControlTheme? itemContainerTheme = null, diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs index 1701d0354d..5eb3e378eb 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs +++ b/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(); - 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((_, __) => canvas)); diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index ffb7ab9048..6ba7255a27 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/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( + 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 { } diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Converters.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Converters.cs index a7ad06533b..5cac0e6732 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Converters.cs +++ b/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(instancedBinding.Expression); + var expression = (BindingExpression)target.CreateInstance( + textBlock, + TextBlock.TextProperty, + null); - Assert.Same(StringConverters.IsNullOrEmpty, expressionObserver.Converter); + Assert.Same(StringConverters.IsNullOrEmpty, expression.Converter); } [Fact] diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionNodeFactoryTests.cs b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionNodeFactoryTests.cs index 70d4a197c9..2ccb624318 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionNodeFactoryTests.cs +++ b/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; diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs index bac18e60a7..82d5268b73 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs +++ b/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 { diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AvaloniaProperty.cs b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AvaloniaProperty.cs index 7c02d6a6e0..63916e03e5 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AvaloniaProperty.cs +++ b/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 { diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Indexer.cs b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Indexer.cs index f69f333f10..ce056ad750 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Indexer.cs +++ b/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; diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Method.cs b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Method.cs index f8aa960f93..876a956571 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Method.cs +++ b/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 { diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs index 51698d76e3..a62adc4d43 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs +++ b/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; diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Property.cs b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Property.cs index 97b9671f1d..7b8aa2528d 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Property.cs +++ b/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; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 9efc243f40..91af22b952 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/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; } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/StyleTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/StyleTests.cs index e49243379f..3b50b1e1d0 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/StyleTests.cs +++ b/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(setter.Value); + Assert.IsType(setter.Value); } } @@ -40,7 +40,7 @@ namespace Avalonia.Markup.Xaml.UnitTests """); var setter = (Setter)style.Setters.First(); - Assert.IsType(setter.Value); + Assert.IsType(setter.Value); } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/AssignBindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/AssignBindingTests.cs index 114b626e63..307c01812d 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/AssignBindingTests.cs +++ b/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 AttachedBindingProperty = - AvaloniaProperty.RegisterAttached("AttachedBinding"); + public static readonly AttachedProperty AttachedBindingProperty = + AvaloniaProperty.RegisterAttached("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); } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs index 83c9db5b31..41c349d7e4 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs +++ b/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().ToArray(); + var bindings = target.Bindings.Cast().ToArray(); Assert.Equal("Foo", bindings[0].Path); Assert.Equal("Bar", bindings[1].Path); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs index 81e94bde4f..37c4c9c5aa 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs +++ b/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(template.ItemsSource); + Assert.IsType(template.ItemsSource); } }