diff --git a/src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs b/src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs
index b2f2974440..316e31b37e 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs
@@ -28,7 +28,12 @@ namespace Avalonia.Diagnostics.Controls
};
controller.Exit += eh;
}
-
+ RendererRoot = application.ApplicationLifetime switch
+ {
+ Lifetimes.IClassicDesktopStyleApplicationLifetime classic => classic.MainWindow.Renderer,
+ Lifetimes.ISingleViewApplicationLifetime single => (single.MainView as VisualTree.IVisual)?.VisualRoot?.Renderer,
+ _ => null
+ };
}
internal App Instance => _application;
@@ -105,5 +110,10 @@ namespace Avalonia.Diagnostics.Controls
///
public string? Name =>
_application.Name;
+
+ ///
+ /// Gets the root of the visual tree, if the control is attached to a visual tree.
+ ///
+ internal Rendering.IRenderer? RendererRoot { get; }
}
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Converters/GetTypeNameConverter.cs b/src/Avalonia.Diagnostics/Diagnostics/Converters/GetTypeNameConverter.cs
new file mode 100644
index 0000000000..6fd61cbaa1
--- /dev/null
+++ b/src/Avalonia.Diagnostics/Diagnostics/Converters/GetTypeNameConverter.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Globalization;
+using Avalonia.Data;
+using Avalonia.Data.Converters;
+
+namespace Avalonia.Diagnostics.Converters
+{
+ internal class GetTypeNameConverter : IValueConverter
+ {
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value is Type type)
+ {
+ return type.GetTypeName();
+ }
+ return BindingOperations.DoNothing;
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ return BindingOperations.DoNothing;
+ }
+ }
+}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/TypeExtesnions.cs b/src/Avalonia.Diagnostics/Diagnostics/TypeExtesnions.cs
new file mode 100644
index 0000000000..9c7e40ddb6
--- /dev/null
+++ b/src/Avalonia.Diagnostics/Diagnostics/TypeExtesnions.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Linq;
+
+namespace Avalonia.Diagnostics
+{
+ internal static class TypeExtesnions
+ {
+ private static readonly ConditionalWeakTable s_getTypeNameCache =
+ new ConditionalWeakTable();
+
+ public static string GetTypeName(this Type type)
+ {
+ if (!s_getTypeNameCache.TryGetValue(type, out var name))
+ {
+ name = type.Name;
+ if (Nullable.GetUnderlyingType(type) is Type nullable)
+ {
+ name = nullable.Name + "?";
+ }
+ else if (type.IsGenericType)
+ {
+ var definition = type.GetGenericTypeDefinition();
+ var arguments = type.GetGenericArguments();
+ name = definition.Name.Substring(0, definition.Name.IndexOf('`'));
+ name = $"{name}<{string.Join(",", arguments.Select(GetTypeName))}>";
+ }
+ s_getTypeNameCache.Add(type, name);
+ }
+ return name;
+ }
+ }
+}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs
index c23fdc166a..aa03330cc5 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs
@@ -3,10 +3,11 @@ namespace Avalonia.Diagnostics.ViewModels
internal class AvaloniaPropertyViewModel : PropertyViewModel
{
private readonly AvaloniaObject _target;
- private System.Type _type;
+ private System.Type _assignedType;
private object? _value;
private string _priority;
private string _group;
+ private readonly System.Type _propertyType;
#nullable disable
// Remove "nullable disable" after MemberNotNull will work on our CI.
@@ -20,6 +21,7 @@ namespace Avalonia.Diagnostics.ViewModels
$"[{property.OwnerType.Name}.{property.Name}]" :
property.Name;
DeclaringType = property.OwnerType;
+ _propertyType = property.PropertyType;
Update();
}
@@ -32,7 +34,7 @@ namespace Avalonia.Diagnostics.ViewModels
public override string Priority =>
_priority;
- public override System.Type Type => _type;
+ public override System.Type AssignedType => _assignedType;
public override string? Value
{
@@ -43,6 +45,7 @@ namespace Avalonia.Diagnostics.ViewModels
{
var convertedValue = ConvertFromString(value, Property.PropertyType);
_target.SetValue(Property, convertedValue);
+ Update();
}
catch { }
}
@@ -51,6 +54,7 @@ namespace Avalonia.Diagnostics.ViewModels
public override string Group => _group;
public override System.Type? DeclaringType { get; }
+ public override System.Type PropertyType => _propertyType;
// [MemberNotNull(nameof(_type), nameof(_group), nameof(_priority))]
public override void Update()
@@ -58,7 +62,7 @@ namespace Avalonia.Diagnostics.ViewModels
if (Property.IsDirect)
{
RaiseAndSetIfChanged(ref _value, _target.GetValue(Property), nameof(Value));
- RaiseAndSetIfChanged(ref _type, _value?.GetType() ?? Property.PropertyType, nameof(Type));
+ RaiseAndSetIfChanged(ref _assignedType,_value?.GetType() ?? Property.PropertyType, nameof(AssignedType));
RaiseAndSetIfChanged(ref _priority, "Direct", nameof(Priority));
_group = "Properties";
@@ -68,7 +72,7 @@ namespace Avalonia.Diagnostics.ViewModels
var val = _target.GetDiagnostic(Property);
RaiseAndSetIfChanged(ref _value, val?.Value, nameof(Value));
- RaiseAndSetIfChanged(ref _type, _value?.GetType() ?? Property.PropertyType, nameof(Type));
+ RaiseAndSetIfChanged(ref _assignedType, _value?.GetType() ?? Property.PropertyType, nameof(AssignedType));
if (val != null)
{
@@ -81,6 +85,7 @@ namespace Avalonia.Diagnostics.ViewModels
RaiseAndSetIfChanged(ref _group, "Unset", nameof(Group));
}
}
+ RaisePropertyChanged(nameof(Type));
}
}
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs
index 8cf1be154d..e2d8a30c8a 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs
@@ -5,8 +5,9 @@ namespace Avalonia.Diagnostics.ViewModels
internal class ClrPropertyViewModel : PropertyViewModel
{
private readonly object _target;
- private System.Type _type;
+ private System.Type _assignedType;
private object? _value;
+ private readonly System.Type _propertyType;
#nullable disable
// Remove "nullable disable" after MemberNotNull will work on our CI.
@@ -25,6 +26,8 @@ namespace Avalonia.Diagnostics.ViewModels
Name = property.DeclaringType.Name + '.' + property.Name;
}
DeclaringType = property.DeclaringType;
+ _propertyType = property.PropertyType;
+
Update();
}
@@ -33,7 +36,8 @@ namespace Avalonia.Diagnostics.ViewModels
public override string Name { get; }
public override string Group => "CLR Properties";
- public override System.Type Type => _type;
+ public override System.Type AssignedType => _assignedType;
+ public override System.Type PropertyType => _propertyType;
public override string? Value
{
@@ -44,6 +48,7 @@ namespace Avalonia.Diagnostics.ViewModels
{
var convertedValue = ConvertFromString(value, Property.PropertyType);
Property.SetValue(_target, convertedValue);
+ Update();
}
catch { }
}
@@ -62,7 +67,8 @@ namespace Avalonia.Diagnostics.ViewModels
{
var val = Property.GetValue(_target);
RaiseAndSetIfChanged(ref _value, val, nameof(Value));
- RaiseAndSetIfChanged(ref _type, _value?.GetType() ?? Property.PropertyType, nameof(Type));
+ RaiseAndSetIfChanged(ref _assignedType, _value?.GetType() ?? Property.PropertyType, nameof(AssignedType));
+ RaisePropertyChanged(nameof(Type));
}
}
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
index c96172d5bb..2617091a79 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
@@ -406,8 +406,8 @@ namespace Avalonia.Diagnostics.ViewModels
var selectedEntityName = SelectedEntityName;
if (selectedEntity == null
|| selectedProperty == null
- || selectedProperty.Type == typeof(string)
- || selectedProperty.Type.IsValueType
+ || selectedProperty.PropertyType == typeof(string)
+ || selectedProperty.PropertyType.IsValueType
)
return;
@@ -421,7 +421,7 @@ namespace Avalonia.Diagnostics.ViewModels
property = selectedEntity.GetType().GetProperties()
.FirstOrDefault(pi => pi.Name == selectedProperty.Name
&& pi.DeclaringType == selectedProperty.DeclaringType
- && pi.PropertyType.Name == selectedProperty.Type.Name)
+ && pi.PropertyType.Name == selectedProperty.PropertyType.Name)
?.GetValue(selectedEntity);
}
if (property == null) return;
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
index d809768a47..8bc13a9525 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
@@ -26,6 +26,7 @@ namespace Avalonia.Diagnostics.ViewModels
private bool _freezePopups;
private string? _pointerOverElementName;
private IInputRoot? _pointerOverRoot;
+ private bool _showPropertyType;
public MainViewModel(AvaloniaObject root)
{
_root = root;
@@ -76,7 +77,20 @@ namespace Avalonia.Diagnostics.ViewModels
get => _shouldVisualizeDirtyRects;
set
{
- ((TopLevel)_root).Renderer.DrawDirtyRects = value;
+ var changed = true;
+ if (_root is TopLevel topLevel && topLevel.Renderer is { })
+ {
+ topLevel.Renderer.DrawDirtyRects = value;
+ }
+ else if (_root is Controls.Application app && app.RendererRoot is { })
+ {
+ app.RendererRoot.DrawDirtyRects = value;
+ }
+ else
+ {
+ changed = false;
+ }
+ if (changed)
RaiseAndSetIfChanged(ref _shouldVisualizeDirtyRects, value);
}
}
@@ -96,8 +110,21 @@ namespace Avalonia.Diagnostics.ViewModels
get => _showFpsOverlay;
set
{
- ((TopLevel)_root).Renderer.DrawFps = value;
- RaiseAndSetIfChanged(ref _showFpsOverlay, value);
+ var changed = true;
+ if (_root is TopLevel topLevel && topLevel.Renderer is { })
+ {
+ topLevel.Renderer.DrawFps = value;
+ }
+ else if (_root is Controls.Application app && app.RendererRoot is { })
+ {
+ app.RendererRoot.DrawFps = value;
+ }
+ else
+ {
+ changed = false;
+ }
+ if(changed)
+ RaiseAndSetIfChanged(ref _showFpsOverlay, value);
}
}
@@ -263,5 +290,16 @@ namespace Avalonia.Diagnostics.ViewModels
{
StartupScreenIndex = options.StartupScreenIndex;
}
+
+ public bool ShowDettailsPropertyType
+ {
+ get => _showPropertyType;
+ private set => RaiseAndSetIfChanged(ref _showPropertyType , value);
+ }
+
+ public void ToggleShowDettailsPropertyType(object paramter)
+ {
+ ShowDettailsPropertyType = !ShowDettailsPropertyType;
+ }
}
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs
index d5909d0553..a7faf35769 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs
@@ -14,12 +14,17 @@ namespace Avalonia.Diagnostics.ViewModels
public abstract object Key { get; }
public abstract string Name { get; }
public abstract string Group { get; }
- public abstract Type Type { get; }
+ public abstract Type AssignedType { get; }
public abstract Type? DeclaringType { get; }
public abstract string? Value { get; set; }
public abstract string Priority { get; }
- public abstract bool? IsAttached { get; }
- public abstract void Update();
+ public abstract bool? IsAttached { get; }
+ public abstract void Update();
+ public abstract Type PropertyType { get; }
+ public string Type => PropertyType == AssignedType
+ ? PropertyType.GetTypeName()
+ : $"{PropertyType.GetTypeName()} {{{AssignedType.GetTypeName()}}}";
+
protected static string? ConvertToString(object? value)
{
@@ -31,7 +36,7 @@ namespace Avalonia.Diagnostics.ViewModels
var converter = TypeDescriptor.GetConverter(value);
//CollectionConverter does not deliver any important information. It just displays "(Collection)".
- if (!converter.CanConvertTo(typeof(string)) ||
+ if (!converter.CanConvertTo(typeof(string)) ||
converter.GetType() == typeof(CollectionConverter))
{
return value.ToString() ?? "(null)";
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml
index 7f924af144..d7acbbd577 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml
@@ -10,6 +10,7 @@
+
@@ -53,7 +54,18 @@
-
+
+
+
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml
index 7e0dab1d6e..381fc68cf3 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml
@@ -15,6 +15,16 @@
IsEnabled="False" />
+