Browse Source

Merge remote-tracking branch 'upstream/master' into fixes/glyphRunLifetime

pull/10015/head
Benedikt Stebner 4 years ago
parent
commit
5dc49fe3e8
  1. 83
      samples/ControlCatalog/Pages/ScreenPage.cs
  2. 3
      src/Avalonia.Base/Avalonia.Base.csproj
  3. 59
      src/Avalonia.Base/Data/BindingOperations.cs
  4. 2
      src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs
  5. 2
      src/Avalonia.Base/Data/Core/BindingExpression.cs
  6. 2
      src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs
  7. 2
      src/Avalonia.Base/Data/Core/ExpressionNode.cs
  8. 2
      src/Avalonia.Base/Data/Core/ExpressionObserver.cs
  9. 2
      src/Avalonia.Base/Data/Core/IndexerNodeBase.cs
  10. 2
      src/Avalonia.Base/Data/Core/LogicalNotNode.cs
  11. 2
      src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs
  12. 28
      src/Avalonia.Base/Data/Core/Plugins/BindingPlugins.cs
  13. 2
      src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
  14. 2
      src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs
  15. 2
      src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs
  16. 2
      src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs
  17. 2
      src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
  18. 2
      src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs
  19. 2
      src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs
  20. 2
      src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs
  21. 2
      src/Avalonia.Base/Data/Core/SettableNode.cs
  22. 2
      src/Avalonia.Base/Data/Core/StreamNode.cs
  23. 2
      src/Avalonia.Base/Data/Core/TypeCastNode.cs
  24. 43
      src/Avalonia.Base/Data/InstancedBinding.cs
  25. 2
      src/Avalonia.Base/Styling/Setter.cs
  26. 5
      src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs
  27. 18
      src/Avalonia.Controls/TreeViewItem.cs
  28. 1
      src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml
  29. 3
      src/Avalonia.Themes.Simple/Controls/MenuItem.xaml
  30. 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs
  31. 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
  32. 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/StrongTypeCastNode.cs
  33. 1
      src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
  34. 2
      src/Markup/Avalonia.Markup/Data/Binding.cs
  35. 12
      src/Markup/Avalonia.Markup/Data/BindingBase.cs
  36. 4
      src/Markup/Avalonia.Markup/Data/MultiBinding.cs
  37. 2
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
  38. 2
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs
  39. 2
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs
  40. 2
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/SelfNode.cs
  41. 6
      tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs
  42. 8
      tests/Avalonia.Markup.UnitTests/Data/BindingTests_Converters.cs
  43. 6
      tests/Avalonia.Markup.UnitTests/Data/BindingTests_DataValidation.cs
  44. 4
      tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests.cs

83
samples/ControlCatalog/Pages/ScreenPage.cs

