19 changed files with 216 additions and 408 deletions
@ -0,0 +1,4 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<packages> |
||||
|
<package id="Serilog" version="1.5.9" targetFramework="net46" /> |
||||
|
</packages> |
||||
@ -1,120 +0,0 @@ |
|||||
// Copyright (c) The Perspex Project. All rights reserved.
|
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Reactive.Linq; |
|
||||
using Perspex.Controls; |
|
||||
using Perspex.Diagnostics.ViewModels; |
|
||||
using Perspex.Input; |
|
||||
using Perspex.Themes.Default; |
|
||||
using ReactiveUI; |
|
||||
|
|
||||
namespace Perspex.Diagnostics |
|
||||
{ |
|
||||
public class DevTools : Decorator |
|
||||
{ |
|
||||
public static readonly PerspexProperty<Control> RootProperty = |
|
||||
PerspexProperty.Register<DevTools, Control>("Root"); |
|
||||
|
|
||||
private readonly DevToolsViewModel _viewModel; |
|
||||
|
|
||||
public DevTools() |
|
||||
{ |
|
||||
_viewModel = new DevToolsViewModel(); |
|
||||
this.GetObservable(RootProperty).Subscribe(x => _viewModel.Root = x); |
|
||||
|
|
||||
InitializeComponent(); |
|
||||
} |
|
||||
|
|
||||
public Control Root |
|
||||
{ |
|
||||
get { return GetValue(RootProperty); } |
|
||||
set { SetValue(RootProperty, value); } |
|
||||
} |
|
||||
|
|
||||
public static IDisposable Attach(Window window) |
|
||||
{ |
|
||||
return window.AddHandler( |
|
||||
KeyDownEvent, |
|
||||
WindowPreviewKeyDown, |
|
||||
Interactivity.RoutingStrategies.Tunnel); |
|
||||
} |
|
||||
|
|
||||
private static void WindowPreviewKeyDown(object sender, KeyEventArgs e) |
|
||||
{ |
|
||||
if (e.Key == Key.F12) |
|
||||
{ |
|
||||
Window window = new Window |
|
||||
{ |
|
||||
Width = 1024, |
|
||||
Height = 512, |
|
||||
Content = new DevTools |
|
||||
{ |
|
||||
Root = (Window)sender, |
|
||||
}, |
|
||||
}; |
|
||||
|
|
||||
window.Show(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void InitializeComponent() |
|
||||
{ |
|
||||
DataTemplates.Add(new ViewLocator<ReactiveObject>()); |
|
||||
Styles.Add(new DefaultTheme()); |
|
||||
|
|
||||
Child = new Grid |
|
||||
{ |
|
||||
RowDefinitions = new RowDefinitions("*,Auto"), |
|
||||
Children = new Controls.Controls |
|
||||
{ |
|
||||
new TabControl |
|
||||
{ |
|
||||
Items = new[] |
|
||||
{ |
|
||||
new TabItem |
|
||||
{ |
|
||||
Header = "Logical Tree", |
|
||||
[!ContentControl.ContentProperty] = _viewModel.WhenAnyValue(x => x.LogicalTree), |
|
||||
}, |
|
||||
new TabItem |
|
||||
{ |
|
||||
Header = "Visual Tree", |
|
||||
[!ContentControl.ContentProperty] = _viewModel.WhenAnyValue(x => x.VisualTree), |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
new StackPanel |
|
||||
{ |
|
||||
Orientation = Orientation.Horizontal, |
|
||||
Gap = 4, |
|
||||
[Grid.RowProperty] = 1, |
|
||||
Children = new Controls.Controls |
|
||||
{ |
|
||||
new TextBlock |
|
||||
{ |
|
||||
Text = "Focused: " |
|
||||
}, |
|
||||
new TextBlock |
|
||||
{ |
|
||||
[!TextBlock.TextProperty] = _viewModel |
|
||||
.WhenAnyValue(x => x.FocusedControl) |
|
||||
.Select(x => x?.GetType().Name ?? "(null)") |
|
||||
}, |
|
||||
new TextBlock |
|
||||
{ |
|
||||
Text = "Pointer Over: " |
|
||||
}, |
|
||||
new TextBlock |
|
||||
{ |
|
||||
[!TextBlock.TextProperty] = _viewModel |
|
||||
.WhenAnyValue(x => x.PointerOverElement) |
|
||||
.Select(x => x?.GetType().Name ?? "(null)") |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,17 @@ |
|||||
|
<UserControl xmlns="https://github.com/perspex"> |
||||
|
<Grid RowDefinitions="Auto,*,Auto"> |
||||
|
<TabStrip SelectedIndex="{Binding SelectedTab, Mode=TwoWay}"> |
||||
|
<TabStripItem Content="Logical Tree"/> |
||||
|
<TabStripItem Content="Visual Tree"/> |
||||
|
</TabStrip> |
||||
|
|
||||
|
<ContentControl Content="{Binding Content}" Grid.Row="1"/> |
||||
|
|
||||
|
<StackPanel Gap="4" Orientation="Horizontal" Grid.Row="2"> |
||||
|
<TextBlock>Focused:</TextBlock> |
||||
|
<TextBlock Text="{Binding FocusedControl}"/> |
||||
|
<TextBlock>Pointer Over:</TextBlock> |
||||
|
<TextBlock Text="{Binding PointerOverElement}"/> |
||||
|
</StackPanel> |
||||
|
</Grid> |
||||
|
</UserControl> |
||||
@ -0,0 +1,52 @@ |
|||||
|
using System; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Controls.Templates; |
||||
|
using Perspex.Diagnostics.ViewModels; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Interactivity; |
||||
|
using Perspex.Markup.Xaml; |
||||
|
using ReactiveUI; |
||||
|
|
||||
|
namespace Perspex.Diagnostics |
||||
|
{ |
||||
|
public class DevTools : UserControl |
||||
|
{ |
||||
|
public DevTools(IControl root) |
||||
|
{ |
||||
|
this.InitializeComponent(); |
||||
|
this.DataContext = new DevToolsViewModel(root); |
||||
|
} |
||||
|
|
||||
|
public static IDisposable Attach(Window window) |
||||
|
{ |
||||
|
return window.AddHandler( |
||||
|
KeyDownEvent, |
||||
|
WindowPreviewKeyDown, |
||||
|
RoutingStrategies.Tunnel); |
||||
|
} |
||||
|
|
||||
|
private static void WindowPreviewKeyDown(object sender, KeyEventArgs e) |
||||
|
{ |
||||
|
if (e.Key == Key.F12) |
||||
|
{ |
||||
|
Window window = new Window |
||||
|
{ |
||||
|
Width = 1024, |
||||
|
Height = 512, |
||||
|
Content = new DevTools((IControl)sender), |
||||
|
DataTemplates = new DataTemplates |
||||
|
{ |
||||
|
new ViewLocator<ReactiveObject>(), |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
window.Show(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void InitializeComponent() |
||||
|
{ |
||||
|
PerspexXamlLoader.Load(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,34 +0,0 @@ |
|||||
// Copyright (c) The Perspex Project. All rights reserved.
|
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
||||
|
|
||||
using System.Reactive.Linq; |
|
||||
using Perspex.Controls; |
|
||||
using ReactiveUI; |
|
||||
|
|
||||
namespace Perspex.Diagnostics.ViewModels |
|
||||
{ |
|
||||
internal class LogicalTreeViewModel : ReactiveObject |
|
||||
{ |
|
||||
private LogicalTreeNode _selected; |
|
||||
|
|
||||
private readonly ObservableAsPropertyHelper<ControlDetailsViewModel> _details; |
|
||||
|
|
||||
public LogicalTreeViewModel(Control root) |
|
||||
{ |
|
||||
Nodes = LogicalTreeNode.Create(root); |
|
||||
_details = this.WhenAnyValue(x => x.SelectedNode) |
|
||||
.Select(x => x != null ? new ControlDetailsViewModel(x.Control) : null) |
|
||||
.ToProperty(this, x => x.Details); |
|
||||
} |
|
||||
|
|
||||
public LogicalTreeNode[] Nodes { get; } |
|
||||
|
|
||||
public LogicalTreeNode SelectedNode |
|
||||
{ |
|
||||
get { return _selected; } |
|
||||
set { this.RaiseAndSetIfChanged(ref _selected, value); } |
|
||||
} |
|
||||
|
|
||||
public ControlDetailsViewModel Details => _details.Value; |
|
||||
} |
|
||||
} |
|
||||
@ -1,99 +0,0 @@ |
|||||
// Copyright (c) The Perspex Project. All rights reserved.
|
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Reactive.Linq; |
|
||||
using Perspex.Controls; |
|
||||
using Perspex.Controls.Templates; |
|
||||
using Perspex.Diagnostics.ViewModels; |
|
||||
using ReactiveUI; |
|
||||
|
|
||||
namespace Perspex.Diagnostics.Views |
|
||||
{ |
|
||||
using Controls = Controls.Controls; |
|
||||
|
|
||||
internal class LogicalTreeView : TreePage |
|
||||
{ |
|
||||
private static readonly PerspexProperty<LogicalTreeViewModel> ViewModelProperty = |
|
||||
PerspexProperty.Register<LogicalTreeView, LogicalTreeViewModel>("ViewModel"); |
|
||||
|
|
||||
public LogicalTreeView() |
|
||||
{ |
|
||||
InitializeComponent(); |
|
||||
this.GetObservable(DataContextProperty) |
|
||||
.Subscribe(x => ViewModel = (LogicalTreeViewModel)x); |
|
||||
} |
|
||||
|
|
||||
public LogicalTreeViewModel ViewModel |
|
||||
{ |
|
||||
get { return GetValue(ViewModelProperty); } |
|
||||
private set { SetValue(ViewModelProperty, value); } |
|
||||
} |
|
||||
|
|
||||
private void InitializeComponent() |
|
||||
{ |
|
||||
TreeView tree; |
|
||||
|
|
||||
Content = new Grid |
|
||||
{ |
|
||||
ColumnDefinitions = new ColumnDefinitions |
|
||||
{ |
|
||||
new ColumnDefinition(1, GridUnitType.Star), |
|
||||
new ColumnDefinition(4, GridUnitType.Pixel), |
|
||||
new ColumnDefinition(3, GridUnitType.Star), |
|
||||
}, |
|
||||
Children = new Controls |
|
||||
{ |
|
||||
(tree = new TreeView |
|
||||
{ |
|
||||
DataTemplates = new DataTemplates |
|
||||
{ |
|
||||
new FuncTreeDataTemplate<LogicalTreeNode>(GetHeader, x => x.Children), |
|
||||
}, |
|
||||
[!ItemsControl.ItemsProperty] = this.WhenAnyValue(x => x.ViewModel.Nodes), |
|
||||
}), |
|
||||
new GridSplitter |
|
||||
{ |
|
||||
Width = 4, |
|
||||
Orientation = Orientation.Vertical, |
|
||||
[Grid.ColumnProperty] = 1, |
|
||||
}, |
|
||||
new ContentControl |
|
||||
{ |
|
||||
[!ContentProperty] = this.WhenAnyValue(x => x.ViewModel.Details), |
|
||||
[Grid.ColumnProperty] = 2, |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
tree.GetObservable(TreeView.SelectedItemProperty) |
|
||||
.OfType<LogicalTreeNode>() |
|
||||
.Subscribe(x => ViewModel.SelectedNode = x); |
|
||||
} |
|
||||
|
|
||||
private Control GetHeader(LogicalTreeNode node) |
|
||||
{ |
|
||||
var result = new StackPanel |
|
||||
{ |
|
||||
Orientation = Orientation.Horizontal, |
|
||||
Gap = 8, |
|
||||
Children = new Controls |
|
||||
{ |
|
||||
new TextBlock |
|
||||
{ |
|
||||
Text = node.Type, |
|
||||
}, |
|
||||
new TextBlock |
|
||||
{ |
|
||||
[!TextBlock.TextProperty] = node.WhenAnyValue(x => x.Classes), |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
result.PointerEnter += AddAdorner; |
|
||||
result.PointerLeave += RemoveAdorner; |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,24 @@ |
|||||
|
<UserControl xmlns="https://github.com/perspex" |
||||
|
xmlns:vm="clr-namespace:Perspex.Diagnostics.ViewModels;assembly=Perspex.Diagnostics"> |
||||
|
<Grid ColumnDefinitions="*,4,3*"> |
||||
|
<TreeView Name="tree" Items="{Binding Nodes}" SelectedItem="{Binding SelectedNode, Mode=TwoWay}"> |
||||
|
<TreeView.DataTemplates> |
||||
|
<TreeDataTemplate DataType="vm:TreeNode" |
||||
|
ItemsSource="{Binding Children}"> |
||||
|
<StackPanel Orientation="Horizontal" Gap="8"> |
||||
|
<TextBlock Text="{Binding Type}"/> |
||||
|
<TextBlock Text="{Binding Classes}"/> |
||||
|
</StackPanel> |
||||
|
</TreeDataTemplate> |
||||
|
</TreeView.DataTemplates> |
||||
|
<!--<TreeView.Styles> |
||||
|
<Style Selector="TreeViewItem"> |
||||
|
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/> |
||||
|
</Style> |
||||
|
</TreeView.Styles>--> |
||||
|
</TreeView> |
||||
|
|
||||
|
<GridSplitter Width="4" Orientation="Vertical" Grid.Column="1"/> |
||||
|
<ContentControl Content="{Binding Details}" Grid.Column="2"/> |
||||
|
</Grid> |
||||
|
</UserControl> |
||||
@ -1,101 +0,0 @@ |
|||||
// Copyright (c) The Perspex Project. All rights reserved.
|
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Reactive.Linq; |
|
||||
using Perspex.Controls; |
|
||||
using Perspex.Controls.Templates; |
|
||||
using Perspex.Diagnostics.ViewModels; |
|
||||
using Perspex.Media; |
|
||||
using ReactiveUI; |
|
||||
|
|
||||
namespace Perspex.Diagnostics.Views |
|
||||
{ |
|
||||
using Controls = Controls.Controls; |
|
||||
|
|
||||
internal class VisualTreeView : TreePage |
|
||||
{ |
|
||||
private static readonly PerspexProperty<VisualTreeViewModel> ViewModelProperty = |
|
||||
PerspexProperty.Register<VisualTreeView, VisualTreeViewModel>("ViewModel"); |
|
||||
|
|
||||
public VisualTreeView() |
|
||||
{ |
|
||||
InitializeComponent(); |
|
||||
this.GetObservable(DataContextProperty) |
|
||||
.Subscribe(x => ViewModel = (VisualTreeViewModel)x); |
|
||||
} |
|
||||
|
|
||||
public VisualTreeViewModel ViewModel |
|
||||
{ |
|
||||
get { return GetValue(ViewModelProperty); } |
|
||||
private set { SetValue(ViewModelProperty, value); } |
|
||||
} |
|
||||
|
|
||||
private void InitializeComponent() |
|
||||
{ |
|
||||
TreeView tree; |
|
||||
|
|
||||
Content = new Grid |
|
||||
{ |
|
||||
ColumnDefinitions = new ColumnDefinitions |
|
||||
{ |
|
||||
new ColumnDefinition(1, GridUnitType.Star), |
|
||||
new ColumnDefinition(4, GridUnitType.Pixel), |
|
||||
new ColumnDefinition(3, GridUnitType.Star), |
|
||||
}, |
|
||||
Children = new Controls |
|
||||
{ |
|
||||
(tree = new TreeView |
|
||||
{ |
|
||||
DataTemplates = new DataTemplates |
|
||||
{ |
|
||||
new FuncTreeDataTemplate<VisualTreeNode>(GetHeader, x => x.Children), |
|
||||
}, |
|
||||
[!ItemsControl.ItemsProperty] = this.WhenAnyValue(x => x.ViewModel.Nodes), |
|
||||
}), |
|
||||
new GridSplitter |
|
||||
{ |
|
||||
Width = 4, |
|
||||
Orientation = Orientation.Vertical, |
|
||||
[Grid.ColumnProperty] = 1, |
|
||||
}, |
|
||||
new ContentControl |
|
||||
{ |
|
||||
[!ContentProperty] = this.WhenAnyValue(x => x.ViewModel.Details), |
|
||||
[Grid.ColumnProperty] = 2, |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
tree.GetObservable(TreeView.SelectedItemProperty) |
|
||||
.OfType<VisualTreeNode>() |
|
||||
.Subscribe(x => ViewModel.SelectedNode = x); |
|
||||
} |
|
||||
|
|
||||
private Control GetHeader(VisualTreeNode node) |
|
||||
{ |
|
||||
var result = new StackPanel |
|
||||
{ |
|
||||
Orientation = Orientation.Horizontal, |
|
||||
Gap = 8, |
|
||||
Children = new Controls |
|
||||
{ |
|
||||
new TextBlock |
|
||||
{ |
|
||||
FontStyle = node.IsInTemplate ? FontStyle.Italic : FontStyle.Normal, |
|
||||
Text = node.Type, |
|
||||
}, |
|
||||
new TextBlock |
|
||||
{ |
|
||||
[!TextBlock.TextProperty] = node.WhenAnyValue(x => x.Classes), |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
result.PointerEnter += AddAdorner; |
|
||||
result.PointerLeave += RemoveAdorner; |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue