Browse Source

Improve debug display of controls and related error messages (#15219)

* Improve debug display of controls and related error messages

* Move DebugDisplayHelper

---------

Co-authored-by: Max Katz <maxkatz6@outlook.com>
pull/15333/head
Julien Lebosquain 2 years ago
committed by GitHub
parent
commit
cfc0f158b3
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 9
      src/Avalonia.Base/Animation/AnimatorKeyFrame.cs
  2. 11
      src/Avalonia.Base/Animation/Animators/TransformOperationsAnimator.cs
  3. 15
      src/Avalonia.Base/Animation/KeyFrame.cs
  4. 8
      src/Avalonia.Base/Animation/TransitionBase.cs
  5. 12
      src/Avalonia.Base/Animation/Transitions.cs
  6. 30
      src/Avalonia.Base/AvaloniaObject.cs
  7. 54
      src/Avalonia.Base/Collections/AvaloniaList.cs
  8. 6
      src/Avalonia.Base/Collections/IAvaloniaListItemValidator.cs
  9. 49
      src/Avalonia.Base/Diagnostics/DebugDisplayHelper.cs
  10. 17
      src/Avalonia.Base/StyledElement.cs
  11. 18
      src/Avalonia.Base/Styling/Styles.cs
  12. 1
      src/Avalonia.Base/Threading/DispatcherOperation.cs
  13. 18
      src/Avalonia.Base/Visual.cs
  14. 12
      src/Avalonia.Controls/ContentControl.cs
  15. 19
      src/Avalonia.Controls/Controls.cs
  16. 17
      src/Avalonia.Controls/NativeMenu.cs
  17. 11
      src/Avalonia.Controls/NativeMenuItem.cs
  18. 13
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  19. 11
      src/Avalonia.Controls/Primitives/HeaderedContentControl.cs
  20. 11
      src/Avalonia.Controls/Templates/DataTemplates.cs
  21. 15
      src/Avalonia.Controls/TextBlock.cs

9
src/Avalonia.Base/Animation/AnimatorKeyFrame.cs

@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Avalonia.Animation.Animators;
using Avalonia.Data;
using Avalonia.Reactive;
@ -75,5 +76,13 @@ namespace Avalonia.Animation
return (T)typeConv.ConvertTo(Value, typeof(T))!;
}
internal override void BuildDebugDisplay(StringBuilder builder, bool includeContent)
{
base.BuildDebugDisplay(builder, includeContent);
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Property), Property, includeContent);
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Cue), Cue, includeContent);
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Value), Value, includeContent);
}
}
}

11
src/Avalonia.Base/Animation/Animators/TransformOperationsAnimator.cs

@ -1,14 +1,15 @@
using System;
using Avalonia.Collections;
using Avalonia.Media;
using Avalonia.Media.Transformation;
namespace Avalonia.Animation.Animators
{
internal class TransformOperationsAnimator : Animator<TransformOperations>
internal class TransformOperationsAnimator : Animator<TransformOperations>, IAvaloniaListItemValidator<AnimatorKeyFrame>
{
public TransformOperationsAnimator()
{
Validate = ValidateTransform;
Validator = this;
}
public override TransformOperations Interpolate(double progress, TransformOperations oldValue, TransformOperations newValue)
@ -24,11 +25,11 @@ namespace Avalonia.Animation.Animators
return value as TransformOperations ?? TransformOperations.Identity;
}
private void ValidateTransform(AnimatorKeyFrame kf)
void IAvaloniaListItemValidator<AnimatorKeyFrame>.Validate(AnimatorKeyFrame item)
{
if (!(kf.Value is TransformOperations))
if (item.Value is not TransformOperations)
{
throw new InvalidOperationException($"All keyframes must be of type {typeof(TransformOperations)}.");
throw new InvalidOperationException($"{item.DebugDisplay} must have a value of type {typeof(TransformOperations)}.");
}
}
}