@ -1,5 +1,7 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Net.Http.Headers;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
@ -12,6 +14,11 @@ namespace ControlCatalog.Pages
public class ScreenPage : UserControl public class ScreenPage : UserControl
{ {
private double _leftMost; private double _leftMost;
private double _topMost;
private IBrush _primaryBrush = SolidColorBrush.Parse("#FF0078D7");
private IBrush _defaultBrush = Brushes.LightGray;
private IPen _activePen = new Pen(Brushes.Black);
private IPen _defaultPen = new Pen(Brushes.DarkGray);
protected override bool BypassFlowDirectionPolicies => true; protected override bool BypassFlowDirectionPolicies => true;
@ -34,54 +41,88 @@ namespace ControlCatalog.Pages
var screens = w.Screens.All; var screens = w.Screens.All;
var scaling = ((IRenderRoot)w).RenderScaling; var scaling = ((IRenderRoot)w).RenderScaling;
var drawBrush = Brushes.Black; var activeScreen = w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling)));
Pen p = new Pen(drawBrush); double maxBottom = 0;
foreach (Screen screen in screens) for (int i = 0; i<screens.Count; i++ )
{ {
var screen = screens[i];
if (screen.Bounds.X / 10f < _leftMost) if (screen.Bounds.X / 10f < _leftMost)
{ {
_leftMost = screen.Bounds.X / 10f; _leftMost = screen.Bounds.X / 10f;
Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background);
return; return;
} }
if (screen.Bounds.Y / 10f < _topMost)
{
_topMost = screen.Bounds.Y / 10f;
Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background);
return;
}
bool primary = screen.IsPrimary;
bool active = screen.Equals(activeScreen);
Rect boundsRect = new Rect(screen.Bounds.X / 10f + Math.Abs(_leftMost), screen.Bounds.Y / 10f, screen.Bounds.Width / 10f, Rect boundsRect = new Rect(screen.Bounds.X / 10f + Math.Abs(_leftMost), screen.Bounds.Y / 10f+Math.Abs(_topMost), screen.Bounds.Width / 10f,
screen.Bounds.Height / 10f); screen.Bounds.Height / 10f);
Rect workingAreaRect = new Rect(screen.WorkingArea.X / 10f + Math.Abs(_leftMost), screen.WorkingArea.Y / 10f, screen.WorkingArea.Width / 10f, Rect workingAreaRect = new Rect(screen.WorkingArea.X / 10f + Math.Abs(_leftMost), screen.WorkingArea.Y / 10f+Math.Abs(_topMost), screen.WorkingArea.Width / 10f,
screen.WorkingArea.Height / 10f); screen.WorkingArea.Height / 10f);
context.DrawRectangle(p, boundsRect); context.DrawRectangle(primary ? _primaryBrush : _defaultBrush, active ? _activePen : _defaultPen, boundsRect);
context.DrawRectangle(p, workingAreaRect); context.DrawRectangle(primary ? _primaryBrush : _defaultBrush, active ? _activePen : _defaultPen, workingAreaRect);
var identifier = CreateScreenIdentifier((i+1).ToString(), primary);
var center = boundsRect.Center - new Point(identifier.Width / 2.0f, identifier.Height / 2.0f);
context.DrawText(identifier, center);
maxBottom = Math.Max(maxBottom, boundsRect.Bottom);
}
var formattedText = CreateFormattedText($"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}"); double currentHeight = maxBottom;
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height));
formattedText = for(int i = 0; i< screens.Count; i++)
CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}"); {
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 20)); var screen = screens[i];
var formattedText = CreateFormattedText($"Screen {i+1}", 18);
context.DrawText(formattedText, new Point(0, currentHeight));
currentHeight += 25;
formattedText = CreateFormattedText($"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}");
context.DrawText(formattedText, new Point(15, currentHeight));
currentHeight += 20;
formattedText = CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}");
context.DrawText(formattedText, new Point(15, currentHeight));
currentHeight += 20;
formattedText = CreateFormattedText($"Scaling: {screen.Scaling * 100}%"); formattedText = CreateFormattedText($"Scaling: {screen.Scaling * 100}%");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40)); context.DrawText(formattedText, new Point(15, currentHeight));
currentHeight += 20;
formattedText = CreateFormattedText($"IsPrimary: {screen.IsPrimary}"); formattedText = CreateFormattedText($"IsPrimary: {screen.IsPrimary}");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 60)); context.DrawText(formattedText, new Point(15, currentHeight));
currentHeight += 20;
formattedText = CreateFormattedText( $"Current: {screen.Equals(activeScreen)}");
context.DrawText(formattedText, new Point(15, currentHeight));
currentHeight += 30;
formattedText =
CreateFormattedText(
$"Current: {screen.Equals(w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling))))}");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 80));
} }
context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10f, w.Bounds.Width / 10, w.Bounds.Height / 10)); context.DrawRectangle(_activePen, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10f+Math.Abs(_topMost), w.Bounds.Width / 10, w.Bounds.Height / 10));
} }
private static FormattedText CreateFormattedText(string textToFormat) private static FormattedText CreateFormattedText(string textToFormat, double size = 12)
{ {
return new FormattedText(textToFormat, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, return new FormattedText(textToFormat, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
Typeface.Default, 12, Brushes.Green); Typeface.Default, size, Brushes.Green);
}
private static FormattedText CreateScreenIdentifier(string textToFormat, bool primary)
{
return new FormattedText(textToFormat, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, Typeface.Default, 20, primary ? Brushes.White : Brushes.Black);
} }
} }
} }

3
src/Avalonia.Base/Avalonia.Base.csproj

@ -47,6 +47,9 @@
<InternalsVisibleTo Include="Avalonia.DesignerSupport, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.DesignerSupport, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Direct2D1.RenderTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Direct2D1.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.LeakTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.LeakTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.Xaml, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.Xaml.UnitTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Markup.Xaml.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Skia.RenderTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Skia.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Skia.UnitTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Skia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />

59
src/Avalonia.Base/Data/BindingOperations.cs

