Browse Source

Apply requested changes and some more clean up.

pull/1888/head
Siegfried Pammer 8 years ago
parent
commit
032fc349f9
  1. 32
      src/Avalonia.Diagnostics/Models/EventChainLink.cs
  2. 97
      src/Avalonia.Diagnostics/ViewModels/EventEntryTreeNode.cs
  3. 18
      src/Avalonia.Diagnostics/ViewModels/EventOwnerTreeNode.cs
  4. 118
      src/Avalonia.Diagnostics/ViewModels/EventTreeNode.cs
  5. 78
      src/Avalonia.Diagnostics/ViewModels/EventTreeNodeBase.cs
  6. 33
      src/Avalonia.Diagnostics/ViewModels/EventsViewModel.cs
  7. 31
      src/Avalonia.Diagnostics/ViewModels/FiredEvent.cs
  8. 4
      src/Avalonia.Diagnostics/Views/EventsView.xaml
  9. 8
      src/Avalonia.Diagnostics/Views/EventsView.xaml.cs

32
src/Avalonia.Diagnostics/Models/ChainLink.cs → src/Avalonia.Diagnostics/Models/EventChainLink.cs

@ -1,13 +1,24 @@
using System; // Copyright (c) The Avalonia Project. All rights reserved.
using System.Collections.Generic; // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Text;
using System;
using Avalonia.Interactivity; using Avalonia.Interactivity;
namespace Avalonia.Diagnostics.Models namespace Avalonia.Diagnostics.Models
{ {
internal class ChainLink internal class EventChainLink
{ {
public object Handler { get; private set; } public EventChainLink(object handler, bool handled, RoutingStrategies route)
{
Contract.Requires<ArgumentNullException>(handler != null);
this.Handler = handler;
this.Handled = handled;
this.Route = route;
}
public object Handler { get; }
public string HandlerName public string HandlerName
{ {
get get
@ -19,16 +30,9 @@ namespace Avalonia.Diagnostics.Models
return Handler.GetType().Name; return Handler.GetType().Name;
} }
} }
public bool Handled { get; private set; }
public RoutingStrategies Route { get; private set; }
public ChainLink(object handler, bool handled, RoutingStrategies route) public bool Handled { get; }
{
Contract.Requires<ArgumentNullException>(handler != null);
this.Handler = handler; public RoutingStrategies Route { get; }
this.Handled = handled;
this.Route = route;
}
} }
} }

97
src/Avalonia.Diagnostics/ViewModels/EventEntryTreeNode.cs

@ -1,97 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Diagnostics.Models;
using Avalonia.Interactivity;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.ViewModels
{
internal class EventEntryTreeNode : EventTreeNode
{
RoutedEvent _event;
EventsViewModel _parentViewModel;
bool _isRegistered;
FiredEvent _currentEvent;
public EventEntryTreeNode(ControlTreeNode parent, RoutedEvent @event, EventsViewModel vm)
: base(parent, @event.Name)
{
Contract.Requires<ArgumentNullException>(@event != null);
Contract.Requires<ArgumentNullException>(vm != null);
this._event = @event;
this._parentViewModel = vm;
}
public override bool? IsEnabled
{
get => base.IsEnabled;
set
{
if (base.IsEnabled != value)
{
base.IsEnabled = value;
UpdateTracker();
if (Parent != null && _updateParent)
{
try
{
Parent._updateChildren = false;
Parent.UpdateChecked();
}
finally
{
Parent._updateChildren = true;
}
}
}
}
}
private void UpdateTracker()
{
if (IsEnabled.GetValueOrDefault() && !_isRegistered)
{
_event.AddClassHandler(typeof(object), HandleEvent, (RoutingStrategies)7, handledEventsToo: true);
_isRegistered = true;
}
}
private void HandleEvent(object sender, RoutedEventArgs e)
{
if (!_isRegistered || IsEnabled == false)
return;
if (sender is IVisual v && DevTools.BelongsToDevTool(v))
return;
var s = sender;
var handled = e.Handled;
var route = e.Route;
Action handler = delegate
{
if (_currentEvent == null || !_currentEvent.IsPartOfSameEventChain(e))
{
_currentEvent = new FiredEvent(e, new ChainLink(s, handled, route));
_parentViewModel.RecordedEvents.Add(_currentEvent);
while (_parentViewModel.RecordedEvents.Count > 100)
_parentViewModel.RecordedEvents.RemoveAt(0);
}
else
{
_currentEvent.AddToChain(new ChainLink(s, handled, route));
}
};
if (!Dispatcher.UIThread.CheckAccess())
Dispatcher.UIThread.Post(handler);
else
handler();
}
}
}

