From 86aad32bee708db2b93bcfdac87f1af620c089ca Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Thu, 20 Aug 2020 18:23:19 +0200 Subject: [PATCH 1/3] Adding support for regex filter of devtools properties --- .../ViewModels/ControlDetailsViewModel.cs | 31 ++++++------- .../ViewModels/TreePageViewModel.cs | 34 +++++++++++--- .../Diagnostics/Views/ControlDetailsView.xaml | 45 +++++++++++-------- 3 files changed, 67 insertions(+), 43 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index 4e7129da41..c8f618580b 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using System.Text.RegularExpressions; using Avalonia.Collections; using Avalonia.VisualTree; @@ -12,12 +13,13 @@ namespace Avalonia.Diagnostics.ViewModels private readonly IVisual _control; private readonly IDictionary> _propertyIndex; private AvaloniaPropertyViewModel _selectedProperty; - private string _propertyFilter; - public ControlDetailsViewModel(IVisual control, string propertyFilter) + public ControlDetailsViewModel(TreePageViewModel treePage, IVisual control) { _control = control; + TreePage = treePage; + var properties = GetAvaloniaProperties(control) .Concat(GetClrProperties(control)) .OrderBy(x => x, PropertyComparer.Instance) @@ -25,7 +27,6 @@ namespace Avalonia.Diagnostics.ViewModels .ToList(); _propertyIndex = properties.GroupBy(x => x.Key).ToDictionary(x => x.Key, x => x.ToList()); - _propertyFilter = propertyFilter; var view = new DataGridCollectionView(properties); view.GroupDescriptions.Add(new DataGridPathGroupDescription(nameof(AvaloniaPropertyViewModel.Group))); @@ -43,19 +44,9 @@ namespace Avalonia.Diagnostics.ViewModels } } - public DataGridCollectionView PropertiesView { get; } + public TreePageViewModel TreePage { get; } - public string PropertyFilter - { - get => _propertyFilter; - set - { - if (RaiseAndSetIfChanged(ref _propertyFilter, value)) - { - PropertiesView.Refresh(); - } - } - } + public DataGridCollectionView PropertiesView { get; } public AvaloniaPropertyViewModel SelectedProperty { @@ -137,9 +128,15 @@ namespace Avalonia.Diagnostics.ViewModels private bool FilterProperty(object arg) { - if (!string.IsNullOrWhiteSpace(PropertyFilter) && arg is PropertyViewModel property) + if (!string.IsNullOrWhiteSpace(TreePage.PropertyFilter) && arg is PropertyViewModel property) { - return property.Name.IndexOf(PropertyFilter, StringComparison.OrdinalIgnoreCase) != -1; + if (TreePage.UseRegexFilter) + { + var regex = new Regex(TreePage.PropertyFilter); + return regex.IsMatch(property.Name); + } + + return property.Name.IndexOf(TreePage.PropertyFilter, StringComparison.OrdinalIgnoreCase) != -1; } return true; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs index ec48cff399..77594570ed 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs @@ -8,7 +8,8 @@ namespace Avalonia.Diagnostics.ViewModels { private TreeNode _selectedNode; private ControlDetailsViewModel _details; - private string _propertyFilter; + private string _propertyFilter = string.Empty; + private bool _useRegexFilter; public TreePageViewModel(TreeNode[] nodes) { @@ -34,15 +35,10 @@ namespace Avalonia.Diagnostics.ViewModels get => _selectedNode; private set { - if (Details != null) - { - _propertyFilter = Details.PropertyFilter; - } - if (RaiseAndSetIfChanged(ref _selectedNode, value)) { Details = value != null ? - new ControlDetailsViewModel(value.Visual, _propertyFilter) : + new ControlDetailsViewModel(this, value.Visual) : null; } } @@ -62,6 +58,30 @@ namespace Avalonia.Diagnostics.ViewModels } } + public string PropertyFilter + { + get => _propertyFilter; + set + { + if (RaiseAndSetIfChanged(ref _propertyFilter, value)) + { + Details.PropertiesView.Refresh(); + } + } + } + + public bool UseRegexFilter + { + get => _useRegexFilter; + set + { + if (RaiseAndSetIfChanged(ref _useRegexFilter, value)) + { + Details.PropertiesView.Refresh(); + } + } + } + public void Dispose() { foreach (var node in Nodes) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml index 29ce26fcdf..b2d3a8bddb 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml @@ -2,24 +2,31 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:conv="clr-namespace:Avalonia.Diagnostics.Converters" x:Class="Avalonia.Diagnostics.Views.ControlDetailsView"> - - - - - - - - - - - - + + + + + + + + + + + + + + + From 2ab0f80459101628a00f310d86d10efc0f818c0c Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Thu, 20 Aug 2020 18:41:48 +0200 Subject: [PATCH 2/3] Reduce overhead of compiling regex Use INotifyDataErrorInfo to signal error --- .../ViewModels/ControlDetailsViewModel.cs | 3 +- .../ViewModels/TreePageViewModel.cs | 60 ++++++++++++++++--- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index c8f618580b..b4243c6088 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -132,8 +132,7 @@ namespace Avalonia.Diagnostics.ViewModels { if (TreePage.UseRegexFilter) { - var regex = new Regex(TreePage.PropertyFilter); - return regex.IsMatch(property.Name); + return TreePage.FilterRegex?.IsMatch(property.Name) ?? true; } return property.Name.IndexOf(TreePage.PropertyFilter, StringComparison.OrdinalIgnoreCase) != -1; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs index 77594570ed..da2fd25c0c 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs @@ -1,11 +1,16 @@ using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text.RegularExpressions; using Avalonia.Controls; using Avalonia.VisualTree; namespace Avalonia.Diagnostics.ViewModels { - internal class TreePageViewModel : ViewModelBase, IDisposable + internal class TreePageViewModel : ViewModelBase, IDisposable, INotifyDataErrorInfo { + private readonly Dictionary _errors = new Dictionary(); private TreeNode _selectedNode; private ControlDetailsViewModel _details; private string _propertyFilter = string.Empty; @@ -14,17 +19,13 @@ namespace Avalonia.Diagnostics.ViewModels public TreePageViewModel(TreeNode[] nodes) { Nodes = nodes; - Selection = new SelectionModel - { - SingleSelect = true, - Source = Nodes - }; + Selection = new SelectionModel { SingleSelect = true, Source = Nodes }; Selection.SelectionChanged += (s, e) => { SelectedNode = (TreeNode)Selection.SelectedItem; }; - } + } public TreeNode[] Nodes { get; protected set; } @@ -58,6 +59,37 @@ namespace Avalonia.Diagnostics.ViewModels } } + public Regex FilterRegex { get; set; } + + private void UpdateFilterRegex() + { + void ClearError() + { + if (_errors.Remove(nameof(PropertyFilter))) + { + ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(PropertyFilter))); + } + } + + if (UseRegexFilter) + { + try + { + FilterRegex = new Regex(PropertyFilter); + ClearError(); + } + catch (Exception exception) + { + _errors[nameof(PropertyFilter)] = exception.Message; + ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(PropertyFilter))); + } + } + else + { + ClearError(); + } + } + public string PropertyFilter { get => _propertyFilter; @@ -65,6 +97,7 @@ namespace Avalonia.Diagnostics.ViewModels { if (RaiseAndSetIfChanged(ref _propertyFilter, value)) { + UpdateFilterRegex(); Details.PropertiesView.Refresh(); } } @@ -77,6 +110,7 @@ namespace Avalonia.Diagnostics.ViewModels { if (RaiseAndSetIfChanged(ref _useRegexFilter, value)) { + UpdateFilterRegex(); Details.PropertiesView.Refresh(); } } @@ -158,5 +192,17 @@ namespace Avalonia.Diagnostics.ViewModels return null; } + + public IEnumerable GetErrors(string propertyName) + { + if (_errors.TryGetValue(propertyName, out var error)) + { + yield return error; + } + } + + public bool HasErrors => _errors.Count > 0; + + public event EventHandler ErrorsChanged; } } From ba8e6f029ec3befb5514ee860627844e91cab984 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Fri, 28 Aug 2020 11:43:11 +0200 Subject: [PATCH 3/3] Compile regex --- .../Diagnostics/ViewModels/ControlDetailsViewModel.cs | 1 - .../Diagnostics/ViewModels/TreePageViewModel.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index b4243c6088..764d699658 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; -using System.Text.RegularExpressions; using Avalonia.Collections; using Avalonia.VisualTree; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs index 53abba8906..bd65a3b06b 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs @@ -70,7 +70,7 @@ namespace Avalonia.Diagnostics.ViewModels { try { - FilterRegex = new Regex(PropertyFilter); + FilterRegex = new Regex(PropertyFilter, RegexOptions.Compiled); ClearError(); } catch (Exception exception)