15
src/Avalonia.Base/Animation/KeyFrame.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Collections;
using Avalonia.Metadata;
@ -95,6 +96,20 @@ namespace Avalonia.Animation
}
}
internal override void BuildDebugDisplay(StringBuilder builder, bool includeContent)
{
base.BuildDebugDisplay(builder, includeContent);
switch (TimingMode)
{
case KeyFrameTimingMode.TimeSpan:
DebugDisplayHelper.AppendOptionalValue(builder, nameof(KeyTime), KeyTime, includeContent);
break;
case KeyFrameTimingMode.Cue:
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Cue), Cue, includeContent);
break;
}
}
}
}

8
src/Avalonia.Base/Animation/TransitionBase.cs

@ -1,5 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Avalonia.Animation.Easings;
namespace Avalonia.Animation
@ -96,5 +97,12 @@ namespace Avalonia.Animation
=> Apply(control, clock, oldValue, newValue);
internal abstract IDisposable Apply(Animatable control, IClock clock, object? oldValue, object? newValue);
internal override void BuildDebugDisplay(StringBuilder builder, bool includeContent)
{
base.BuildDebugDisplay(builder, includeContent);
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Property), Property, includeContent);
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Duration), Duration, includeContent);
}
}
}

12
src/Avalonia.Base/Animation/Transitions.cs

@ -7,7 +7,7 @@ namespace Avalonia.Animation
/// <summary>
/// A collection of <see cref="ITransition"/> definitions.
/// </summary>
public sealed class Transitions : AvaloniaList<ITransition>
public sealed class Transitions : AvaloniaList<ITransition>, IAvaloniaListItemValidator<ITransition>
{
/// <summary>
/// Initializes a new instance of the <see cref="Transitions"/> class.
@ -15,16 +15,18 @@ namespace Avalonia.Animation
public Transitions()
{
ResetBehavior = ResetBehavior.Remove;
Validate = ValidateTransition;
Validator = this;
}
private void ValidateTransition(ITransition obj)
void IAvaloniaListItemValidator<ITransition>.Validate(ITransition item)
{
Dispatcher.UIThread.VerifyAccess();
if (obj.Property.IsDirect)
var property = item.Property;
if (property.IsDirect)
{
throw new InvalidOperationException("Cannot animate a direct property.");
var display = item is TransitionBase transition ? transition.DebugDisplay : item.ToString();
throw new InvalidOperationException($"Cannot animate direct property {property} on {display}.");
}
}
}

