Browse Source

Improve DevTools previewer of setter values by including all value priorities (#13802)

* Replace "Active Styles" dev tools analysis with "Active Value Frames"

* Remove old diagnostic methods from public API (breaking change)

* Show full style selectors

* Avoid unnecessary value setters by checking if color was actually changed

* Run UpdateStyles from the dispatcher to fix flickering issue

* Fix build
pull/15676/head
Max Katz 2 years ago
committed by GitHub
parent
commit
6938183319
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 18
      api/Avalonia.nupkg.xml
  2. 18
      src/Avalonia.Base/Diagnostics/AppliedStyle.cs
  3. 27
      src/Avalonia.Base/Diagnostics/IValueFrameDiagnostic.cs
  4. 18
      src/Avalonia.Base/Diagnostics/LocalValueFrameDiagnostic.cs
  5. 21
      src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs
  6. 63
      src/Avalonia.Base/Diagnostics/StyleValueFrameDiagnostic.cs
  7. 21
      src/Avalonia.Base/Diagnostics/StyledElementExtensions.cs
  8. 37
      src/Avalonia.Base/Diagnostics/ValueFrameDiagnostic.cs
  9. 17
      src/Avalonia.Base/Diagnostics/ValueStoreDiagnostic.cs
  10. 6
      src/Avalonia.Base/PropertyStore/FramePriority.cs
  11. 34
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  12. 13
      src/Avalonia.Base/StyledElement.cs
  13. 9
      src/Avalonia.Diagnostics/Diagnostics/Controls/BrushEditor.axaml.cs
  14. 139
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
  15. 2
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
  16. 43
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/StyleViewModel.cs
  17. 117
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ValueFrameViewModel.cs
  18. 31
      src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml

18
api/Avalonia.nupkg.xml

@ -1015,6 +1015,18 @@
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Diagnostics.AppliedStyle</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Diagnostics.StyleDiagnostics</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Input.FocusManager.&lt;GetFocusScopeAncestors&gt;d__18</Target>
@ -1075,6 +1087,12 @@
<Left>baseline/netstandard2.0/Avalonia.Dialogs.dll</Left>
<Right>target/netstandard2.0/Avalonia.Dialogs.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Diagnostics.StyledElementExtensions.GetStyleDiagnostics(Avalonia.StyledElement)</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.IAssetLoader.InvalidateAssemblyCache</Target>

18
src/Avalonia.Base/Diagnostics/AppliedStyle.cs

@ -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;
}
}

27
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<ValueEntryDiagnostic> Values { get; }
}

18
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<ValueEntryDiagnostic> 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<ValueEntryDiagnostic> Values { get; }
}

21
src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs

@ -1,21 +0,0 @@
using System.Collections.Generic;
using Avalonia.Styling;
namespace Avalonia.Diagnostics
{
/// <summary>
/// Contains information about style related diagnostics of a control.
/// </summary>
public class StyleDiagnostics
{
/// <summary>
/// Currently applied styles.
/// </summary>
public IReadOnlyList<AppliedStyle> AppliedStyles { get; }
public StyleDiagnostics(IReadOnlyList<AppliedStyle> appliedStyles)
{
AppliedStyles = appliedStyles;
}
}
}

63
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<ValueEntryDiagnostic> 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<string>();
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);
}
}

21
src/Avalonia.Base/Diagnostics/StyledElementExtensions.cs

@ -1,17 +1,16 @@
namespace Avalonia.Diagnostics
namespace Avalonia.Diagnostics;
/// <summary>
/// Defines diagnostic extensions on <see cref="StyledElement"/>s.
/// </summary>
public static class StyledElementExtensions
{
/// <summary>
/// Defines diagnostic extensions on <see cref="StyledElement"/>s.
/// Gets a style diagnostics for a <see cref="StyledElement"/>.
/// </summary>
public static class StyledElementExtensions
/// <param name="styledElement">The element.</param>
public static ValueStoreDiagnostic GetValueStoreDiagnostic(this StyledElement styledElement)
{
/// <summary>
/// Gets a style diagnostics for a <see cref="StyledElement"/>.
/// </summary>
/// <param name="styledElement">The element.</param>
public static StyleDiagnostics GetStyleDiagnostics(this StyledElement styledElement)
{
return styledElement.GetStyleDiagnosticsInternal();
}
return styledElement.GetValueStore().GetStoreDiagnostic();
}
}

37
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<ValueEntryDiagnostic> 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());
}
}
}
}
}

17
src/Avalonia.Base/Diagnostics/ValueStoreDiagnostic.cs

@ -0,0 +1,17 @@
using System.Collections.Generic;
using Avalonia.Styling;
namespace Avalonia.Diagnostics;
public class ValueStoreDiagnostic
{
/// <summary>
/// Currently applied frames.
/// </summary>
public IReadOnlyList<IValueFrameDiagnostic> AppliedFrames { get; }
internal ValueStoreDiagnostic(IReadOnlyList<IValueFrameDiagnostic> appliedFrames)
{
AppliedFrames = appliedFrames;
}
}

6
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;

34
src/Avalonia.Base/PropertyStore/ValueStore.cs

@ -836,6 +836,40 @@ namespace Avalonia.PropertyStore
}
}
public ValueStoreDiagnostic GetStoreDiagnostic()
{
var frames = new List<IValueFrameDiagnostic>();
var effectiveLocalValues = new List<ValueEntryDiagnostic>(_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));

13
src/Avalonia.Base/StyledElement.cs

