27 changed files with 549 additions and 506 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,18 @@ |
|||
<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}"/> |
|||
<Separator/> |
|||
<TextBlock>Pointer Over:</TextBlock> |
|||
<TextBlock Text="{Binding PointerOverElement}"/> |
|||
</StackPanel> |
|||
</Grid> |
|||
</UserControl> |
|||
@ -0,0 +1,92 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
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 |
|||
{ |
|||
private static Dictionary<Window, Window> s_open = new Dictionary<Window, Window>(); |
|||
|
|||
public DevTools(IControl root) |
|||
{ |
|||
InitializeComponent(); |
|||
Root = root; |
|||
DataContext = new DevToolsViewModel(root); |
|||
Root.PointerMoved += RootPointerMoved; |
|||
} |
|||
|
|||
public IControl Root { get; } |
|||
|
|||
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) |
|||
{ |
|||
var window = (Window)sender; |
|||
var devToolsWindow = default(Window); |
|||
|
|||
if (s_open.TryGetValue(window, out devToolsWindow)) |
|||
{ |
|||
devToolsWindow.Activate(); |
|||
} |
|||
else |
|||
{ |
|||
devToolsWindow = new Window |
|||
{ |
|||
Width = 1024, |
|||
Height = 512, |
|||
Content = new DevTools(window), |
|||
DataTemplates = new DataTemplates |
|||
{ |
|||
new ViewLocator<ReactiveObject>(), |
|||
} |
|||
}; |
|||
|
|||
devToolsWindow.Closed += DevToolsClosed; |
|||
s_open.Add((Window)sender, devToolsWindow); |
|||
devToolsWindow.Show(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static void DevToolsClosed(object sender, EventArgs e) |
|||
{ |
|||
var devToolsWindow = (Window)sender; |
|||
var devTools = (DevTools)devToolsWindow.Content; |
|||
var window = (Window)devTools.Root; |
|||
|
|||
s_open.Remove(window); |
|||
devToolsWindow.Closed -= DevToolsClosed; |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
PerspexXamlLoader.Load(this); |
|||
} |
|||
|
|||
private void RootPointerMoved(object sender, PointerEventArgs e) |
|||
{ |
|||
var modifiers = InputModifiers.Control | InputModifiers.Shift; |
|||
|
|||
if ((e.InputModifiers & modifiers) == modifiers) |
|||
{ |
|||
var vm = (DevToolsViewModel)DataContext; |
|||
vm.SelectControl((IControl)e.Source); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
// 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 Perspex.VisualTree; |
|||
using ReactiveUI; |
|||
|
|||
namespace Perspex.Diagnostics.ViewModels |
|||
{ |
|||
internal class TreePageViewModel : ReactiveObject |
|||
{ |
|||
private TreeNode _selected; |
|||
|
|||
private readonly ObservableAsPropertyHelper<ControlDetailsViewModel> _details; |
|||
|
|||
public TreePageViewModel(TreeNode[] nodes) |
|||
{ |
|||
Nodes = nodes; |
|||
_details = this.WhenAnyValue(x => x.SelectedNode) |
|||
.Select(x => x != null ? new ControlDetailsViewModel(x.Control) : null) |
|||
.ToProperty(this, x => x.Details); |
|||
} |
|||
|
|||
public TreeNode[] Nodes { get; protected set; } |
|||
|
|||
public TreeNode SelectedNode |
|||
{ |
|||
get { return _selected; } |
|||
set { this.RaiseAndSetIfChanged(ref _selected, value); } |
|||
} |
|||
|
|||
public ControlDetailsViewModel Details => _details.Value; |
|||
|
|||
public TreeNode FindNode(IControl control) |
|||
{ |
|||
foreach (var node in Nodes) |
|||
{ |
|||
var result = FindNode(node, control); |
|||
|
|||
if (result != null) |
|||
{ |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
public void SelectControl(IControl control) |
|||
{ |
|||
var node = default(TreeNode); |
|||
|
|||
while (node == null && control != null) |
|||
{ |
|||
node = FindNode(control); |
|||
|
|||
if (node == null) |
|||
{ |
|||
control = control.GetVisualParent<IControl>(); |
|||
} |
|||
} |
|||
|
|||
if (node != null) |
|||
{ |
|||
SelectedNode = node; |
|||
ExpandNode(node.Parent); |
|||
} |
|||
} |
|||
|
|||
private void ExpandNode(TreeNode node) |
|||
{ |
|||
if (node != null) |
|||
{ |
|||
node.IsExpanded = true; |
|||
ExpandNode(node.Parent); |
|||
} |
|||
} |
|||
|
|||
private TreeNode FindNode(TreeNode node, IControl control) |
|||
{ |
|||
if (node.Control == control) |
|||
{ |
|||
return node; |
|||
} |
|||
else |
|||
{ |
|||
foreach (var child in node.Children) |
|||
{ |
|||
var result = FindNode(child, control); |
|||
|
|||
if (result != null) |
|||
{ |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -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 VisualTreeViewModel : ReactiveObject |
|||
{ |
|||
private VisualTreeNode _selected; |
|||
|
|||
private readonly ObservableAsPropertyHelper<ControlDetailsViewModel> _details; |
|||
|
|||
public VisualTreeViewModel(Control root) |
|||
{ |
|||
Nodes = VisualTreeNode.Create(root); |
|||
_details = this.WhenAnyValue(x => x.SelectedNode) |
|||
.Select(x => x != null ? new ControlDetailsViewModel(x.Control) : null) |
|||
.ToProperty(this, x => x.Details); |
|||
} |
|||
|
|||
public VisualTreeNode[] Nodes { get; } |
|||
|
|||
public VisualTreeNode 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; |
|||
} |
|||
} |
|||
} |
|||
@ -1,43 +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 Perspex.Controls; |
|||
using Perspex.Controls.Primitives; |
|||
using Perspex.Controls.Shapes; |
|||
using Perspex.Diagnostics.ViewModels; |
|||
using Perspex.Input; |
|||
using Perspex.Media; |
|||
|
|||
namespace Perspex.Diagnostics.Views |
|||
{ |
|||
internal class TreePage : UserControl |
|||
{ |
|||
private Control _adorner; |
|||
|
|||
protected void AddAdorner(object sender, PointerEventArgs e) |
|||
{ |
|||
var node = (TreeNode)((Control)sender).DataContext; |
|||
var layer = AdornerLayer.GetAdornerLayer(node.Control); |
|||
|
|||
if (layer != null) |
|||
{ |
|||
_adorner = new Rectangle |
|||
{ |
|||
Fill = new SolidColorBrush(0x80a0c5e8), |
|||
[AdornerLayer.AdornedElementProperty] = node.Control, |
|||
}; |
|||
|
|||
layer.Children.Add(_adorner); |
|||
} |
|||
} |
|||
|
|||
protected void RemoveAdorner(object sender, PointerEventArgs e) |
|||
{ |
|||
if (_adorner != null) |
|||
{ |
|||
((Panel)_adorner.Parent).Children.Remove(_adorner); |
|||
_adorner = null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
using Perspex.Controls; |
|||
using Perspex.Controls.Generators; |
|||
using Perspex.Controls.Primitives; |
|||
using Perspex.Controls.Shapes; |
|||
using Perspex.Diagnostics.ViewModels; |
|||
using Perspex.Input; |
|||
using Perspex.Markup.Xaml; |
|||
using Perspex.Media; |
|||
|
|||
namespace Perspex.Diagnostics.Views |
|||
{ |
|||
public class TreePageView : UserControl |
|||
{ |
|||
private Control _adorner; |
|||
private TreeView _tree; |
|||
|
|||
public TreePageView() |
|||
{ |
|||
this.InitializeComponent(); |
|||
_tree.ItemContainerGenerator.Index.Materialized += TreeViewItemMaterialized; |
|||
} |
|||
|
|||
protected void AddAdorner(object sender, PointerEventArgs e) |
|||
{ |
|||
var node = (TreeNode)((Control)sender).DataContext; |
|||
var layer = AdornerLayer.GetAdornerLayer(node.Control); |
|||
|
|||
if (layer != null) |
|||
{ |
|||
_adorner = new Rectangle |
|||
{ |
|||
Fill = new SolidColorBrush(0x80a0c5e8), |
|||
[AdornerLayer.AdornedElementProperty] = node.Control, |
|||
}; |
|||
|
|||
layer.Children.Add(_adorner); |
|||
} |
|||
} |
|||
|
|||
protected void RemoveAdorner(object sender, PointerEventArgs e) |
|||
{ |
|||
if (_adorner != null) |
|||
{ |
|||
((Panel)_adorner.Parent).Children.Remove(_adorner); |
|||
_adorner = null; |
|||
} |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
PerspexXamlLoader.Load(this); |
|||
_tree = this.FindControl<TreeView>("tree"); |
|||
} |
|||
|
|||
private void TreeViewItemMaterialized(object sender, ItemContainerEventArgs e) |
|||
{ |
|||
var item = (TreeViewItem)e.Containers[0].ContainerControl; |
|||
item.TemplateApplied += TreeViewItemTemplateApplied; |
|||
} |
|||
|
|||
private void TreeViewItemTemplateApplied(object sender, TemplateAppliedEventArgs e) |
|||
{ |
|||
var item = (TreeViewItem)sender; |
|||
var header = item.HeaderPresenter.Child; |
|||
header.PointerEnter += AddAdorner; |
|||
header.PointerLeave += RemoveAdorner; |
|||
item.TemplateApplied -= TreeViewItemTemplateApplied; |
|||
} |
|||
} |
|||
} |
|||
@ -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