30
src/Avalonia.Base/AvaloniaObject.cs

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using Avalonia.Data;
using Avalonia.Data.Core;
using Avalonia.Diagnostics;
@ -18,6 +20,7 @@ namespace Avalonia
/// <remarks>
/// This class is analogous to DependencyObject in WPF.
/// </remarks>
[DebuggerDisplay("{DebugDisplay}")]
public class AvaloniaObject : IAvaloniaObjectDebug, INotifyPropertyChanged
{
private readonly ValueStore _values;
@ -101,6 +104,11 @@ namespace Avalonia
set { this.Bind(binding.Property!, value); }
}
/// <summary>
/// Gets a string to display inside the debugger for this object.
/// </summary>
internal string DebugDisplay => GetDebugDisplay(true);
/// <summary>
/// Returns a value indicating whether the current thread is the UI thread.
/// </summary>
@ -860,6 +868,28 @@ namespace Avalonia
priority);
}
internal string GetDebugDisplay(bool includeContent)
{
var builder = new StringBuilder();
BuildDebugDisplay(builder, includeContent);
return builder.ToString();
}
internal virtual void BuildDebugDisplay(StringBuilder builder, bool includeContent)
{
var type = GetType();
if (type.Namespace is { } ns &&
(ns == "Avalonia" || ns.StartsWith("Avalonia.", StringComparison.Ordinal)))
{
builder.Append(type.Name);
}
else
{
builder.Append(ToString());
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ValidatePriority(BindingPriority priority)
{

54
src/Avalonia.Base/Collections/AvaloniaList.cs

@ -115,7 +115,34 @@ namespace Avalonia.Collections
/// Gets or sets a validation routine that can be used to validate items before they are
/// added.
/// </summary>
public Action<T>? Validate { get; set; }
public Action<T>? Validate
{
get => Validator switch
{
null => null,
ItemValidator itemValidator => itemValidator.Validate,
{ } other => other.Validate
};
set
{
if (value is null)
{
Validator = null;
return;
}
if (Validator is ItemValidator itemValidator)
{
itemValidator.Validate = value;
}
else
{
Validator = new ItemValidator(value);
}
}
}
internal IAvaloniaListItemValidator<T>? Validator { get; set; }
/// <inheritdoc/>
bool IList.IsFixedSize => false;
@ -149,7 +176,7 @@ namespace Avalonia.Collections
set
{
Validate?.Invoke(value);
Validator?.Validate(value);
T old = _inner[index];
@ -196,7 +223,7 @@ namespace Avalonia.Collections
/// <param name="item">The item.</param>
public virtual void Add(T item)
{
Validate?.Invoke(item);
Validator?.Validate(item);
int index = _inner.Count;
_inner.Add(item);
NotifyAdd(item, index);
@ -303,7 +330,7 @@ namespace Avalonia.Collections
/// <param name="item">The item.</param>
public virtual void Insert(int index, T item)
{
Validate?.Invoke(item);
Validator?.Validate(item);
_inner.Insert(index, item);
NotifyAdd(item, index);
}
@ -318,7 +345,7 @@ namespace Avalonia.Collections
_ = items ?? throw new ArgumentNullException(nameof(items));
bool willRaiseCollectionChanged = _collectionChanged != null;
bool hasValidation = Validate != null;
bool hasValidation = Validator is not null;
if (items is IList list)
{
@ -330,7 +357,7 @@ namespace Avalonia.Collections
{
foreach (T item in collection)
{
Validate!(item);
Validator!.Validate(item);
}
}
@ -351,7 +378,7 @@ namespace Avalonia.Collections
if (hasValidation)
{
Validate!(item);
Validator!.Validate(item);
}
_inner.Insert(insertIndex++, item);
@ -381,7 +408,7 @@ namespace Avalonia.Collections
if (hasValidation)
{
Validate!(item);
Validator!.Validate(item);
}
_inner.Insert(insertIndex++, item);
@ -772,6 +799,17 @@ namespace Avalonia.Collections
_innerEnumerator.Dispose();
}
}
private sealed class ItemValidator : IAvaloniaListItemValidator<T>
{
public ItemValidator(Action<T> validate)
=> Validate = validate;
public Action<T> Validate { get; set; }
void IAvaloniaListItemValidator<T>.Validate(T item)
=> Validate(item);
}
}
internal static class EventArgsCache

6
src/Avalonia.Base/Collections/IAvaloniaListItemValidator.cs

@ -0,0 +1,6 @@
namespace Avalonia.Collections;
internal interface IAvaloniaListItemValidator<T>
{
void Validate(T item);
}

49
src/Avalonia.Base/Diagnostics/DebugDisplayHelper.cs

@ -0,0 +1,49 @@
using System.Text;
namespace Avalonia;
internal static class DebugDisplayHelper
{
public static void AppendOptionalValue(StringBuilder builder, string name, object? value, bool includeContent)
{
const int maxValueLength = 50;
if (value is null or string { Length: 0 })
{
return;
}
if (builder.Length > 0 && builder[builder.Length - 1] == ')')
{
--builder.Length;
builder.Append(", ");
}
else
{
builder.Append(" (");
}
builder.Append(name);
builder.Append(" = ");
if (value is AvaloniaObject avaloniaObject)
{
avaloniaObject.BuildDebugDisplay(builder, includeContent);
}
else
{
var stringValue = value.ToString();
if (stringValue?.Length > maxValueLength)
{
builder.Append(stringValue, 0, maxValueLength - 1);
builder.Append('…');
}
else
{
builder.Append(stringValue);
}
}
builder.Append(')');
}
}

17
src/Avalonia.Base/StyledElement.cs

@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Avalonia.Animation;
using Avalonia.Collections;
using Avalonia.Controls;
@ -34,6 +34,7 @@ namespace Avalonia
ISetInheritanceParent,
ISupportInitialize,
INamed,
IAvaloniaListItemValidator<ILogical>,
#pragma warning disable CS0618 // Type or member is obsolete
IStyleable
#pragma warning restore CS0618 // Type or member is obsolete
@ -277,7 +278,7 @@ namespace Avalonia
var list = new AvaloniaList<ILogical>
{
ResetBehavior = ResetBehavior.Remove,
Validate = logical => ValidateLogicalChild(logical)
Validator = this
};
list.CollectionChanged += LogicalChildrenCollectionChanged;
_logicalChildren = list;
@ -787,11 +788,11 @@ namespace Avalonia
return null;
}
private static void ValidateLogicalChild(ILogical c)
void IAvaloniaListItemValidator<ILogical>.Validate(ILogical item)
{
if (c == null)
if (item is null)
{
throw new ArgumentException("Cannot add null to LogicalChildren.");
throw new ArgumentException($"Cannot add null to {nameof(LogicalChildren)}.");
}
}
@ -1018,6 +1019,12 @@ namespace Avalonia
}
}
internal override void BuildDebugDisplay(StringBuilder builder, bool includeContent)
{
base.BuildDebugDisplay(builder, includeContent);
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Name), Name, includeContent);
}
private static IReadOnlyList<Style>? FlattenStyles(IReadOnlyList<IStyle> styles)
{
List<Style>? result = null;

18
src/Avalonia.Base/Styling/Styles.cs

@ -14,7 +14,8 @@ namespace Avalonia.Styling
public class Styles : AvaloniaObject,
IAvaloniaList<IStyle>,
IStyle,
IResourceProvider
IResourceProvider,
IAvaloniaListItemValidator<IStyle>
{
private readonly AvaloniaList<IStyle> _styles = new();
private IResourceHost? _owner;
@ -24,11 +25,7 @@ namespace Avalonia.Styling
{
_styles.ResetBehavior = ResetBehavior.Remove;
_styles.CollectionChanged += OnCollectionChanged;
_styles.Validate = i =>
{
if (i is ControlTheme)
throw new InvalidOperationException("ControlThemes cannot be added to a Styles collection.");
};
_styles.Validator = this;
}
public Styles(IResourceHost owner)
@ -311,5 +308,14 @@ namespace Avalonia.Styling
CollectionChanged?.Invoke(this, e);
}
void IAvaloniaListItemValidator<IStyle>.Validate(IStyle item)
{
if (item is ControlTheme theme)
{
throw new InvalidOperationException(
$"{nameof(ControlTheme)} (for {theme.TargetType}) cannot be added to a {nameof(Styles)} collection.");
}
}
}
}