@ -420,19 +420,6 @@ namespace Avalonia
}
}
internal StyleDiagnostics GetStyleDiagnosticsInternal()
{
var styles = new List<AppliedStyle>();
foreach (var frame in GetValueStore().Frames)
{
if (frame is IStyleInstance style)
styles.Add(new(style));
}
return new StyleDiagnostics(styles);
}
/// <inheritdoc/>
void ILogical.NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{

9
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;
}

139
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<object, PropertyViewModel[]>? _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<StyleViewModel>();
AppliedFrames = new ObservableCollection<ValueFrameViewModel>();
PseudoClasses = new ObservableCollection<PseudoClassViewModel>();
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<SetterViewModel>();
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<StyleViewModel> AppliedStyles { get; }
public ObservableCollection<ValueFrameViewModel> AppliedFrames { get; }
public ObservableCollection<PseudoClassViewModel> 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<AvaloniaProperty, List<SetterViewModel>>();
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)

2
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;
}
}

43
src/Avalonia.Diagnostics/Diagnostics/ViewModels/StyleViewModel.cs

@ -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<SetterViewModel> 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<SetterViewModel> Setters { get; }
public void Update()
{
IsActive = _styleInstance.IsActive;
}
}
}

117
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<SetterViewModel>();
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<SetterViewModel> 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;
}
}
}

31
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 @@
<Grid Grid.Row="0" Margin="4" RowDefinitions="Auto,Auto">
<Grid Grid.Row="0" Margin="2" ColumnDefinitions="Auto,*,Auto,Auto">
<TextBlock FontWeight="Bold" Grid.Column="0" Text="{Binding StyleStatus}" VerticalAlignment="Center" />
<CheckBox Margin="2,0,0,0" Grid.Column="2" Content="Show inactive" IsChecked="{Binding ShowInactiveStyles}" ToolTip.Tip="Show styles that are currently inactive" />
<ToggleButton Margin="2,0,0,0" Grid.Column="3" ToolTip.Tip="Snapshot current styles (Alt+S/Alt+D to enable/disable within debugged window)" Content="Snapshot" IsChecked="{Binding SnapshotStyles}" />
<TextBlock FontWeight="Bold" Grid.Column="0" Text="{Binding FramesStatus}" VerticalAlignment="Center" />
<CheckBox Margin="2,0,0,0" Grid.Column="2" Content="Show inactive" IsChecked="{Binding ShowInactiveFrames}" ToolTip.Tip="Show values that are currently inactive" />
<ToggleButton Margin="2,0,0,0" Grid.Column="3" ToolTip.Tip="Snapshot current values (Alt+S/Alt+D to enable/disable within debugged window)" Content="Snapshot" IsChecked="{Binding SnapshotFrames}" />
</Grid>
<controls:FilterTextBox Grid.Row="1" Margin="2" Grid.Column="0"
DataContext="{Binding TreePage.SettersFilter}"
Text="{Binding FilterString}"
Watermark="Filter setters"
Watermark="Filter values"
UseCaseSensitiveFilter="{Binding UseCaseSensitiveFilter}"
UseWholeWordFilter="{Binding UseWholeWordFilter}"
UseRegexFilter="{Binding UseRegexFilter}" />
</Grid>
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Disabled">
<ItemsControl ItemsSource="{Binding AppliedStyles}" >
<ItemsControl ItemsSource="{Binding AppliedFrames}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0,0,0,1" BorderBrush="#6C6C6C" Opacity="{Binding IsActive, Converter={StaticResource BoolToOpacity}}">
@ -176,14 +177,14 @@
<MultiBinding Converter="{x:Static BoolConverters.And}">
<MultiBinding Converter="{x:Static BoolConverters.Or}" >
<Binding Path="IsActive" />
<Binding Path="#Main.((vm:ControlDetailsViewModel)DataContext).ShowInactiveStyles" />
<Binding Path="#Main.((vm:ControlDetailsViewModel)DataContext).ShowInactiveFrames" />
</MultiBinding>
<Binding Path="IsVisible" />
</MultiBinding>
</Border.IsVisible>
<Expander IsExpanded="True" Margin="0" Padding="8,0" ContentTransition="{x:Null}" >
<Expander.Header>
<TextBlock Grid.Row="0" Text="{Binding Name}" />
<TextBlock Grid.Row="0" Text="{Binding Description}" />
</Expander.Header>
<ItemsControl Margin="20,0,0,0" Grid.Row="1" ItemsSource="{Binding Setters}">
@ -259,7 +260,13 @@
<StackPanel Orientation="Horizontal" Spacing="2" HorizontalAlignment="Left">
<TextBlock Classes="property-name" PointerPressed="PropertyNamePressed" Text="{Binding Name}" />
<TextBlock Text=":" />
<ContentControl Content="{Binding Value}"/>
<ContentControl Content="{Binding Value}">
<ContentControl.ContentTemplate>
<DataTemplate x:DataType="system:Object">
<TextBlock Text="{Binding}" />
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
<TextBlock>(</TextBlock>
<Ellipse Height="8" Width="8" VerticalAlignment="Center" Fill="{Binding Tint}" ToolTip.Tip="{Binding ValueTypeTooltip}"/>
<TextBlock FontStyle="Italic" Text="{Binding Key}" />
@ -280,7 +287,13 @@
<StackPanel Orientation="Horizontal" Spacing="2">
<TextBlock Classes="property-name" PointerPressed="PropertyNamePressed" Text="{Binding Name}" />
<TextBlock Text=":" />
<ContentControl Content="{Binding Value}"/>
<ContentControl Content="{Binding Value}">
<ContentControl.ContentTemplate>
<DataTemplate x:DataType="system:Object">
<TextBlock Text="{Binding}" />
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</StackPanel>
<Rectangle Classes="property-inactive" IsVisible="{Binding !IsActive}" />
</Panel>

Loading…
Cancel
Save