diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
index d4b988acd4..fa41eacbeb 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
@@ -32,6 +32,8 @@ namespace Avalonia.Diagnostics.ViewModels
view.Filter = FilterProperty;
PropertiesView = view;
+ Layout = new ControlLayoutViewModel(control);
+
if (control is INotifyPropertyChanged inpc)
{
inpc.PropertyChanged += ControlPropertyChanged;
@@ -52,6 +54,8 @@ namespace Avalonia.Diagnostics.ViewModels
get => _selectedProperty;
set => RaiseAndSetIfChanged(ref _selectedProperty, value);
}
+
+ public ControlLayoutViewModel Layout { get; }
public void Dispose()
{
@@ -112,6 +116,8 @@ namespace Avalonia.Diagnostics.ViewModels
property.Update();
}
}
+
+ Layout.ControlPropertyChanged(sender, e);
}
private void ControlPropertyChanged(object sender, PropertyChangedEventArgs e)
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs
new file mode 100644
index 0000000000..0173f19358
--- /dev/null
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs
@@ -0,0 +1,150 @@
+using System.ComponentModel;
+using Avalonia.Controls;
+using Avalonia.Layout;
+using Avalonia.VisualTree;
+
+namespace Avalonia.Diagnostics.ViewModels
+{
+ internal class ControlLayoutViewModel : ViewModelBase
+ {
+ private readonly IVisual _control;
+ private Thickness _marginThickness;
+ private Thickness _borderThickness;
+ private Thickness _paddingThickness;
+ private string _sizeText;
+
+ public Thickness MarginThickness
+ {
+ get => _marginThickness;
+ set => RaiseAndSetIfChanged(ref _marginThickness, value);
+ }
+
+ public Thickness BorderThickness
+ {
+ get => _borderThickness;
+ set => RaiseAndSetIfChanged(ref _borderThickness, value);
+ }
+
+ public Thickness PaddingThickness
+ {
+ get => _paddingThickness;
+ set => RaiseAndSetIfChanged(ref _paddingThickness, value);
+ }
+
+ public string SizeText
+ {
+ get => _sizeText;
+ private set => RaiseAndSetIfChanged(ref _sizeText, value);
+ }
+
+ public bool HasPadding { get; }
+
+ public bool HasBorder { get; }
+
+ public ControlLayoutViewModel(IVisual control)
+ {
+ _control = control;
+
+ HasPadding = AvaloniaPropertyRegistry.Instance.IsRegistered(control, Decorator.PaddingProperty);
+ HasBorder = AvaloniaPropertyRegistry.Instance.IsRegistered(control, Border.BorderThicknessProperty);
+
+ if (control is AvaloniaObject ao)
+ {
+ MarginThickness = ao.GetValue(Layoutable.MarginProperty);
+
+ if (HasPadding)
+ {
+ PaddingThickness = ao.GetValue(Decorator.PaddingProperty);
+ }
+
+ if (HasBorder)
+ {
+ BorderThickness = ao.GetValue(Border.BorderThicknessProperty);
+ }
+ }
+
+ UpdateSize();
+ }
+
+ private bool _updatingFromControl;
+
+ protected override void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ base.OnPropertyChanged(e);
+
+ if (_updatingFromControl)
+ {
+ return;
+ }
+
+ if (_control is AvaloniaObject ao)
+ {
+ if (e.PropertyName == nameof(MarginThickness))
+ {
+ ao.SetValue(Layoutable.MarginProperty, MarginThickness);
+ }
+ else if (HasPadding && e.PropertyName == nameof(PaddingThickness))
+ {
+ ao.SetValue(Decorator.PaddingProperty, PaddingThickness);
+ }
+ else if (HasBorder && e.PropertyName == nameof(BorderThickness))
+ {
+ ao.SetValue(Border.BorderThicknessProperty, BorderThickness);
+ }
+ }
+ }
+
+ public void ControlPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
+ {
+ try
+ {
+ _updatingFromControl = true;
+
+ var updateSize = false;
+
+ if (e.Property == Visual.BoundsProperty)
+ {
+ updateSize = true;
+ }
+ else
+ {
+ if (_control is IAvaloniaObject ao)
+ {
+ if (e.Property == Layoutable.MarginProperty)
+ {
+ MarginThickness = ao.GetValue(Layoutable.MarginProperty);
+ }
+ else if (e.Property == Decorator.PaddingProperty)
+ {
+ PaddingThickness = ao.GetValue(Decorator.PaddingProperty);
+ }
+ else if (e.Property == Border.BorderThicknessProperty)
+ {
+ BorderThickness = ao.GetValue(Border.BorderThicknessProperty);
+
+ updateSize = true;
+ }
+ }
+ }
+
+ if (updateSize)
+ {
+ UpdateSize();
+ }
+ }
+ finally
+ {
+ _updatingFromControl = false;
+ }
+ }
+
+ private void UpdateSize()
+ {
+ var size = _control.Bounds;
+
+ size.Deflate(BorderThickness);
+
+ SizeText = $"{size.Width} × {size.Height}";
+ }
+ }
+}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml
index 8aad5fffd8..1487cb6dde 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml
@@ -1,38 +1,121 @@

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs
new file mode 100644
index 0000000000..58ea91c2fa
--- /dev/null
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs
@@ -0,0 +1,117 @@
+using Avalonia.Controls;
+using Avalonia.Data;
+
+namespace Avalonia.Diagnostics.Views
+{
+ internal class ThicknessEditor : ContentControl
+ {
+ public static readonly DirectProperty ThicknessProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Thickness), o => o.Thickness,
+ (o, v) => o.Thickness = v, defaultBindingMode: BindingMode.TwoWay);
+
+ public static readonly DirectProperty HeaderProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Header), o => o.Header,
+ (o, v) => o.Header = v);
+
+ public static readonly DirectProperty IsPresentProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Header), o => o.IsPresent,
+ (o, v) => o.IsPresent = v);
+
+ public static readonly DirectProperty LeftProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Left), o => o.Left, (o, v) => o.Left = v);
+
+ public static readonly DirectProperty TopProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Top), o => o.Top, (o, v) => o.Top = v);
+
+ public static readonly DirectProperty RightProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Right), o => o.Right,
+ (o, v) => o.Right = v);
+
+ public static readonly DirectProperty BottomProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Bottom), o => o.Bottom,
+ (o, v) => o.Bottom = v);
+
+
+ private Thickness _thickness;
+ private string _header;
+ private bool _isPresent = true;
+ private double _left;
+ private double _top;
+ private double _right;
+ private double _bottom;
+
+ private bool _isUpdatingThickness;
+
+ public Thickness Thickness
+ {
+ get => _thickness;
+ set => SetAndRaise(ThicknessProperty, ref _thickness, value);
+ }
+
+ public string Header
+ {
+ get => _header;
+ set => SetAndRaise(HeaderProperty, ref _header, value);
+ }
+
+ public bool IsPresent
+ {
+ get => _isPresent;
+ set => SetAndRaise(IsPresentProperty, ref _isPresent, value);
+ }
+
+ public double Left
+ {
+ get => _left;
+ set => SetAndRaise(LeftProperty, ref _left, value);
+ }
+
+ public double Top
+ {
+ get => _top;
+ set => SetAndRaise(TopProperty, ref _top, value);
+ }
+
+ public double Right
+ {
+ get => _right;
+ set => SetAndRaise(RightProperty, ref _right, value);
+ }
+
+ public double Bottom
+ {
+ get => _bottom;
+ set => SetAndRaise(BottomProperty, ref _bottom, value);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == ThicknessProperty)
+ {
+ try
+ {
+ _isUpdatingThickness = true;
+
+ var value = change.NewValue.GetValueOrDefault();
+
+ Left = value.Left;
+ Top = value.Top;
+ Right = value.Right;
+ Bottom = value.Bottom;
+ }
+ finally
+ {
+ _isUpdatingThickness = false;
+ }
+ }
+ else if (!_isUpdatingThickness &&
+ (change.Property == LeftProperty || change.Property == TopProperty ||
+ change.Property == RightProperty || change.Property == BottomProperty))
+ {
+ Thickness = new Thickness(Left, Top, Right, Bottom);
+ }
+ }
+ }
+}