1
src/Avalonia.Base/Threading/DispatcherOperation.cs

@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;

18
src/Avalonia.Base/Visual.cs

@ -29,7 +29,7 @@ namespace Avalonia
/// extension methods defined in <see cref="VisualExtensions"/>.
/// </remarks>
[UsableDuringInitialization]
public partial class Visual : StyledElement
public partial class Visual : StyledElement, IAvaloniaListItemValidator<Visual>
{
/// <summary>
/// Defines the <see cref="Bounds"/> property.
@ -150,7 +150,7 @@ namespace Avalonia
var visualChildren = new AvaloniaList<Visual>();
visualChildren.ResetBehavior = ResetBehavior.Remove;
visualChildren.Validate = visual => ValidateVisualChild(visual);
visualChildren.Validator = this;
visualChildren.CollectionChanged += VisualChildrenChanged;
VisualChildren = visualChildren;
}
@ -618,17 +618,19 @@ namespace Avalonia
/// <summary>
/// Ensures a visual child is not null and not already parented.
/// </summary>
/// <param name="c">The visual child.</param>
private static void ValidateVisualChild(Visual c)
/// <param name="item">The visual child.</param>
void IAvaloniaListItemValidator<Visual>.Validate(Visual item)
{
if (c == null)
if (item is null)
{
throw new ArgumentNullException(nameof(c), "Cannot add null to VisualChildren.");
throw new ArgumentNullException(nameof(item), $"Cannot add null to {nameof(VisualChildren)}.");
}
if (c.VisualParent != null)
if (item.VisualParent is { } parent)
{
throw new InvalidOperationException("The control already has a visual parent.");
throw new InvalidOperationException(
$"The control {item.DebugDisplay} already has a visual parent {parent.GetDebugDisplay(false)} " +
$"while trying to add it as a child of {GetDebugDisplay(false)}.");
}
}

