diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index 27aab099fa..a47a716d3d 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -1015,6 +1015,18 @@ baseline/netstandard2.0/Avalonia.Base.dll target/netstandard2.0/Avalonia.Base.dll + + CP0001 + T:Avalonia.Diagnostics.AppliedStyle + baseline/netstandard2.0/Avalonia.Base.dll + target/netstandard2.0/Avalonia.Base.dll + + + CP0001 + T:Avalonia.Diagnostics.StyleDiagnostics + baseline/netstandard2.0/Avalonia.Base.dll + target/netstandard2.0/Avalonia.Base.dll + CP0001 T:Avalonia.Input.FocusManager.<GetFocusScopeAncestors>d__18 @@ -1075,6 +1087,12 @@ baseline/netstandard2.0/Avalonia.Dialogs.dll target/netstandard2.0/Avalonia.Dialogs.dll + + CP0002 + M:Avalonia.Diagnostics.StyledElementExtensions.GetStyleDiagnostics(Avalonia.StyledElement) + baseline/netstandard2.0/Avalonia.Base.dll + target/netstandard2.0/Avalonia.Base.dll + CP0006 M:Avalonia.Platform.IAssetLoader.InvalidateAssemblyCache diff --git a/src/Avalonia.Base/Diagnostics/AppliedStyle.cs b/src/Avalonia.Base/Diagnostics/AppliedStyle.cs deleted file mode 100644 index 90390a85da..0000000000 --- a/src/Avalonia.Base/Diagnostics/AppliedStyle.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Avalonia.Styling; - -namespace Avalonia.Diagnostics -{ - public sealed class AppliedStyle - { - private readonly IStyleInstance _instance; - - internal AppliedStyle(IStyleInstance instance) - { - _instance = instance; - } - - public bool HasActivator => _instance.HasActivator; - public bool IsActive => _instance.IsActive; - public StyleBase Style => (StyleBase)_instance.Source; - } -} diff --git a/src/Avalonia.Base/Diagnostics/IValueFrameDiagnostic.cs b/src/Avalonia.Base/Diagnostics/IValueFrameDiagnostic.cs new file mode 100644 index 0000000000..df48efe04e --- /dev/null +++ b/src/Avalonia.Base/Diagnostics/IValueFrameDiagnostic.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Avalonia.Data; +using Avalonia.Metadata; + +namespace Avalonia.Diagnostics; + +public record ValueEntryDiagnostic(AvaloniaProperty Property, object? Value); + +[Unstable] +[NotClientImplementable] +public interface IValueFrameDiagnostic +{ + public enum FrameType + { + Unknown = 0, + Local, + Theme, + Style, + Template + } + + string? Description { get; } + FrameType Type { get; } + bool IsActive { get; } + BindingPriority Priority { get; } + IEnumerable Values { get; } +} diff --git a/src/Avalonia.Base/Diagnostics/LocalValueFrameDiagnostic.cs b/src/Avalonia.Base/Diagnostics/LocalValueFrameDiagnostic.cs new file mode 100644 index 0000000000..96d72c1480 --- /dev/null +++ b/src/Avalonia.Base/Diagnostics/LocalValueFrameDiagnostic.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Avalonia.Data; + +namespace Avalonia.Diagnostics; + +internal class LocalValueFrameDiagnostic : IValueFrameDiagnostic +{ + public LocalValueFrameDiagnostic(IEnumerable values) + { + Values = values; + } + + public string? Description => null; + public IValueFrameDiagnostic.FrameType Type => IValueFrameDiagnostic.FrameType.Local; + public bool IsActive => true; + public BindingPriority Priority => BindingPriority.LocalValue; + public IEnumerable Values { get; } +} diff --git a/src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs b/src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs deleted file mode 100644 index 90326891c6..0000000000 --- a/src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using Avalonia.Styling; - -namespace Avalonia.Diagnostics -{ - /// - /// Contains information about style related diagnostics of a control. - /// - public class StyleDiagnostics - { - /// - /// Currently applied styles. - /// - public IReadOnlyList AppliedStyles { get; } - - public StyleDiagnostics(IReadOnlyList appliedStyles) - { - AppliedStyles = appliedStyles; - } - } -} diff --git a/src/Avalonia.Base/Diagnostics/StyleValueFrameDiagnostic.cs b/src/Avalonia.Base/Diagnostics/StyleValueFrameDiagnostic.cs new file mode 100644 index 0000000000..03d901cee2 --- /dev/null +++ b/src/Avalonia.Base/Diagnostics/StyleValueFrameDiagnostic.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using Avalonia.Data; +using Avalonia.PropertyStore; +using Avalonia.Styling; + +namespace Avalonia.Diagnostics; + +internal class StyleValueFrameDiagnostic : IValueFrameDiagnostic +{ + private readonly StyleInstance _styleInstance; + + internal StyleValueFrameDiagnostic(StyleInstance styleInstance) + { + _styleInstance = styleInstance; + } + + public string? Description => _styleInstance.Source switch + { + Style s => GetFullSelector(s), + ControlTheme t => t.TargetType?.Name, + _ => null + }; + + public IValueFrameDiagnostic.FrameType Type => _styleInstance.Source switch + { + Style => IValueFrameDiagnostic.FrameType.Style, + ControlTheme => IValueFrameDiagnostic.FrameType.Theme, + _ => IValueFrameDiagnostic.FrameType.Unknown + }; + + public bool IsActive => _styleInstance.IsActive(); + public BindingPriority Priority => _styleInstance.FramePriority.ToBindingPriority(); + public IEnumerable Values + { + get + { + foreach (var setter in ((StyleBase)_styleInstance.Source!).Setters) + { + if (setter is Setter { Property: not null } regularSetter) + { + yield return new ValueEntryDiagnostic(regularSetter.Property, regularSetter.Value); + } + } + } + } + + private string GetFullSelector(Style? style) + { + var selectors = new Stack(); + + while (style is not null) + { + if (style.Selector is not null) + { + selectors.Push(style.Selector.ToString()); + } + + style = style.Parent as Style; + } + + return string.Concat(selectors); + } +} diff --git a/src/Avalonia.Base/Diagnostics/StyledElementExtensions.cs b/src/Avalonia.Base/Diagnostics/StyledElementExtensions.cs index d7bcc1aa47..32bbe78218 100644 --- a/src/Avalonia.Base/Diagnostics/StyledElementExtensions.cs +++ b/src/Avalonia.Base/Diagnostics/StyledElementExtensions.cs @@ -1,17 +1,16 @@ -namespace Avalonia.Diagnostics +namespace Avalonia.Diagnostics; + +/// +/// Defines diagnostic extensions on s. +/// +public static class StyledElementExtensions { /// - /// Defines diagnostic extensions on s. + /// Gets a style diagnostics for a . /// - public static class StyledElementExtensions + /// The element. + public static ValueStoreDiagnostic GetValueStoreDiagnostic(this StyledElement styledElement) { - /// - /// Gets a style diagnostics for a . - /// - /// The element. - public static StyleDiagnostics GetStyleDiagnostics(this StyledElement styledElement) - { - return styledElement.GetStyleDiagnosticsInternal(); - } + return styledElement.GetValueStore().GetStoreDiagnostic(); } } diff --git a/src/Avalonia.Base/Diagnostics/ValueFrameDiagnostic.cs b/src/Avalonia.Base/Diagnostics/ValueFrameDiagnostic.cs new file mode 100644 index 0000000000..9b28cf0cdb --- /dev/null +++ b/src/Avalonia.Base/Diagnostics/ValueFrameDiagnostic.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using Avalonia.Data; +using Avalonia.PropertyStore; +using Avalonia.Styling; + +namespace Avalonia.Diagnostics; + +internal sealed class ValueFrameDiagnostic : IValueFrameDiagnostic +{ + private readonly ValueFrame _valueFrame; + + internal ValueFrameDiagnostic(ValueFrame valueFrame) + { + _valueFrame = valueFrame; + } + + public string? Description => (_valueFrame.Owner?.Owner as StyledElement)?.StyleKey.Name; + + public IValueFrameDiagnostic.FrameType Type => IValueFrameDiagnostic.FrameType.Template; + + public bool IsActive => _valueFrame.IsActive(); + public BindingPriority Priority => _valueFrame.FramePriority.ToBindingPriority(); + public IEnumerable Values + { + get + { + for (var i = 0; i < _valueFrame.EntryCount; i++) + { + var entry = _valueFrame.GetEntry(i); + if (entry.HasValue()) + { + yield return new ValueEntryDiagnostic(entry.Property, entry.GetValue()); + } + } + } + } +} diff --git a/src/Avalonia.Base/Diagnostics/ValueStoreDiagnostic.cs b/src/Avalonia.Base/Diagnostics/ValueStoreDiagnostic.cs new file mode 100644 index 0000000000..e4880cdc76 --- /dev/null +++ b/src/Avalonia.Base/Diagnostics/ValueStoreDiagnostic.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using Avalonia.Styling; + +namespace Avalonia.Diagnostics; + +public class ValueStoreDiagnostic +{ + /// + /// Currently applied frames. + /// + public IReadOnlyList AppliedFrames { get; } + + internal ValueStoreDiagnostic(IReadOnlyList appliedFrames) + { + AppliedFrames = appliedFrames; + } +} diff --git a/src/Avalonia.Base/PropertyStore/FramePriority.cs b/src/Avalonia.Base/PropertyStore/FramePriority.cs index 950a8375f2..a77bbe211b 100644 --- a/src/Avalonia.Base/PropertyStore/FramePriority.cs +++ b/src/Avalonia.Base/PropertyStore/FramePriority.cs @@ -28,6 +28,12 @@ namespace Avalonia.PropertyStore return (FramePriority)(p * 3 + (int)type); } + public static BindingPriority ToBindingPriority(this FramePriority priority) + { + var p = (int)priority / 3; + return p == 0 ? BindingPriority.Animation : (BindingPriority)p; + } + public static bool IsType(this FramePriority priority, FrameType type) { return (FrameType)((int)priority % 3) == type; diff --git a/src/Avalonia.Base/PropertyStore/ValueStore.cs b/src/Avalonia.Base/PropertyStore/ValueStore.cs index 35406993c7..e443a11781 100644 --- a/src/Avalonia.Base/PropertyStore/ValueStore.cs +++ b/src/Avalonia.Base/PropertyStore/ValueStore.cs @@ -836,6 +836,40 @@ namespace Avalonia.PropertyStore } } + public ValueStoreDiagnostic GetStoreDiagnostic() + { + var frames = new List(); + + var effectiveLocalValues = new List(_effectiveValues.Count); + for (var i = 0; i < _effectiveValues.Count; i++) + { + if (_effectiveValues.GetValue(i) is { } effectiveValue + && effectiveValue.Priority == BindingPriority.LocalValue) + { + effectiveLocalValues.Add(new ValueEntryDiagnostic(effectiveValue.Property, effectiveValue.Value)); + } + } + + if (effectiveLocalValues.Count > 0) + { + frames.Add(new LocalValueFrameDiagnostic(effectiveLocalValues)); + } + + foreach (var frame in Frames) + { + if (frame is StyleInstance { Source: StyleBase } styleInstance) + { + frames.Add(new StyleValueFrameDiagnostic(styleInstance)); + } + else + { + frames.Add(new ValueFrameDiagnostic(frame)); + } + } + + return new ValueStoreDiagnostic(frames); + } + private int InsertFrame(ValueFrame frame) { Debug.Assert(!_frames.Contains(frame)); diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs index 07ea2f69bb..1f0c00ef63 100644 --- a/src/Avalonia.Base/StyledElement.cs +++ b/src/Avalonia.Base/StyledElement.cs @@ -420,19 +420,6 @@ namespace Avalonia } } - internal StyleDiagnostics GetStyleDiagnosticsInternal() - { - var styles = new List(); - - foreach (var frame in GetValueStore().Frames) - { - if (frame is IStyleInstance style) - styles.Add(new(style)); - } - - return new StyleDiagnostics(styles); - } - /// void ILogical.NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/Controls/BrushEditor.axaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Controls/BrushEditor.axaml.cs index 268e42cff3..40412a6373 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Controls/BrushEditor.axaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Controls/BrushEditor.axaml.cs @@ -23,7 +23,14 @@ namespace Avalonia.Diagnostics.Controls public BrushEditor() { FlyoutBase.SetAttachedFlyout(this, new Flyout { Content = _colorView }); - _colorView.ColorChanged += (_, e) => Brush = new ImmutableSolidColorBrush(e.NewColor); + _colorView.ColorChanged += (_, e) => + { + // Avoid unnecessary value setters by checking if color was actually changed. + if (Brush is null || Brush is not ISolidColorBrush oldSolidBrush || oldSolidBrush.Color != e.NewColor) + { + Brush = new ImmutableSolidColorBrush(e.NewColor); + } + }; clearHandler = (s, e) => Brush = default; } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index d57d3088f6..957f71292d 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -11,6 +11,7 @@ using Avalonia.Controls.Metadata; using Avalonia.Data; using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Styling; +using Avalonia.Threading; namespace Avalonia.Diagnostics.ViewModels { @@ -21,9 +22,9 @@ namespace Avalonia.Diagnostics.ViewModels private IDictionary? _propertyIndex; private PropertyViewModel? _selectedProperty; private DataGridCollectionView? _propertiesView; - private bool _snapshotStyles; - private bool _showInactiveStyles; - private string? _styleStatus; + private bool _snapshotFrames; + private bool _showInactiveFrames; + private string? _framesStatus; private object? _selectedEntity; private readonly Stack<(string Name, object Entry)> _selectedEntitiesStack = new(); private string? _selectedEntityName; @@ -46,12 +47,12 @@ namespace Avalonia.Diagnostics.ViewModels _pinnedProperties = pinnedProperties; TreePage = treePage; Layout = avaloniaObject is Visual visual - ? new ControlLayoutViewModel(visual) - : default; + ? new ControlLayoutViewModel(visual) + : default; NavigateToProperty(_avaloniaObject, (_avaloniaObject as Control)?.Name ?? _avaloniaObject.ToString()); - AppliedStyles = new ObservableCollection(); + AppliedFrames = new ObservableCollection(); PseudoClasses = new ObservableCollection(); if (avaloniaObject is StyledElement styledElement) @@ -68,64 +69,13 @@ namespace Avalonia.Diagnostics.ViewModels } } - var styleDiagnostics = styledElement.GetStyleDiagnostics(); + var styleDiagnostics = styledElement.GetValueStoreDiagnostic(); var clipboard = TopLevel.GetTopLevel(_avaloniaObject as Visual)?.Clipboard; - // We need to place styles without activator first, such styles will be overwritten by ones with activators. - foreach (var appliedStyle in styleDiagnostics.AppliedStyles.OrderBy(s => s.HasActivator)) + foreach (var appliedStyle in styleDiagnostics.AppliedFrames.OrderBy(s => s.Priority)) { - var styleSource = appliedStyle.Style; - - var setters = new List(); - - if (styleSource is StyleBase style) - { - var selector = style switch - { - Style s => s.Selector?.ToString(), - ControlTheme t => t.TargetType?.Name.ToString(), - _ => null, - }; - - foreach (var setter in style.Setters) - { - if (setter is Setter regularSetter - && regularSetter.Property != null) - { - var setterValue = regularSetter.Value; - - var resourceInfo = GetResourceInfo(setterValue); - - SetterViewModel setterVm; - - if (resourceInfo.HasValue) - { - var resourceKey = resourceInfo.Value.resourceKey; - var resourceValue = styledElement.FindResource(resourceKey); - - setterVm = new ResourceSetterViewModel(regularSetter.Property, resourceKey, resourceValue, resourceInfo.Value.isDynamic, clipboard); - } - else - { - var isBinding = IsBinding(setterValue); - - if (isBinding) - { - setterVm = new BindingSetterViewModel(regularSetter.Property, setterValue, clipboard); - } - else - { - setterVm = new SetterViewModel(regularSetter.Property, setterValue, clipboard); - } - } - - setters.Add(setterVm); - } - } - - AppliedStyles.Add(new StyleViewModel(appliedStyle, selector ?? "No selector", setters)); - } + AppliedFrames.Add(new ValueFrameViewModel(styledElement, appliedStyle, clipboard)); } UpdateStyles(); @@ -134,35 +84,6 @@ namespace Avalonia.Diagnostics.ViewModels public bool CanNavigateToParentProperty => _selectedEntitiesStack.Count >= 1; - private static (object resourceKey, bool isDynamic)? GetResourceInfo(object? value) - { - if (value is StaticResourceExtension staticResource - && staticResource.ResourceKey != null) - { - return (staticResource.ResourceKey, false); - } - else if (value is DynamicResourceExtension dynamicResource - && dynamicResource.ResourceKey != null) - { - return (dynamicResource.ResourceKey, true); - } - - return null; - } - - private static bool IsBinding(object? value) - { - switch (value) - { - case Binding: - case CompiledBindingExtension: - case TemplateBinding: - return true; - } - - return false; - } - public TreePageViewModel TreePage { get; } public DataGridCollectionView? PropertiesView @@ -171,7 +92,7 @@ namespace Avalonia.Diagnostics.ViewModels private set => RaiseAndSetIfChanged(ref _propertiesView, value); } - public ObservableCollection AppliedStyles { get; } + public ObservableCollection AppliedFrames { get; } public ObservableCollection PseudoClasses { get; } @@ -199,22 +120,22 @@ namespace Avalonia.Diagnostics.ViewModels set => RaiseAndSetIfChanged(ref _selectedProperty, value); } - public bool SnapshotStyles + public bool SnapshotFrames { - get => _snapshotStyles; - set => RaiseAndSetIfChanged(ref _snapshotStyles, value); + get => _snapshotFrames; + set => RaiseAndSetIfChanged(ref _snapshotFrames, value); } - public bool ShowInactiveStyles + public bool ShowInactiveFrames { - get => _showInactiveStyles; - set => RaiseAndSetIfChanged(ref _showInactiveStyles, value); + get => _showInactiveFrames; + set => RaiseAndSetIfChanged(ref _showInactiveFrames, value); } - public string? StyleStatus + public string? FramesStatus { - get => _styleStatus; - set => RaiseAndSetIfChanged(ref _styleStatus, value); + get => _framesStatus; + set => RaiseAndSetIfChanged(ref _framesStatus, value); } public ControlLayoutViewModel? Layout { get; } @@ -223,9 +144,9 @@ namespace Avalonia.Diagnostics.ViewModels { base.OnPropertyChanged(e); - if (e.PropertyName == nameof(SnapshotStyles)) + if (e.PropertyName == nameof(SnapshotFrames)) { - if (!SnapshotStyles) + if (!SnapshotFrames) { UpdateStyles(); } @@ -234,7 +155,7 @@ namespace Avalonia.Diagnostics.ViewModels public void UpdateStyleFilters() { - foreach (var style in AppliedStyles) + foreach (var style in AppliedFrames) { var hasVisibleSetter = false; @@ -332,17 +253,17 @@ namespace Avalonia.Diagnostics.ViewModels } } - if (!SnapshotStyles) + if (!SnapshotFrames) { - UpdateStyles(); + Dispatcher.UIThread.Post(UpdateStyles); } } void IClassesChangedListener.Changed() { - if (!SnapshotStyles) + if (!SnapshotFrames) { - UpdateStyles(); + Dispatcher.UIThread.Post(UpdateStyles); } } @@ -350,7 +271,7 @@ namespace Avalonia.Diagnostics.ViewModels { int activeCount = 0; - foreach (var style in AppliedStyles) + foreach (var style in AppliedFrames) { style.Update(); @@ -362,7 +283,7 @@ namespace Avalonia.Diagnostics.ViewModels var propertyBuckets = new Dictionary>(); - foreach (var style in AppliedStyles) + foreach (var style in AppliedFrames.Reverse()) { if (!style.IsActive) { @@ -398,7 +319,7 @@ namespace Avalonia.Diagnostics.ViewModels pseudoClass.Update(); } - StyleStatus = $"Styles ({activeCount}/{AppliedStyles.Count} active)"; + FramesStatus = $"Value Frames ({activeCount}/{AppliedFrames.Count} active)"; } private bool FilterProperty(object arg) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index 99baefc9e4..eaa5802aa5 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -242,7 +242,7 @@ namespace Avalonia.Diagnostics.ViewModels { if (Content is TreePageViewModel treeVm && treeVm.Details != null) { - treeVm.Details.SnapshotStyles = enable; + treeVm.Details.SnapshotFrames = enable; } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/StyleViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/StyleViewModel.cs deleted file mode 100644 index d60aff0646..0000000000 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/StyleViewModel.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Collections.Generic; -using Avalonia.Styling; - -namespace Avalonia.Diagnostics.ViewModels -{ - internal class StyleViewModel : ViewModelBase - { - private readonly AppliedStyle _styleInstance; - private bool _isActive; - private bool _isVisible; - - public StyleViewModel(AppliedStyle styleInstance, string name, List setters) - { - _styleInstance = styleInstance; - IsVisible = true; - Name = name; - Setters = setters; - - Update(); - } - - public bool IsActive - { - get => _isActive; - set => RaiseAndSetIfChanged(ref _isActive, value); - } - - public bool IsVisible - { - get => _isVisible; - set => RaiseAndSetIfChanged(ref _isVisible, value); - } - - public string Name { get; } - - public List Setters { get; } - - public void Update() - { - IsActive = _styleInstance.IsActive; - } - } -} diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ValueFrameViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ValueFrameViewModel.cs new file mode 100644 index 0000000000..924ac287fc --- /dev/null +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ValueFrameViewModel.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Input.Platform; +using Avalonia.Markup.Xaml.MarkupExtensions; +using Avalonia.Styling; + +namespace Avalonia.Diagnostics.ViewModels +{ + internal class ValueFrameViewModel : ViewModelBase + { + private readonly IValueFrameDiagnostic _valueFrame; + private bool _isActive; + private bool _isVisible; + + public ValueFrameViewModel(StyledElement styledElement, IValueFrameDiagnostic valueFrame, IClipboard? clipboard) + { + _valueFrame = valueFrame; + IsVisible = true; + + Description = (_valueFrame.Type, _valueFrame.Description) switch + { + (IValueFrameDiagnostic.FrameType.Local, _) => "Local Values " + _valueFrame.Description, + (IValueFrameDiagnostic.FrameType.Template, _) => "Template " + _valueFrame.Description, + (IValueFrameDiagnostic.FrameType.Theme, _) => "Theme " + _valueFrame.Description, + (_, {Length:>0}) => _valueFrame.Description, + _ => _valueFrame.Priority.ToString() + }; + + Setters = new List(); + + foreach (var (setterProperty, setterValue) in valueFrame.Values) + { + var resourceInfo = GetResourceInfo(setterValue); + + SetterViewModel setterVm; + + if (resourceInfo.HasValue) + { + var resourceKey = resourceInfo.Value.resourceKey; + var resourceValue = styledElement.FindResource(resourceKey); + + setterVm = new ResourceSetterViewModel(setterProperty, resourceKey, resourceValue, + resourceInfo.Value.isDynamic, clipboard); + } + else + { + var isBinding = IsBinding(setterValue); + + if (isBinding) + { + setterVm = new BindingSetterViewModel(setterProperty, setterValue, clipboard); + } + else + { + setterVm = new SetterViewModel(setterProperty, setterValue, clipboard); + } + } + Setters.Add(setterVm); + } + + Update(); + } + + public bool IsActive + { + get => _isActive; + set => RaiseAndSetIfChanged(ref _isActive, value); + } + + public bool IsVisible + { + get => _isVisible; + set => RaiseAndSetIfChanged(ref _isVisible, value); + } + + public string? Description { get; } + + public List Setters { get; } + + public void Update() + { + IsActive = _valueFrame.IsActive; + } + + private static (object resourceKey, bool isDynamic)? GetResourceInfo(object? value) + { + if (value is StaticResourceExtension staticResource + && staticResource.ResourceKey != null) + { + return (staticResource.ResourceKey, false); + } + else if (value is DynamicResourceExtension dynamicResource + && dynamicResource.ResourceKey != null) + { + return (dynamicResource.ResourceKey, true); + } + + return null; + } + + private static bool IsBinding(object? value) + { + switch (value) + { + case Binding: + case CompiledBindingExtension: + case TemplateBinding: + return true; + } + + return false; + } + } +} diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml index f62c0f2a72..e2ee4dd67f 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml @@ -5,6 +5,7 @@ xmlns:controls="using:Avalonia.Diagnostics.Controls" xmlns:vm="using:Avalonia.Diagnostics.ViewModels" xmlns:lb="using:Avalonia.Diagnostics.Behaviors" + xmlns:system="clr-namespace:System;assembly=System.Runtime" x:Class="Avalonia.Diagnostics.Views.ControlDetailsView" x:Name="Main" x:DataType="vm:ControlDetailsViewModel"> @@ -153,22 +154,22 @@ - - - + + + - + @@ -176,14 +177,14 @@ - + - + @@ -259,7 +260,13 @@ - + + + + + + + ( @@ -280,7 +287,13 @@ - + + + + + + +