18
src/Avalonia.Diagnostics/ViewModels/ControlTreeNode.cs → src/Avalonia.Diagnostics/ViewModels/EventOwnerTreeNode.cs

@ -11,16 +11,9 @@ using Avalonia.Interactivity;
namespace Avalonia.Diagnostics.ViewModels namespace Avalonia.Diagnostics.ViewModels
{ {
internal class ControlTreeNode : EventTreeNode internal class EventOwnerTreeNode : EventTreeNodeBase
{ {
public ControlTreeNode(Type type, IEnumerable<RoutedEvent> events, EventsViewModel vm) private static readonly RoutedEvent[] s_defaultEvents = new RoutedEvent[]
: base(null, type.Name)
{
this.Children = new AvaloniaList<EventTreeNode>(events.OrderBy(e => e.Name).Select(e => new EventEntryTreeNode(this, e, vm) { IsEnabled = IsDefault(e) }));
this.IsExpanded = true;
}
RoutedEvent[] defaultEvents = new RoutedEvent[]
{ {
Button.ClickEvent, Button.ClickEvent,
InputElement.KeyDownEvent, InputElement.KeyDownEvent,
@ -30,9 +23,12 @@ namespace Avalonia.Diagnostics.ViewModels
InputElement.PointerPressedEvent, InputElement.PointerPressedEvent,
}; };
private bool IsDefault(RoutedEvent e) public EventOwnerTreeNode(Type type, IEnumerable<RoutedEvent> events, EventsViewModel vm)
: base(null, type.Name)
{ {
return defaultEvents.Contains(e); this.Children = new AvaloniaList<EventTreeNodeBase>(events.OrderBy(e => e.Name)
.Select(e => new EventTreeNode(this, e, vm) { IsEnabled = s_defaultEvents.Contains(e) }));
this.IsExpanded = true;
} }
public override bool? IsEnabled public override bool? IsEnabled

118
src/Avalonia.Diagnostics/ViewModels/EventTreeNode.cs

@ -1,80 +1,98 @@
// Copyright (c) The Avalonia Project. All rights reserved. // Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Diagnostics; using System;
using Avalonia.Collections;
using Avalonia.Diagnostics.Models;
using Avalonia.Interactivity;
using Avalonia.Threading;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.ViewModels namespace Avalonia.Diagnostics.ViewModels
{ {
internal abstract class EventTreeNode : ViewModelBase internal class EventTreeNode : EventTreeNodeBase
{ {
internal bool _updateChildren = true; private RoutedEvent _event;
internal bool _updateParent = true; private EventsViewModel _parentViewModel;
private bool _isExpanded; private bool _isRegistered;
private bool? _isEnabled = false; private FiredEvent _currentEvent;
public EventTreeNode(EventTreeNode parent, string text)
{
this.Parent = parent;
this.Text = text;
}
public IAvaloniaReadOnlyList<EventTreeNode> Children public EventTreeNode(EventOwnerTreeNode parent, RoutedEvent @event, EventsViewModel vm)
: base(parent, @event.Name)
{ {
get; Contract.Requires<ArgumentNullException>(@event != null);
protected set; Contract.Requires<ArgumentNullException>(vm != null);
}
public bool IsExpanded this._event = @event;
{ this._parentViewModel = vm;
get { return _isExpanded; }
set { RaiseAndSetIfChanged(ref _isExpanded, value); }
} }
public virtual bool? IsEnabled public override bool? IsEnabled
{ {
get { return _isEnabled; } get => base.IsEnabled;
set { RaiseAndSetIfChanged(ref _isEnabled, value); } set
{
if (base.IsEnabled != value)
{
base.IsEnabled = value;
UpdateTracker();
if (Parent != null && _updateParent)
{
try
{
Parent._updateChildren = false;
Parent.UpdateChecked();
}
finally
{
Parent._updateChildren = true;
}
}
}
}
} }
public EventTreeNode Parent private void UpdateTracker()
{ {
get; if (IsEnabled.GetValueOrDefault() && !_isRegistered)
{
_event.AddClassHandler(typeof(object), HandleEvent, (RoutingStrategies)7, handledEventsToo: true);
_isRegistered = true;
}
} }
public string Text private void HandleEvent(object sender, RoutedEventArgs e)
{ {
get; if (!_isRegistered || IsEnabled == false)
private set; return;
} if (sender is IVisual v && DevTools.BelongsToDevTool(v))
return;
internal void UpdateChecked() var s = sender;
{ var handled = e.Handled;
IsEnabled = GetValue(); var route = e.Route;
bool? GetValue() Action handler = delegate
{ {
if (Children == null) if (_currentEvent == null || !_currentEvent.IsPartOfSameEventChain(e))
return false;
bool? value = false;
for (int i = 0; i < Children.Count; i++)
{ {
if (i == 0) _currentEvent = new FiredEvent(e, new EventChainLink(s, handled, route));
{
value = Children[i].IsEnabled;
continue;
}
if (value != Children[i].IsEnabled) _parentViewModel.RecordedEvents.Add(_currentEvent);
{
value = null; while (_parentViewModel.RecordedEvents.Count > 100)
break; _parentViewModel.RecordedEvents.RemoveAt(0);
}
} }
else
{
_currentEvent.AddToChain(new EventChainLink(s, handled, route));
}
};
return value; if (!Dispatcher.UIThread.CheckAccess())
} Dispatcher.UIThread.Post(handler);
else
handler();
} }
} }
} }

78
src/Avalonia.Diagnostics/ViewModels/EventTreeNodeBase.cs

@ -0,0 +1,78 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Collections;
namespace Avalonia.Diagnostics.ViewModels
{
internal abstract class EventTreeNodeBase : ViewModelBase
{
internal bool _updateChildren = true;
internal bool _updateParent = true;
private bool _isExpanded;
private bool? _isEnabled = false;
public EventTreeNodeBase(EventTreeNodeBase parent, string text)
{
this.Parent = parent;
this.Text = text;
}
public IAvaloniaReadOnlyList<EventTreeNodeBase> Children
{
get;
protected set;
}
public bool IsExpanded
{
get { return _isExpanded; }
set { RaiseAndSetIfChanged(ref _isExpanded, value); }
}
public virtual bool? IsEnabled
{
get { return _isEnabled; }
set { RaiseAndSetIfChanged(ref _isEnabled, value); }
}
public EventTreeNodeBase Parent
{
get;
}
public string Text
{
get;
private set;
}
internal void UpdateChecked()
{
IsEnabled = GetValue();
bool? GetValue()
{
if (Children == null)
return false;
bool? value = false;
for (int i = 0; i < Children.Count; i++)
{
if (i == 0)
{
value = Children[i].IsEnabled;
continue;
}
if (value != Children[i].IsEnabled)
{
value = null;
break;
}
}
return value;
}
}
}
}

33
src/Avalonia.Diagnostics/ViewModels/EventsViewModel.cs

@ -2,26 +2,22 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Text;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading;
namespace Avalonia.Diagnostics.ViewModels namespace Avalonia.Diagnostics.ViewModels
{ {
internal class EventsViewModel : ViewModelBase internal class EventsViewModel : ViewModelBase
{ {
private IControl _root; private readonly IControl _root;
private FiredEvent _selectedEvent; private FiredEvent _selectedEvent;
private ICommand ClearCommand { get; }
public EventsViewModel(IControl root) public EventsViewModel(IControl root)
{ {
@ -29,27 +25,11 @@ namespace Avalonia.Diagnostics.ViewModels
this.Nodes = RoutedEventRegistry.Instance.GetAllRegistered() this.Nodes = RoutedEventRegistry.Instance.GetAllRegistered()
.GroupBy(e => e.OwnerType) .GroupBy(e => e.OwnerType)
.OrderBy(e => e.Key.Name) .OrderBy(e => e.Key.Name)
.Select(g => new ControlTreeNode(g.Key, g, this)) .Select(g => new EventOwnerTreeNode(g.Key, g, this))
.ToArray(); .ToArray();
} }
private void ClearExecute() public EventTreeNodeBase[] Nodes { get; }
{
Action action = delegate
{
RecordedEvents.Clear();
};
if (!Dispatcher.UIThread.CheckAccess())
{
Dispatcher.UIThread.Post(action);
}
else
{
action();
}
}
public EventTreeNode[] Nodes { get; }
public ObservableCollection<FiredEvent> RecordedEvents { get; } = new ObservableCollection<FiredEvent>(); public ObservableCollection<FiredEvent> RecordedEvents { get; } = new ObservableCollection<FiredEvent>();
@ -58,6 +38,11 @@ namespace Avalonia.Diagnostics.ViewModels
get => _selectedEvent; get => _selectedEvent;
set => RaiseAndSetIfChanged(ref _selectedEvent, value); set => RaiseAndSetIfChanged(ref _selectedEvent, value);
} }
private void Clear()
{
RecordedEvents.Clear();
}
} }
internal class BoolToBrushConverter : IValueConverter internal class BoolToBrushConverter : IValueConverter

31
src/Avalonia.Diagnostics/ViewModels/FiredEvent.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Avalonia.Diagnostics.Models; using Avalonia.Diagnostics.Models;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -11,17 +12,15 @@ namespace Avalonia.Diagnostics.ViewModels
internal class FiredEvent : ViewModelBase internal class FiredEvent : ViewModelBase
{ {
private RoutedEventArgs _eventArgs; private RoutedEventArgs _eventArgs;
private EventChainLink _handledBy;
private ChainLink _handledBy; public FiredEvent(RoutedEventArgs eventArgs, EventChainLink originator)
private ChainLink _originator;
public FiredEvent(RoutedEventArgs eventArgs, ChainLink originator)
{ {
Contract.Requires<ArgumentNullException>(eventArgs != null); Contract.Requires<ArgumentNullException>(eventArgs != null);
Contract.Requires<ArgumentNullException>(originator != null); Contract.Requires<ArgumentNullException>(originator != null);
this._eventArgs = eventArgs; this._eventArgs = eventArgs;
this._originator = originator; this.Originator = originator;
AddToChain(originator); AddToChain(originator);
} }
@ -34,7 +33,7 @@ namespace Avalonia.Diagnostics.ViewModels
public bool IsHandled => HandledBy?.Handled == true; public bool IsHandled => HandledBy?.Handled == true;
public ObservableCollection<ChainLink> EventChain { get; } = new ObservableCollection<ChainLink>(); public ObservableCollection<EventChainLink> EventChain { get; } = new ObservableCollection<EventChainLink>();
public string DisplayText public string DisplayText
{ {
@ -49,21 +48,9 @@ namespace Avalonia.Diagnostics.ViewModels
} }
} }
public ChainLink Originator public EventChainLink Originator { get; }
{
get { return _originator; }
set
{
if (_originator != value)
{
_originator = value;
RaisePropertyChanged();
RaisePropertyChanged(nameof(DisplayText));
}
}
}
public ChainLink HandledBy public EventChainLink HandledBy
{ {
get { return _handledBy; } get { return _handledBy; }
set set
@ -80,10 +67,10 @@ namespace Avalonia.Diagnostics.ViewModels
public void AddToChain(object handler, bool handled, RoutingStrategies route) public void AddToChain(object handler, bool handled, RoutingStrategies route)
{ {
AddToChain(new ChainLink(handler, handled, route)); AddToChain(new EventChainLink(handler, handled, route));
} }
public void AddToChain(ChainLink link) public void AddToChain(EventChainLink link)
{ {
EventChain.Add(link); EventChain.Add(link);
if (HandledBy == null && link.Handled) if (HandledBy == null && link.Handled)

4
src/Avalonia.Diagnostics/Views/EventsView.xaml

@ -7,7 +7,7 @@
<Grid ColumnDefinitions="*,4,3*"> <Grid ColumnDefinitions="*,4,3*">
<TreeView Name="tree" Items="{Binding Nodes}" SelectedItem="{Binding SelectedNode, Mode=TwoWay}" Grid.RowSpan="2"> <TreeView Name="tree" Items="{Binding Nodes}" SelectedItem="{Binding SelectedNode, Mode=TwoWay}" Grid.RowSpan="2">
<TreeView.DataTemplates> <TreeView.DataTemplates>
<TreeDataTemplate DataType="vm:EventTreeNode" <TreeDataTemplate DataType="vm:EventTreeNodeBase"
ItemsSource="{Binding Children}"> ItemsSource="{Binding Children}">
<CheckBox Content="{Binding Text}" IsChecked="{Binding IsEnabled, Mode=TwoWay}" /> <CheckBox Content="{Binding Text}" IsChecked="{Binding IsEnabled, Mode=TwoWay}" />
</TreeDataTemplate> </TreeDataTemplate>
@ -46,7 +46,7 @@
</ListBox> </ListBox>
</DockPanel> </DockPanel>
<StackPanel Orientation="Horizontal" Grid.Row="3"> <StackPanel Orientation="Horizontal" Grid.Row="3">
<Button Content="Clear" Margin="3" Command="{Binding ClearExecute}" /> <Button Content="Clear" Margin="3" Command="{Binding Clear}" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</Grid> </Grid>

8
src/Avalonia.Diagnostics/Views/EventsView.xaml.cs

@ -1,4 +1,8 @@
using System.Linq; // Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Diagnostics.ViewModels; using Avalonia.Diagnostics.ViewModels;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
@ -7,7 +11,7 @@ namespace Avalonia.Diagnostics.Views
{ {
public class EventsView : UserControl public class EventsView : UserControl
{ {
ListBox _events; private ListBox _events;
public EventsView() public EventsView()
{ {

Loading…
Cancel
Save