12
src/Avalonia.Controls/ContentControl.cs

@ -1,6 +1,6 @@
using System.Text;
using Avalonia.Collections;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
@ -152,5 +152,15 @@ namespace Avalonia.Controls
LogicalChildren.Add(newChild);
}
}
internal override void BuildDebugDisplay(StringBuilder builder, bool includeContent)
{
base.BuildDebugDisplay(builder, includeContent);
if (includeContent)
{
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Content), Content, includeContent);
}
}
}
}

19
src/Avalonia.Controls/Controls.cs

@ -7,7 +7,7 @@ namespace Avalonia.Controls
/// <summary>
/// A collection of <see cref="Control"/>s.
/// </summary>
public class Controls : AvaloniaList<Control>
public class Controls : AvaloniaList<Control>, IAvaloniaListItemValidator<Control>
{
/// <summary>
/// Initializes a new instance of the <see cref="Controls"/> class.
@ -30,14 +30,17 @@ namespace Avalonia.Controls
private void Configure()
{
ResetBehavior = ResetBehavior.Remove;
Validate = item =>
Validator = this;
}
void IAvaloniaListItemValidator<Control>.Validate(Control item)
{
if (item is null)
{
if (item is null)
{
throw new ArgumentNullException(nameof(item),
$"A null control cannot be added to a {nameof(Controls)} collection.");
}
};
throw new ArgumentNullException(
nameof(item),
$"A null control cannot be added to a {nameof(Controls)} collection.");
}
}
}
}

17
src/Avalonia.Controls/NativeMenu.cs

@ -7,7 +7,10 @@ using Avalonia.Metadata;
namespace Avalonia.Controls
{
public partial class NativeMenu : AvaloniaObject, IEnumerable<NativeMenuItemBase>, INativeMenuExporterEventsImplBridge
public partial class NativeMenu : AvaloniaObject,
IEnumerable<NativeMenuItemBase>,
INativeMenuExporterEventsImplBridge,
IAvaloniaListItemValidator<NativeMenuItemBase>
{
private readonly AvaloniaList<NativeMenuItemBase> _items =
new AvaloniaList<NativeMenuItemBase> { ResetBehavior = ResetBehavior.Remove };
@ -43,7 +46,7 @@ namespace Avalonia.Controls
public NativeMenu()
{
_items.Validate = Validator;
_items.Validator = this;
_items.CollectionChanged += ItemsChanged;
}
@ -62,10 +65,14 @@ namespace Avalonia.Controls
Closed?.Invoke(this, EventArgs.Empty);
}
private void Validator(NativeMenuItemBase obj)
void IAvaloniaListItemValidator<NativeMenuItemBase>.Validate(NativeMenuItemBase item)
{
if (obj.Parent != null)
throw new InvalidOperationException("NativeMenuItem already has a parent");
if (item.Parent is { } parent)
{
throw new InvalidOperationException(
$"The menu item {item.DebugDisplay} already has a parent {parent.DebugDisplay} " +
$"while trying to add it as a child of {DebugDisplay}.");
}
}
private void ItemsChanged(object? sender, NotifyCollectionChangedEventArgs e)

11
src/Avalonia.Controls/NativeMenuItem.cs

@ -1,4 +1,5 @@
using System;
using System.Text;
using System.Windows.Input;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
@ -216,6 +217,16 @@ namespace Avalonia.Controls
CanExecuteChanged();
}
}
internal override void BuildDebugDisplay(StringBuilder builder, bool includeContent)
{
base.BuildDebugDisplay(builder, includeContent);
if (includeContent)
{
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Header), Header, true);
}
}
}
// TODO12: remove this enum and use MenuItemToggleType only