@ -41,54 +41,41 @@ namespace Avalonia.Data
{ {
case BindingMode.Default: case BindingMode.Default:
case BindingMode.OneWay: case BindingMode.OneWay:
if (binding.Observable is null) return target.Bind(property, binding.Source, binding.Priority);
throw new InvalidOperationException("InstancedBinding does not contain an observable.");
return target.Bind(property, binding.Observable, binding.Priority);
case BindingMode.TwoWay: case BindingMode.TwoWay:
if (binding.Observable is null) {
throw new InvalidOperationException("InstancedBinding does not contain an observable."); if (binding.Source is not IObserver<object?> observer)
if (binding.Subject is null)
throw new InvalidOperationException("InstancedBinding does not contain a subject."); throw new InvalidOperationException("InstancedBinding does not contain a subject.");
return new TwoWayBindingDisposable( return new TwoWayBindingDisposable(
target.Bind(property, binding.Observable, binding.Priority), target.Bind(property, binding.Source, binding.Priority),
target.GetObservable(property).Subscribe(binding.Subject)); target.GetObservable(property).Subscribe(observer));
}
case BindingMode.OneTime: case BindingMode.OneTime:
if (binding.Observable is {} source) {
{ // Perf: Avoid allocating closure in the outer scope.
// Perf: Avoid allocating closure in the outer scope. var targetCopy = target;
var targetCopy = target; var propertyCopy = property;
var propertyCopy = property; var bindingCopy = binding;
var bindingCopy = binding;
return binding.Source
return source .Where(x => BindingNotification.ExtractValue(x) != AvaloniaProperty.UnsetValue)
.Where(x => BindingNotification.ExtractValue(x) != AvaloniaProperty.UnsetValue) .Take(1)
.Take(1) .Subscribe(x => targetCopy.SetValue(
.Subscribe(x => targetCopy.SetValue( propertyCopy,
propertyCopy, BindingNotification.ExtractValue(x),
BindingNotification.ExtractValue(x), bindingCopy.Priority));
bindingCopy.Priority)); }
}
else
{
target.SetValue(property, binding.Value, binding.Priority);
return Disposable.Empty;
}
case BindingMode.OneWayToSource: case BindingMode.OneWayToSource:
{ {
if (binding.Observable is null) if (binding.Source is not IObserver<object?> observer)
throw new InvalidOperationException("InstancedBinding does not contain an observable.");
if (binding.Subject is null)
throw new InvalidOperationException("InstancedBinding does not contain a subject."); throw new InvalidOperationException("InstancedBinding does not contain a subject.");
// Perf: Avoid allocating closure in the outer scope.
var bindingCopy = binding;
return Observable.CombineLatest( return Observable.CombineLatest(
binding.Observable, binding.Source,
target.GetObservable(property), target.GetObservable(property),
(_, v) => v) (_, v) => v)
.Subscribe(x => bindingCopy.Subject.OnNext(x)); .Subscribe(x => observer.OnNext(x));
} }
default: default:

2
src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs

@ -3,7 +3,7 @@ using Avalonia.Reactive;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class AvaloniaPropertyAccessorNode : SettableNode internal class AvaloniaPropertyAccessorNode : SettableNode
{ {
private IDisposable? _subscription; private IDisposable? _subscription;
private readonly bool _enableValidation; private readonly bool _enableValidation;

2
src/Avalonia.Base/Data/Core/BindingExpression.cs

@ -13,7 +13,7 @@ namespace Avalonia.Data.Core
/// that are sent and received. /// that are sent and received.
/// </summary> /// </summary>
[RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)] [RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)]
public class BindingExpression : LightweightObservableBase<object?>, IAvaloniaSubject<object?>, IDescription internal class BindingExpression : LightweightObservableBase<object?>, IAvaloniaSubject<object?>, IDescription
{ {
private readonly ExpressionObserver _inner; private readonly ExpressionObserver _inner;
private readonly Type _targetType; private readonly Type _targetType;

2
src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs

@ -1,6 +1,6 @@
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class EmptyExpressionNode : ExpressionNode internal class EmptyExpressionNode : ExpressionNode
{ {
public override string Description => "."; public override string Description => ".";
} }

2
src/Avalonia.Base/Data/Core/ExpressionNode.cs

@ -2,7 +2,7 @@ using System;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public abstract class ExpressionNode internal abstract class ExpressionNode
{ {
protected static readonly WeakReference<object?> UnsetReference = protected static readonly WeakReference<object?> UnsetReference =
new WeakReference<object?>(AvaloniaProperty.UnsetValue); new WeakReference<object?>(AvaloniaProperty.UnsetValue);

2
src/Avalonia.Base/Data/Core/ExpressionObserver.cs

@ -11,7 +11,7 @@ namespace Avalonia.Data.Core
/// <summary> /// <summary>
/// Observes and sets the value of an expression on an object. /// Observes and sets the value of an expression on an object.
/// </summary> /// </summary>
public class ExpressionObserver : LightweightObservableBase<object?>, IDescription internal class ExpressionObserver : LightweightObservableBase<object?>, IDescription
{ {
/// <summary> /// <summary>
/// An ordered collection of property accessor plugins that can be used to customize /// An ordered collection of property accessor plugins that can be used to customize

2
src/Avalonia.Base/Data/Core/IndexerNodeBase.cs

@ -7,7 +7,7 @@ using Avalonia.Utilities;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public abstract class IndexerNodeBase : SettableNode, internal abstract class IndexerNodeBase : SettableNode,
IWeakEventSubscriber<NotifyCollectionChangedEventArgs>, IWeakEventSubscriber<NotifyCollectionChangedEventArgs>,
IWeakEventSubscriber<PropertyChangedEventArgs> IWeakEventSubscriber<PropertyChangedEventArgs>
{ {

2
src/Avalonia.Base/Data/Core/LogicalNotNode.cs

@ -3,7 +3,7 @@ using System.Globalization;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class LogicalNotNode : ExpressionNode, ITransformNode internal class LogicalNotNode : ExpressionNode, ITransformNode
{ {
public override string Description => "!"; public override string Description => "!";

2
src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs

@ -8,7 +8,7 @@ namespace Avalonia.Data.Core.Plugins
/// <summary> /// <summary>
/// Reads a property from a <see cref="AvaloniaObject"/>. /// Reads a property from a <see cref="AvaloniaObject"/>.
/// </summary> /// </summary>
public class AvaloniaPropertyAccessorPlugin : IPropertyAccessorPlugin internal class AvaloniaPropertyAccessorPlugin : IPropertyAccessorPlugin
{ {
/// <inheritdoc/> /// <inheritdoc/>
[RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)] [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]

28
src/Avalonia.Base/Data/Core/Plugins/BindingPlugins.cs

@ -0,0 +1,28 @@
using System.Collections.Generic;
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Holds a registry of plugins used for bindings.
/// </summary>
public static class BindingPlugins
{
/// <summary>
/// An ordered collection of property accessor plugins that can be used to customize
/// the reading and subscription of property values on a type.
/// </summary>
public static IList<IPropertyAccessorPlugin> PropertyAccessors => ExpressionObserver.PropertyAccessors;
/// <summary>
/// An ordered collection of validation checker plugins that can be used to customize
/// the validation of view model and model data.
/// </summary>
public static IList<IDataValidationPlugin> DataValidators => ExpressionObserver.DataValidators;
/// <summary>
/// An ordered collection of stream plugins that can be used to customize the behavior
/// of the '^' stream binding operator.
/// </summary>
public static IList<IStreamPlugin> StreamHandlers => ExpressionObserver.StreamHandlers;
}
}

2
src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs

@ -10,7 +10,7 @@ namespace Avalonia.Data.Core.Plugins
/// <summary> /// <summary>
/// Validates properties on that have <see cref="ValidationAttribute"/>s. /// Validates properties on that have <see cref="ValidationAttribute"/>s.
/// </summary> /// </summary>
public class DataAnnotationsValidationPlugin : IDataValidationPlugin internal class DataAnnotationsValidationPlugin : IDataValidationPlugin
{ {
/// <inheritdoc/> /// <inheritdoc/>
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)] [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]

2
src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs

@ -7,7 +7,7 @@ namespace Avalonia.Data.Core.Plugins
/// <summary> /// <summary>
/// Validates properties that report errors by throwing exceptions. /// Validates properties that report errors by throwing exceptions.
/// </summary> /// </summary>
public class ExceptionValidationPlugin : IDataValidationPlugin internal class ExceptionValidationPlugin : IDataValidationPlugin
{ {
/// <inheritdoc/> /// <inheritdoc/>
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)] [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]

2
src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs

@ -10,7 +10,7 @@ namespace Avalonia.Data.Core.Plugins
/// <summary> /// <summary>
/// Validates properties on objects that implement <see cref="INotifyDataErrorInfo"/>. /// Validates properties on objects that implement <see cref="INotifyDataErrorInfo"/>.
/// </summary> /// </summary>
public class IndeiValidationPlugin : IDataValidationPlugin internal class IndeiValidationPlugin : IDataValidationPlugin
{ {
private static readonly WeakEvent<INotifyDataErrorInfo, DataErrorsChangedEventArgs> private static readonly WeakEvent<INotifyDataErrorInfo, DataErrorsChangedEventArgs>
ErrorsChangedWeakEvent = WeakEvent.Register<INotifyDataErrorInfo, DataErrorsChangedEventArgs>( ErrorsChangedWeakEvent = WeakEvent.Register<INotifyDataErrorInfo, DataErrorsChangedEventArgs>(

2
src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs

@ -11,7 +11,7 @@ namespace Avalonia.Data.Core.Plugins
/// Reads a property from a standard C# object that optionally supports the /// Reads a property from a standard C# object that optionally supports the
/// <see cref="INotifyPropertyChanged"/> interface. /// <see cref="INotifyPropertyChanged"/> interface.
/// </summary> /// </summary>
public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin internal class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin
{ {
private readonly Dictionary<(Type, string), PropertyInfo?> _propertyLookup = private readonly Dictionary<(Type, string), PropertyInfo?> _propertyLookup =
new Dictionary<(Type, string), PropertyInfo?>(); new Dictionary<(Type, string), PropertyInfo?>();

2
src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs

@ -6,7 +6,7 @@ using System.Reflection;
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
public class MethodAccessorPlugin : IPropertyAccessorPlugin internal class MethodAccessorPlugin : IPropertyAccessorPlugin
{ {
private readonly Dictionary<(Type, string), MethodInfo?> _methodLookup = private readonly Dictionary<(Type, string), MethodInfo?> _methodLookup =
new Dictionary<(Type, string), MethodInfo?>(); new Dictionary<(Type, string), MethodInfo?>();

2
src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs

@ -10,7 +10,7 @@ namespace Avalonia.Data.Core.Plugins
/// Handles binding to <see cref="IObservable{T}"/>s for the '^' stream binding operator. /// Handles binding to <see cref="IObservable{T}"/>s for the '^' stream binding operator.
/// </summary> /// </summary>
[UnconditionalSuppressMessage("Trimming", "IL3050", Justification = TrimmingMessages.IgnoreNativeAotSupressWarningMessage)] [UnconditionalSuppressMessage("Trimming", "IL3050", Justification = TrimmingMessages.IgnoreNativeAotSupressWarningMessage)]
public class ObservableStreamPlugin : IStreamPlugin internal class ObservableStreamPlugin : IStreamPlugin
{ {
private static MethodInfo? s_observableGeneric; private static MethodInfo? s_observableGeneric;
private static MethodInfo? s_observableSelect; private static MethodInfo? s_observableSelect;

2
src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs

@ -10,7 +10,7 @@ namespace Avalonia.Data.Core.Plugins
/// Handles binding to <see cref="Task"/>s for the '^' stream binding operator. /// Handles binding to <see cref="Task"/>s for the '^' stream binding operator.
/// </summary> /// </summary>
[UnconditionalSuppressMessage("Trimming", "IL3050", Justification = TrimmingMessages.IgnoreNativeAotSupressWarningMessage)] [UnconditionalSuppressMessage("Trimming", "IL3050", Justification = TrimmingMessages.IgnoreNativeAotSupressWarningMessage)]
public class TaskStreamPlugin : IStreamPlugin internal class TaskStreamPlugin : IStreamPlugin
{ {
/// <summary> /// <summary>
/// Checks whether this plugin handles the specified value. /// Checks whether this plugin handles the specified value.

2
src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs

@ -5,7 +5,7 @@ using Avalonia.Data.Core.Plugins;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
[RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)] [RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)]
public class PropertyAccessorNode : SettableNode internal class PropertyAccessorNode : SettableNode
{ {
private readonly bool _enableValidation; private readonly bool _enableValidation;
private IPropertyAccessorPlugin? _customPlugin; private IPropertyAccessorPlugin? _customPlugin;

2
src/Avalonia.Base/Data/Core/SettableNode.cs

@ -2,7 +2,7 @@
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public abstract class SettableNode : ExpressionNode internal abstract class SettableNode : ExpressionNode
{ {
public bool SetTargetValue(object? value, BindingPriority priority) public bool SetTargetValue(object? value, BindingPriority priority)
{ {

2
src/Avalonia.Base/Data/Core/StreamNode.cs

@ -6,7 +6,7 @@ using Avalonia.Reactive;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
[RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)] [RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)]
public class StreamNode : ExpressionNode internal class StreamNode : ExpressionNode
{ {
private IStreamPlugin? _customPlugin = null; private IStreamPlugin? _customPlugin = null;
private IDisposable? _subscription; private IDisposable? _subscription;

2
src/Avalonia.Base/Data/Core/TypeCastNode.cs

@ -4,7 +4,7 @@ using System.Text;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class TypeCastNode : ExpressionNode internal class TypeCastNode : ExpressionNode
{ {
public override string Description => $"as {TargetType.FullName}"; public override string Description => $"as {TargetType.FullName}";

43
src/Avalonia.Base/Data/InstancedBinding.cs

@ -1,5 +1,6 @@
using System; using System;
using Avalonia.Reactive; using Avalonia.Reactive;
using ObservableEx = Avalonia.Reactive.Observable;
namespace Avalonia.Data namespace Avalonia.Data
{ {
@ -14,11 +15,23 @@ namespace Avalonia.Data
/// </remarks> /// </remarks>
public class InstancedBinding public class InstancedBinding
{ {
internal InstancedBinding(object? value, BindingMode mode, BindingPriority priority) /// <summary>
/// Initializes a new instance of the <see cref="InstancedBinding"/> class.
/// </summary>
/// <param name="source">The binding source.</param>
/// <param name="mode">The binding mode.</param>
/// <param name="priority">The priority of the binding.</param>
/// <remarks>
/// This constructor can be used to create any type of binding and as such requires an
/// <see cref="ISubject{Object}"/> as the binding source because this is the only binding
/// source which can be used for all binding modes. If you wish to create an instance with
/// something other than a subject, use one of the static creation methods on this class.
/// </remarks>
internal InstancedBinding(IObservable<object?> source, BindingMode mode, BindingPriority priority)
{ {
Mode = mode; Mode = mode;
Priority = priority; Priority = priority;
Value = value; Source = source ?? throw new ArgumentNullException(nameof(source));
} }
/// <summary> /// <summary>
@ -32,24 +45,12 @@ namespace Avalonia.Data
public BindingPriority Priority { get; } public BindingPriority Priority { get; }
/// <summary> /// <summary>
/// Gets the value or source of the binding. /// Gets the binding source observable.
/// </summary>
public object? Value { get; }
/// <summary>
/// Gets the <see cref="Value"/> as an observable.
/// </summary> /// </summary>
public IObservable<object?>? Observable => Value as IObservable<object?>; public IObservable<object?> Source { get; }
/// <summary> [Obsolete("Use Source property")]
/// Gets the <see cref="Value"/> as an observer. public IObservable<object?> Observable => Source;
/// </summary>
public IObserver<object?>? Observer => Value as IObserver<object?>;
/// <summary>
/// Gets the <see cref="Subject"/> as an subject.
/// </summary>
internal IAvaloniaSubject<object?>? Subject => Value as IAvaloniaSubject<object?>;
/// <summary> /// <summary>
/// Creates a new one-time binding with a fixed value. /// Creates a new one-time binding with a fixed value.
@ -61,7 +62,7 @@ namespace Avalonia.Data
object value, object value,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
return new InstancedBinding(value, BindingMode.OneTime, priority); return new InstancedBinding(ObservableEx.SingleValue(value), BindingMode.OneTime, priority);
} }
/// <summary> /// <summary>
@ -106,7 +107,7 @@ namespace Avalonia.Data
{ {
_ = observer ?? throw new ArgumentNullException(nameof(observer)); _ = observer ?? throw new ArgumentNullException(nameof(observer));
return new InstancedBinding(observer, BindingMode.OneWayToSource, priority); return new InstancedBinding((IObservable<object?>)observer, BindingMode.OneWayToSource, priority);
} }
/// <summary> /// <summary>
@ -135,7 +136,7 @@ namespace Avalonia.Data
/// <returns>An <see cref="InstancedBinding"/> instance.</returns> /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public InstancedBinding WithPriority(BindingPriority priority) public InstancedBinding WithPriority(BindingPriority priority)
{ {
return new InstancedBinding(Value, Mode, priority); return new InstancedBinding(Source, Mode, priority);
} }
} }
} }

2
src/Avalonia.Base/Styling/Setter.cs

@ -109,7 +109,7 @@ namespace Avalonia.Styling
if (mode == BindingMode.OneWay || mode == BindingMode.TwoWay) if (mode == BindingMode.OneWay || mode == BindingMode.TwoWay)
{ {
return new PropertySetterBindingInstance(target, instance, Property, mode, i.Observable!); return new PropertySetterBindingInstance(target, instance, Property, mode, i.Source);
} }
throw new NotSupportedException(); throw new NotSupportedException();

5
src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs

@ -7,6 +7,7 @@ using Avalonia.Data;
using System; using System;
using Avalonia.Controls.Utils; using Avalonia.Controls.Utils;
using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Reactive;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -111,9 +112,9 @@ namespace Avalonia.Controls
if (result != null) if (result != null)
{ {
if(result.Subject != null) if(result.Source is IAvaloniaSubject<object> subject)
{ {
var bindingHelper = new CellEditBinding(result.Subject); var bindingHelper = new CellEditBinding(subject);
var instanceBinding = new InstancedBinding(bindingHelper.InternalSubject, result.Mode, result.Priority); var instanceBinding = new InstancedBinding(bindingHelper.InternalSubject, result.Mode, result.Priority);
BindingOperations.Apply(target, property, instanceBinding, null); BindingOperations.Apply(target, property, instanceBinding, null);

18
src/Avalonia.Controls/TreeViewItem.cs

@ -264,6 +264,18 @@ namespace Avalonia.Controls
Dispatcher.UIThread.Post(this.BringIntoView); // must use the Dispatcher, otherwise the TreeView doesn't scroll Dispatcher.UIThread.Post(this.BringIntoView); // must use the Dispatcher, otherwise the TreeView doesn't scroll
} }
} }
/// <summary>
/// Invoked when the <see cref="DoubleTapped"/> event occurs in the header.
/// </summary>
protected virtual void OnHeaderDoubleTapped(TappedEventArgs e)
{
if (ItemCount > 0)
{
IsExpanded = !IsExpanded;
e.Handled = true;
}
}
private static int CalculateDistanceFromLogicalParent<T>(ILogical? logical, int @default = -1) where T : class private static int CalculateDistanceFromLogicalParent<T>(ILogical? logical, int @default = -1) where T : class
{ {
@ -280,11 +292,7 @@ namespace Avalonia.Controls
private void HeaderDoubleTapped(object? sender, TappedEventArgs e) private void HeaderDoubleTapped(object? sender, TappedEventArgs e)
{ {
if (ItemCount > 0) OnHeaderDoubleTapped(e);
{
IsExpanded = !IsExpanded;
e.Handled = true;
}
} }
} }
} }

1
src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml

@ -92,6 +92,7 @@
<ContentPresenter Name="PART_HeaderPresenter" <ContentPresenter Name="PART_HeaderPresenter"
Content="{TemplateBinding Header}" Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding ItemTemplate}"
VerticalAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
RecognizesAccessKey="True" RecognizesAccessKey="True"

3
src/Avalonia.Themes.Simple/Controls/MenuItem.xaml

@ -42,7 +42,8 @@
Grid.Column="2" Grid.Column="2"
Margin="{TemplateBinding Padding}" Margin="{TemplateBinding Padding}"
VerticalAlignment="Center" VerticalAlignment="Center"
Content="{TemplateBinding Header}"> Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding ItemTemplate}">
<ContentPresenter.DataTemplates> <ContentPresenter.DataTemplates>
<DataTemplate DataType="sys:String"> <DataTemplate DataType="sys:String">
<AccessText Text="{Binding}" /> <AccessText Text="{Binding}" />

2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs

@ -35,7 +35,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
}; };
} }
protected override ExpressionObserver CreateExpressionObserver(AvaloniaObject target, AvaloniaProperty targetProperty, object anchor, bool enableDataValidation) private protected override ExpressionObserver CreateExpressionObserver(AvaloniaObject target, AvaloniaProperty targetProperty, object anchor, bool enableDataValidation)
{ {
if (Source != null) if (Source != null)
{ {

2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs

@ -24,7 +24,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
} }
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.CompiledBindingSafeSupressWarningMessage)] [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.CompiledBindingSafeSupressWarningMessage)]
public ExpressionNode BuildExpression(bool enableValidation) internal ExpressionNode BuildExpression(bool enableValidation)
{ {
ExpressionNode pathRoot = null; ExpressionNode pathRoot = null;
ExpressionNode path = null; ExpressionNode path = null;

2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/StrongTypeCastNode.cs

@ -3,7 +3,7 @@ using Avalonia.Data.Core;
namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
{ {
public class StrongTypeCastNode : TypeCastNode internal class StrongTypeCastNode : TypeCastNode
{ {
private Func<object, object> _cast; private Func<object, object> _cast;

1
src/Markup/Avalonia.Markup/Avalonia.Markup.csproj

@ -19,6 +19,7 @@
<Import Project="..\..\..\build\TrimmingEnable.props" /> <Import Project="..\..\..\build\TrimmingEnable.props" />
<ItemGroup Label="InternalsVisibleTo"> <ItemGroup Label="InternalsVisibleTo">
<InternalsVisibleTo Include="Avalonia.Markup.Xaml, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.UnitTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Markup.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
</ItemGroup> </ItemGroup>
</Project> </Project>

2
src/Markup/Avalonia.Markup/Data/Binding.cs

@ -56,7 +56,7 @@ namespace Avalonia.Data
/// </summary> /// </summary>
public Func<string, string, Type>? TypeResolver { get; set; } public Func<string, string, Type>? TypeResolver { get; set; }
protected override ExpressionObserver CreateExpressionObserver(AvaloniaObject target, AvaloniaProperty? targetProperty, object? anchor, bool enableDataValidation) private protected override ExpressionObserver CreateExpressionObserver(AvaloniaObject target, AvaloniaProperty? targetProperty, object? anchor, bool enableDataValidation)
{ {
_ = target ?? throw new ArgumentNullException(nameof(target)); _ = target ?? throw new ArgumentNullException(nameof(target));

12
src/Markup/Avalonia.Markup/Data/BindingBase.cs

@ -70,7 +70,7 @@ namespace Avalonia.Data
public WeakReference<INameScope>? NameScope { get; set; } public WeakReference<INameScope>? NameScope { get; set; }
protected abstract ExpressionObserver CreateExpressionObserver( private protected abstract ExpressionObserver CreateExpressionObserver(
AvaloniaObject target, AvaloniaObject target,
AvaloniaProperty? targetProperty, AvaloniaProperty? targetProperty,
object? anchor, object? anchor,
@ -127,7 +127,7 @@ namespace Avalonia.Data
return new InstancedBinding(subject, Mode, Priority); return new InstancedBinding(subject, Mode, Priority);
} }
protected ExpressionObserver CreateDataContextObserver( private protected ExpressionObserver CreateDataContextObserver(
AvaloniaObject target, AvaloniaObject target,
ExpressionNode node, ExpressionNode node,
bool targetIsDataContext, bool targetIsDataContext,
@ -162,7 +162,7 @@ namespace Avalonia.Data
} }
} }
protected ExpressionObserver CreateElementObserver( private protected ExpressionObserver CreateElementObserver(
StyledElement target, StyledElement target,
string elementName, string elementName,
ExpressionNode node) ExpressionNode node)
@ -178,7 +178,7 @@ namespace Avalonia.Data
return result; return result;
} }
protected ExpressionObserver CreateFindAncestorObserver( private protected ExpressionObserver CreateFindAncestorObserver(
StyledElement target, StyledElement target,
RelativeSource relativeSource, RelativeSource relativeSource,
ExpressionNode node) ExpressionNode node)
@ -211,7 +211,7 @@ namespace Avalonia.Data
null); null);
} }
protected ExpressionObserver CreateSourceObserver( private protected ExpressionObserver CreateSourceObserver(
object source, object source,
ExpressionNode node) ExpressionNode node)
{ {
@ -220,7 +220,7 @@ namespace Avalonia.Data
return new ExpressionObserver(source, node); return new ExpressionObserver(source, node);
} }
protected ExpressionObserver CreateTemplatedParentObserver( private protected ExpressionObserver CreateTemplatedParentObserver(
AvaloniaObject target, AvaloniaObject target,
ExpressionNode node) ExpressionNode node)
{ {

4
src/Markup/Avalonia.Markup/Data/MultiBinding.cs

@ -85,8 +85,8 @@ namespace Avalonia.Data
var children = Bindings.Select(x => x.Initiate(target, null)); var children = Bindings.Select(x => x.Initiate(target, null));
var input = children.Select(x => x?.Observable!) var input = children.Select(x => x?.Source)
.Where(x => x is not null) .Where(x => x is not null)!
.CombineLatest() .CombineLatest()
.Select(x => ConvertValue(x, targetType, converter)) .Select(x => ConvertValue(x, targetType, converter))
.Where(x => x != BindingOperations.DoNothing); .Where(x => x != BindingOperations.DoNothing);

2
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs

@ -6,7 +6,7 @@ using Avalonia.Utilities;
namespace Avalonia.Markup.Parsers namespace Avalonia.Markup.Parsers
{ {
public static class ExpressionObserverBuilder internal static class ExpressionObserverBuilder
{ {
[RequiresUnreferencedCode(TrimmingMessages.ReflectionBindingRequiresUnreferencedCodeMessage)] [RequiresUnreferencedCode(TrimmingMessages.ReflectionBindingRequiresUnreferencedCodeMessage)]
internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type>? typeResolver = null, internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type>? typeResolver = null,

2
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs

@ -6,7 +6,7 @@ using Avalonia.Reactive;
namespace Avalonia.Markup.Parsers.Nodes namespace Avalonia.Markup.Parsers.Nodes
{ {
public class ElementNameNode : ExpressionNode internal class ElementNameNode : ExpressionNode
{ {
private readonly WeakReference<INameScope> _nameScope; private readonly WeakReference<INameScope> _nameScope;
private readonly string _name; private readonly string _name;

2
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs

@ -5,7 +5,7 @@ using Avalonia.Reactive;
namespace Avalonia.Markup.Parsers.Nodes namespace Avalonia.Markup.Parsers.Nodes
{ {
public class FindAncestorNode : ExpressionNode internal class FindAncestorNode : ExpressionNode
{ {
private readonly int _level; private readonly int _level;
private readonly Type? _ancestorType; private readonly Type? _ancestorType;

2
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/SelfNode.cs

@ -2,7 +2,7 @@
namespace Avalonia.Markup.Parsers.Nodes namespace Avalonia.Markup.Parsers.Nodes
{ {
public class SelfNode : ExpressionNode internal class SelfNode : ExpressionNode
{ {
public override string Description => "$self"; public override string Description => "$self";
} }

6
tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs

@ -334,7 +334,7 @@ namespace Avalonia.Markup.UnitTests.Data
Path = "Foo", Path = "Foo",
}; };
var result = binding.Initiate(target, TextBox.TextProperty).Value; var result = binding.Initiate(target, TextBox.TextProperty).Source;
Assert.IsType<DefaultValueConverter>(((BindingExpression)result).Converter); Assert.IsType<DefaultValueConverter>(((BindingExpression)result).Converter);
} }
@ -350,7 +350,7 @@ namespace Avalonia.Markup.UnitTests.Data
Path = "Foo", Path = "Foo",
}; };
var result = binding.Initiate(target, TextBox.TextProperty).Value; var result = binding.Initiate(target, TextBox.TextProperty).Source;
Assert.Same(converter.Object, ((BindingExpression)result).Converter); Assert.Same(converter.Object, ((BindingExpression)result).Converter);
} }
@ -367,7 +367,7 @@ namespace Avalonia.Markup.UnitTests.Data
Path = "Bar", Path = "Bar",
}; };
var result = binding.Initiate(target, TextBox.TextProperty).Value; var result = binding.Initiate(target, TextBox.TextProperty).Source;
Assert.Same("foo", ((BindingExpression)result).ConverterParameter); Assert.Same("foo", ((BindingExpression)result).ConverterParameter);
} }

8
tests/Avalonia.Markup.UnitTests/Data/BindingTests_Converters.cs

@ -24,7 +24,7 @@ namespace Avalonia.Markup.UnitTests.Data
var expressionObserver = (BindingExpression)target.Initiate( var expressionObserver = (BindingExpression)target.Initiate(
textBlock, textBlock,
TextBlock.TextProperty).Observable; TextBlock.TextProperty).Source;
Assert.Same(StringConverters.IsNullOrEmpty, expressionObserver.Converter); Assert.Same(StringConverters.IsNullOrEmpty, expressionObserver.Converter);
} }
@ -46,7 +46,7 @@ namespace Avalonia.Markup.UnitTests.Data
var expressionObserver = (BindingExpression)target.Initiate( var expressionObserver = (BindingExpression)target.Initiate(
textBlock, textBlock,
TextBlock.TextProperty).Observable; TextBlock.TextProperty).Source;
Assert.IsType<StringFormatValueConverter>(expressionObserver.Converter); Assert.IsType<StringFormatValueConverter>(expressionObserver.Converter);
} }
@ -69,7 +69,7 @@ namespace Avalonia.Markup.UnitTests.Data
var expressionObserver = (BindingExpression)target.Initiate( var expressionObserver = (BindingExpression)target.Initiate(
textBlock, textBlock,
TextBlock.TagProperty).Observable; TextBlock.TagProperty).Source;
Assert.IsType<StringFormatValueConverter>(expressionObserver.Converter); Assert.IsType<StringFormatValueConverter>(expressionObserver.Converter);
} }
@ -92,7 +92,7 @@ namespace Avalonia.Markup.UnitTests.Data
var expressionObserver = (BindingExpression)target.Initiate( var expressionObserver = (BindingExpression)target.Initiate(
textBlock, textBlock,
TextBlock.MarginProperty).Observable; TextBlock.MarginProperty).Source;
Assert.Same(DefaultValueConverter.Instance, expressionObserver.Converter); Assert.Same(DefaultValueConverter.Instance, expressionObserver.Converter);
} }

6
tests/Avalonia.Markup.UnitTests/Data/BindingTests_DataValidation.cs

@ -20,7 +20,7 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new Binding(nameof(Class1.Foo)); var target = new Binding(nameof(Class1.Foo));
var instanced = target.Initiate(textBlock, TextBlock.TextProperty, enableDataValidation: false); var instanced = target.Initiate(textBlock, TextBlock.TextProperty, enableDataValidation: false);
var subject = (BindingExpression)instanced.Value; var subject = (BindingExpression)instanced.Source;
object result = null; object result = null;
subject.Subscribe(x => result = x); subject.Subscribe(x => result = x);
@ -38,7 +38,7 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new Binding(nameof(Class1.Foo)); var target = new Binding(nameof(Class1.Foo));
var instanced = target.Initiate(textBlock, TextBlock.TextProperty, enableDataValidation: true); var instanced = target.Initiate(textBlock, TextBlock.TextProperty, enableDataValidation: true);
var subject = (BindingExpression)instanced.Value; var subject = (BindingExpression)instanced.Source;
object result = null; object result = null;
subject.Subscribe(x => result = x); subject.Subscribe(x => result = x);
@ -56,7 +56,7 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new Binding(nameof(Class1.Foo)) { Priority = BindingPriority.Template }; var target = new Binding(nameof(Class1.Foo)) { Priority = BindingPriority.Template };
var instanced = target.Initiate(textBlock, TextBlock.TextProperty, enableDataValidation: true); var instanced = target.Initiate(textBlock, TextBlock.TextProperty, enableDataValidation: true);
var subject = (BindingExpression)instanced.Value; var subject = (BindingExpression)instanced.Source;
object result = null; object result = null;
subject.Subscribe(x => result = x); subject.Subscribe(x => result = x);

4
tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests.cs

@ -30,7 +30,7 @@ namespace Avalonia.Markup.UnitTests.Data
}; };
var target = new Control { DataContext = source }; var target = new Control { DataContext = source };
var observable = binding.Initiate(target, null).Observable; var observable = binding.Initiate(target, null).Source;
var result = await observable.Take(1); var result = await observable.Take(1);
Assert.Equal("1,2,3", result); Assert.Equal("1,2,3", result);
@ -59,7 +59,7 @@ namespace Avalonia.Markup.UnitTests.Data
}; };
var target = new Control { DataContext = source }; var target = new Control { DataContext = source };
var observable = binding.Initiate(target, null).Observable; var observable = binding.Initiate(target, null).Source;
var result = await observable.Take(1); var result = await observable.Take(1);
Assert.Equal("1,2,3", result); Assert.Equal("1,2,3", result);

Loading…
Cancel
Save