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 c607542991..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; @@ -289,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" /> + + + + + + + +