13
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -1,4 +1,5 @@
using System;
using System.Text;
using Avalonia.Collections;
using Avalonia.Controls.Documents;
using Avalonia.Controls.Metadata;
@ -721,5 +722,17 @@ namespace Avalonia.Controls.Presenters
var host = e.NewValue as IContentPresenterHost;
Host = host?.RegisterContentPresenter(this) == true ? host : null;
}
internal override void BuildDebugDisplay(StringBuilder builder, bool includeContent)
{
base.BuildDebugDisplay(builder, includeContent);
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Host), Host, false);
if (includeContent)
{
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Content), Content, true);
}
}
}
}

11
src/Avalonia.Controls/Primitives/HeaderedContentControl.cs

@ -1,3 +1,4 @@
using System.Text;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
@ -84,5 +85,15 @@ namespace Avalonia.Controls.Primitives
LogicalChildren.Add(newChild);
}
}
internal override void BuildDebugDisplay(StringBuilder builder, bool includeContent)
{
base.BuildDebugDisplay(builder, includeContent);
if (includeContent)
{
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Header), Header, true);
}
}
}
}

11
src/Avalonia.Controls/Templates/DataTemplates.cs

@ -6,7 +6,7 @@ namespace Avalonia.Controls.Templates
/// <summary>
/// A collection of <see cref="IDataTemplate"/>s.
/// </summary>
public class DataTemplates : AvaloniaList<IDataTemplate>
public class DataTemplates : AvaloniaList<IDataTemplate>, IAvaloniaListItemValidator<IDataTemplate>
{
/// <summary>
/// Initializes a new instance of the <see cref="DataTemplates"/> class.
@ -14,13 +14,12 @@ namespace Avalonia.Controls.Templates
public DataTemplates()
{
ResetBehavior = ResetBehavior.Remove;
Validate += ValidateDataTemplate;
Validator = this;
}
private static void ValidateDataTemplate(IDataTemplate template)
void IAvaloniaListItemValidator<IDataTemplate>.Validate(IDataTemplate item)
{
var valid = template switch
var valid = item switch
{
ITypedDataTemplate typed => typed.DataType is not null,
_ => true
@ -32,4 +31,4 @@ namespace Avalonia.Controls.Templates
}
}
}
}
}

15
src/Avalonia.Controls/TextBlock.cs

@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Avalonia.Automation.Peers;
using Avalonia.Collections;
using Avalonia.Controls.Documents;
@ -15,7 +15,6 @@ namespace Avalonia.Controls
/// <summary>
/// A control that displays a block of text.
/// </summary>
[DebuggerDisplay("Text = {" + nameof(DebugText) + "}")]
public class TextBlock : Control, IInlineHost
{
/// <summary>
@ -218,8 +217,6 @@ namespace Avalonia.Controls
set => SetValue(TextProperty, value);
}
private string? DebugText => Text ?? Inlines?.Text;
/// <summary>
/// Gets or sets the font family used to draw the control's text.
/// </summary>
@ -862,6 +859,16 @@ namespace Avalonia.Controls
IAvaloniaList<Visual> IInlineHost.VisualChildren => VisualChildren;
internal override void BuildDebugDisplay(StringBuilder builder, bool includeContent)
{
base.BuildDebugDisplay(builder, includeContent);
if (includeContent)
{
DebugDisplayHelper.AppendOptionalValue(builder, nameof(Text), Text ?? Inlines?.Text, true);
}
}
protected readonly record struct SimpleTextSource : ITextSource
{
private readonly string _text;

Loading…
Cancel
Save