DirectPropertyBase.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. using System;
  2. using Avalonia.Data;
  3. using Avalonia.Reactive;
  4. #nullable enable
  5. namespace Avalonia
  6. {
  7. /// <summary>
  8. /// Base class for direct properties.
  9. /// </summary>
  10. /// <typeparam name="TValue">The type of the property's value.</typeparam>
  11. /// <remarks>
  12. /// Whereas <see cref="DirectProperty{TOwner, TValue}"/> is typed on the owner type, this base
  13. /// class provides a non-owner-typed interface to a direct poperty.
  14. /// </remarks>
  15. public abstract class DirectPropertyBase<TValue> : AvaloniaProperty<TValue>
  16. {
  17. /// <summary>
  18. /// Initializes a new instance of the <see cref="DirectPropertyBase{TValue}"/> class.
  19. /// </summary>
  20. /// <param name="name">The name of the property.</param>
  21. /// <param name="ownerType">The type of the class that registers the property.</param>
  22. /// <param name="metadata">The property metadata.</param>
  23. /// <param name="enableDataValidation">
  24. /// Whether the property is interested in data validation.
  25. /// </param>
  26. protected DirectPropertyBase(
  27. string name,
  28. Type ownerType,
  29. PropertyMetadata metadata,
  30. bool enableDataValidation)
  31. : base(name, ownerType, metadata)
  32. {
  33. IsDataValidationEnabled = enableDataValidation;
  34. }
  35. /// <summary>
  36. /// Initializes a new instance of the <see cref="AvaloniaProperty"/> class.
  37. /// </summary>
  38. /// <param name="source">The property to copy.</param>
  39. /// <param name="ownerType">The new owner type.</param>
  40. /// <param name="metadata">Optional overridden metadata.</param>
  41. /// <param name="enableDataValidation">
  42. /// Whether the property is interested in data validation.
  43. /// </param>
  44. protected DirectPropertyBase(
  45. AvaloniaProperty source,
  46. Type ownerType,
  47. PropertyMetadata metadata,
  48. bool enableDataValidation)
  49. : base(source, ownerType, metadata)
  50. {
  51. IsDataValidationEnabled = enableDataValidation;
  52. }
  53. /// <summary>
  54. /// Gets the type that registered the property.
  55. /// </summary>
  56. public abstract Type Owner { get; }
  57. /// <summary>
  58. /// Gets a value that indicates whether data validation is enabled for the property.
  59. /// </summary>
  60. public bool IsDataValidationEnabled { get; }
  61. /// <summary>
  62. /// Gets the value of the property on the instance.
  63. /// </summary>
  64. /// <param name="instance">The instance.</param>
  65. /// <returns>The property value.</returns>
  66. internal abstract TValue InvokeGetter(IAvaloniaObject instance);
  67. /// <summary>
  68. /// Sets the value of the property on the instance.
  69. /// </summary>
  70. /// <param name="instance">The instance.</param>
  71. /// <param name="value">The value.</param>
  72. internal abstract void InvokeSetter(IAvaloniaObject instance, BindingValue<TValue> value);
  73. /// <summary>
  74. /// Gets the unset value for the property on the specified type.
  75. /// </summary>
  76. /// <param name="type">The type.</param>
  77. /// <returns>The unset value.</returns>
  78. public TValue GetUnsetValue(Type type)
  79. {
  80. type = type ?? throw new ArgumentNullException(nameof(type));
  81. return GetMetadata(type).UnsetValue;
  82. }
  83. /// <summary>
  84. /// Gets the property metadata for the specified type.
  85. /// </summary>
  86. /// <param name="type">The type.</param>
  87. /// <returns>
  88. /// The property metadata.
  89. /// </returns>
  90. public new DirectPropertyMetadata<TValue> GetMetadata(Type type)
  91. {
  92. return (DirectPropertyMetadata<TValue>)base.GetMetadata(type);
  93. }
  94. /// <inheritdoc/>
  95. internal override void NotifyInitialized(IAvaloniaObject o)
  96. {
  97. if (HasNotifyInitializedObservers)
  98. {
  99. var e = new AvaloniaPropertyChangedEventArgs<TValue>(
  100. o,
  101. this,
  102. default,
  103. InvokeGetter(o),
  104. BindingPriority.Unset);
  105. NotifyInitialized(e);
  106. }
  107. }
  108. /// <inheritdoc/>
  109. internal override object? RouteGetValue(IAvaloniaObject o)
  110. {
  111. return o.GetValue<TValue>(this);
  112. }
  113. /// <inheritdoc/>
  114. internal override void RouteSetValue(
  115. IAvaloniaObject o,
  116. object value,
  117. BindingPriority priority)
  118. {
  119. var v = TryConvert(value);
  120. if (v.HasValue)
  121. {
  122. o.SetValue<TValue>(this, (TValue)v.Value, priority);
  123. }
  124. else if (v.Type == BindingValueType.UnsetValue)
  125. {
  126. o.ClearValue(this);
  127. }
  128. else if (v.HasError)
  129. {
  130. throw v.Error!;
  131. }
  132. }
  133. /// <inheritdoc/>
  134. internal override IDisposable RouteBind(
  135. IAvaloniaObject o,
  136. IObservable<BindingValue<object>> source,
  137. BindingPriority priority)
  138. {
  139. var adapter = TypedBindingAdapter<TValue>.Create(o, this, source);
  140. return o.Bind<TValue>(this, adapter, priority);
  141. }
  142. internal override void RouteInheritanceParentChanged(AvaloniaObject o, IAvaloniaObject oldParent)
  143. {
  144. throw new NotSupportedException("Direct properties do not support inheritance.");
  145. }
  146. }
  147. }