Browse Source

Started refactoring DevTools.

pull/58/head
Steven Kirk 12 years ago
parent
commit
97f5770920
  1. 2
      Perspex.Controls/Templates/DataTemplateExtensions.cs
  2. 224
      Perspex.Diagnostics/DevTools.cs
  3. 10
      Perspex.Diagnostics/Perspex.Diagnostics.csproj
  4. 34
      Perspex.Diagnostics/ViewLocator.cs
  5. 6
      Perspex.Diagnostics/ViewModels/ControlDetailsViewModel.cs
  6. 48
      Perspex.Diagnostics/ViewModels/DevToolsViewModel.cs
  7. 40
      Perspex.Diagnostics/ViewModels/LogicalTreeViewModel.cs
  8. 40
      Perspex.Diagnostics/ViewModels/VisualTreeViewModel.cs
  9. 59
      Perspex.Diagnostics/Views/ControlDetailsView.cs
  10. 98
      Perspex.Diagnostics/Views/LogicalTreeView.cs
  11. 46
      Perspex.Diagnostics/Views/TreePage.cs
  12. 100
      Perspex.Diagnostics/Views/VisualTreeView.cs
  13. 2
      Perspex.Themes.Default/DropDownStyle.cs

2
Perspex.Controls/Templates/DataTemplateExtensions.cs

@ -21,7 +21,7 @@ namespace Perspex.Controls.Templates
{
result = template.Build(data);
if (result != null)
if (result != null && result.DataContext == null)
{
result.DataContext = data;
}

224
Perspex.Diagnostics/DevTools.cs

@ -6,107 +6,26 @@
namespace Perspex.Diagnostics
{
using System;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Perspex.Controls;
using Perspex.Controls.Primitives;
using Perspex.Controls.Shapes;
using Perspex.Diagnostics.ViewModels;
using Perspex.Input;
using Perspex.Interactivity;
using Perspex.Layout;
using Perspex.Media;
using ReactiveUI;
using System;
using System.Reactive.Linq;
public class DevTools : Decorator
{
public static readonly PerspexProperty<Control> RootProperty =
PerspexProperty.Register<DevTools, Control>("Root");
private Control adorner;
private DevToolsViewModel viewModel;
public DevTools()
{
TabStrip tabStrip;
TreeView treeView;
var treePane = new Grid
{
RowDefinitions = new RowDefinitions
{
new RowDefinition(GridLength.Auto),
new RowDefinition(new GridLength(1, GridUnitType.Star)),
},
Children = new Controls
{
(tabStrip = new TabStrip
{
Items = new[]
{
new TabItem
{
Header = "Logical Tree",
IsSelected = true,
[!TabItem.TagProperty] = this[!RootProperty].Select(x => LogicalTreeNode.Create(x)),
},
new TabItem
{
Header = "Visual Tree",
[!TabItem.TagProperty] = this[!RootProperty].Select(x => VisualTreeNode.Create(x)),
}
},
}),
(treeView = new TreeView
{
DataTemplates = new DataTemplates
{
new TreeDataTemplate<LogicalTreeNode>(this.GetHeader, x => x.Children),
new TreeDataTemplate<VisualTreeNode>(this.GetHeader, x => x.Children),
},
[!TreeView.ItemsProperty] = tabStrip.WhenAnyValue(x => x.SelectedTab.Tag),
[Grid.RowProperty] = 1,
})
}
};
this.viewModel = new DevToolsViewModel();
this.GetObservable(RootProperty).Subscribe(x => this.viewModel.Root = x);
var detailsView = new ScrollViewer
{
Content = new ContentControl
{
DataTemplates = new DataTemplates
{
new DataTemplate<ControlDetails>(CreateDetailsView),
},
[!ContentControl.ContentProperty] = treeView[!TreeView.SelectedItemProperty]
.Where(x => x != null)
.Cast<TreeNode>()
.Select(x => new ControlDetails(x.Control)),
},
[Grid.ColumnProperty] = 2,
};
var splitter = new GridSplitter
{
[Grid.ColumnProperty] = 1,
Width = 4,
};
this.Content = new Grid
{
ColumnDefinitions = new ColumnDefinitions
{
new ColumnDefinition(1, GridUnitType.Star),
new ColumnDefinition(4, GridUnitType.Pixel),
new ColumnDefinition(3, GridUnitType.Star),
},
Children = new Controls
{
treePane,
splitter,
detailsView,
}
};
this.InitializeComponent();
}
public Control Root
@ -115,9 +34,12 @@ namespace Perspex.Diagnostics
set { this.SetValue(RootProperty, value); }
}
public static IDisposable Attach(Window w)
public static IDisposable Attach(Window window)
{
return w.AddHandler(Window.KeyDownEvent, WindowPreviewKeyDown, Interactivity.RoutingStrategies.Tunnel);
return window.AddHandler(
Window.KeyDownEvent,
WindowPreviewKeyDown,
Interactivity.RoutingStrategies.Tunnel);
}
private static void WindowPreviewKeyDown(object sender, KeyEventArgs e)
@ -138,104 +60,52 @@ namespace Perspex.Diagnostics
}
}
private static Control CreateDetailsView(ControlDetails i)
private void InitializeComponent()
{
return new ItemsControl
{
DataTemplates = new DataTemplates
{
new DataTemplate<PropertyDetails>(x =>
new StackPanel
{
Gap = 16,
Orientation = Orientation.Horizontal,
Children = new Controls
{
new TextBlock { Text = x.Name },
new TextBlock { [!TextBlock.TextProperty] = x.WhenAnyValue(v => v.Value).Select(v => v?.ToString()) },
new TextBlock { Text = x.Priority },
},
}),
},
Items = i.Properties,
};
}
this.DataTemplates.Add(new ViewLocator<ReactiveObject>());
private Control GetHeader(LogicalTreeNode node)
{
var result = new StackPanel
this.Content = new TabControl
{
Orientation = Orientation.Horizontal,
Gap = 8,
Children = new Controls
Items = new[]
{
new TextBlock
new TabItem
{
Text = node.Type,
Header = "Logical Tree",
[!TabItem.ContentProperty] = this.viewModel.WhenAnyValue(x => x.LogicalTree),
},
new TextBlock
new TabItem
{
[!TextBlock.TextProperty] = node.WhenAnyValue(x => x.Classes),
Header = "Visual Tree",
[!TabItem.ContentProperty] = this.viewModel.WhenAnyValue(x => x.VisualTree),
}
}
};
result.PointerEnter += this.AddAdorner;
result.PointerLeave += this.RemoveAdorner;
return result;
}
private Control GetHeader(VisualTreeNode node)
{
var result = new StackPanel
{
Orientation = Orientation.Horizontal,
Gap = 8,
Children = new Controls
{
new TextBlock
{
Text = node.Type,
FontStyle = node.IsInTemplate ? Media.FontStyle.Italic : Media.FontStyle.Normal,
},
new TextBlock
{
[!TextBlock.TextProperty] = node.WhenAnyValue(x => x.Classes),
}
}
},
};
result.PointerEnter += this.AddAdorner;
result.PointerLeave += this.RemoveAdorner;
return result;
}
private void AddAdorner(object sender, PointerEventArgs e)
{
var node = (TreeNode)((Control)sender).DataContext;
var layer = AdornerLayer.GetAdornerLayer(node.Control);
if (layer != null)
{
this.adorner = new Rectangle
{
Fill = new SolidColorBrush(0x80a0c5e8),
[AdornerLayer.AdornedElementProperty] = node.Control,
};
layer.Children.Add(this.adorner);
}
}
private void RemoveAdorner(object sender, PointerEventArgs e)
{
if (this.adorner != null)
{
((Panel)this.adorner.Parent).Children.Remove(this.adorner);
this.adorner = null;
}
}
//private Control GetHeader(VisualTreeNode node)
//{
// var result = new StackPanel
// {
// Orientation = Orientation.Horizontal,
// Gap = 8,
// Children = new Controls
// {
// new TextBlock
// {
// Text = node.Type,
// FontStyle = node.IsInTemplate ? Media.FontStyle.Italic : Media.FontStyle.Normal,
// },
// new TextBlock
// {
// [!TextBlock.TextProperty] = node.WhenAnyValue(x => x.Classes),
// }
// }
// };
// result.PointerEnter += this.AddAdorner;
// result.PointerLeave += this.RemoveAdorner;
// return result;
//}
}
}

10
Perspex.Diagnostics/Perspex.Diagnostics.csproj

@ -74,11 +74,19 @@
<Compile Include="DevTools.cs" />
<Compile Include="Debug.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ViewLocator.cs" />
<Compile Include="ViewModels\DevToolsViewModel.cs" />
<Compile Include="ViewModels\VisualTreeViewModel.cs" />
<Compile Include="ViewModels\LogicalTreeViewModel.cs" />
<Compile Include="ViewModels\PropertyDetails.cs" />
<Compile Include="ViewModels\ControlDetails.cs" />
<Compile Include="ViewModels\ControlDetailsViewModel.cs" />
<Compile Include="ViewModels\LogicalTreeNode.cs" />
<Compile Include="ViewModels\TreeNode.cs" />
<Compile Include="ViewModels\VisualTreeNode.cs" />
<Compile Include="Views\ControlDetailsView.cs" />
<Compile Include="Views\VisualTreeView.cs" />
<Compile Include="Views\LogicalTreeView.cs" />
<Compile Include="Views\TreePage.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="ReactiveUI">

34
Perspex.Diagnostics/ViewLocator.cs

@ -0,0 +1,34 @@
// -----------------------------------------------------------------------
// <copyright file="ViewLocator.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Diagnostics
{
using System;
using Perspex.Controls;
internal class ViewLocator<TViewModel> : IDataTemplate
{
public Control Build(object data)
{
var name = data.GetType().FullName.Replace("ViewModel", "View");
var type = Type.GetType(name);
if (type != null)
{
return (Control)Activator.CreateInstance(type);
}
else
{
return new TextBlock { Text = name };
}
}
public bool Match(object data)
{
return data is TViewModel;
}
}
}

6
Perspex.Diagnostics/ViewModels/ControlDetails.cs → Perspex.Diagnostics/ViewModels/ControlDetailsViewModel.cs

@ -1,5 +1,5 @@
// -----------------------------------------------------------------------
// <copyright file="ControlDetails.cs" company="Steven Kirk">
// <copyright file="ControlDetailsViewModel.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
@ -11,9 +11,9 @@ namespace Perspex.Diagnostics.ViewModels
using Perspex.Controls;
using ReactiveUI;
internal class ControlDetails : ReactiveObject
internal class ControlDetailsViewModel : ReactiveObject
{
public ControlDetails(Control control)
public ControlDetailsViewModel(Control control)
{
if (control != null)
{

48
Perspex.Diagnostics/ViewModels/DevToolsViewModel.cs

@ -0,0 +1,48 @@
// -----------------------------------------------------------------------
// <copyright file="DevToolsViewModel.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Diagnostics.ViewModels
{
using System;
using Perspex.Controls;
using ReactiveUI;
internal class DevToolsViewModel : ReactiveObject
{
private Control root;
private LogicalTreeViewModel logicalTree;
private VisualTreeViewModel visualTree;
public DevToolsViewModel()
{
this.WhenAnyValue(x => x.Root).Subscribe(x =>
{
this.LogicalTree = new LogicalTreeViewModel(root);
this.VisualTree = new VisualTreeViewModel(root);
});
}
public Control Root
{
get { return this.root; }
set { this.RaiseAndSetIfChanged(ref this.root, value); }
}
public LogicalTreeViewModel LogicalTree
{
get { return this.logicalTree; }
private set { this.RaiseAndSetIfChanged(ref this.logicalTree, value); }
}
public VisualTreeViewModel VisualTree
{
get { return this.visualTree; }
private set { this.RaiseAndSetIfChanged(ref this.visualTree, value); }
}
}
}

40
Perspex.Diagnostics/ViewModels/LogicalTreeViewModel.cs

@ -0,0 +1,40 @@
// -----------------------------------------------------------------------
// <copyright file="LogicalTreeViewModel.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Diagnostics.ViewModels
{
using System.Reactive.Linq;
using Perspex.Controls;
using ReactiveUI;
internal class LogicalTreeViewModel : ReactiveObject
{
private LogicalTreeNode selected;
private ObservableAsPropertyHelper<ControlDetailsViewModel> details;
public LogicalTreeViewModel(Control root)
{
this.Nodes = LogicalTreeNode.Create(root);
this.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 this.selected; }
set { this.RaiseAndSetIfChanged(ref this.selected, value); }
}
public ControlDetailsViewModel Details
{
get { return this.details.Value; }
}
}
}

40
Perspex.Diagnostics/ViewModels/VisualTreeViewModel.cs

@ -0,0 +1,40 @@
// -----------------------------------------------------------------------
// <copyright file="VisualTreeViewModel.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Diagnostics.ViewModels
{
using System.Reactive.Linq;
using Perspex.Controls;
using ReactiveUI;
internal class VisualTreeViewModel : ReactiveObject
{
private VisualTreeNode selected;
private ObservableAsPropertyHelper<ControlDetailsViewModel> details;
public VisualTreeViewModel(Control root)
{
this.Nodes = VisualTreeNode.Create(root);
this.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 this.selected; }
set { this.RaiseAndSetIfChanged(ref this.selected, value); }
}
public ControlDetailsViewModel Details
{
get { return this.details.Value; }
}
}
}

59
Perspex.Diagnostics/Views/ControlDetailsView.cs

@ -0,0 +1,59 @@
// -----------------------------------------------------------------------
// <copyright file="DevTools.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Diagnostics.Views
{
using Perspex.Controls;
using Perspex.Diagnostics.ViewModels;
using ReactiveUI;
using System;
using System.Reactive.Linq;
internal class ControlDetailsView : UserControl
{
private static readonly PerspexProperty<ControlDetailsViewModel> ViewModelProperty =
PerspexProperty.Register<ControlDetailsView, ControlDetailsViewModel>("ViewModel");
public ControlDetailsView()
{
this.InitializeComponent();
this.GetObservable(DataContextProperty)
.Subscribe(x => this.ViewModel = (ControlDetailsViewModel)x);
}
public ControlDetailsViewModel ViewModel
{
get { return this.GetValue(ViewModelProperty); }
private set { this.SetValue(ViewModelProperty, value); }
}
private void InitializeComponent()
{
this.Content = new ScrollViewer
{
Content = new ItemsControl
{
DataTemplates = new DataTemplates
{
new DataTemplate<PropertyDetails>(x =>
new StackPanel
{
Gap = 16,
Orientation = Orientation.Horizontal,
Children = new Controls
{
new TextBlock { Text = x.Name },
new TextBlock { [!TextBlock.TextProperty] = x.WhenAnyValue(v => v.Value).Select(v => v?.ToString()) },
new TextBlock { Text = x.Priority },
},
}),
},
[!ItemsControl.ItemsProperty] = this.WhenAnyValue(x => x.ViewModel.Properties),
}
};
}
}
}

98
Perspex.Diagnostics/Views/LogicalTreeView.cs

@ -0,0 +1,98 @@
// -----------------------------------------------------------------------
// <copyright file="DevTools.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Diagnostics.Views
{
using Perspex.Controls;
using Perspex.Diagnostics.ViewModels;
using ReactiveUI;
using System;
using System.Reactive.Linq;
internal class LogicalTreeView : TreePage
{
private static readonly PerspexProperty<LogicalTreeViewModel> ViewModelProperty =
PerspexProperty.Register<LogicalTreeView, LogicalTreeViewModel>("ViewModel");
public LogicalTreeView()
{
this.InitializeComponent();
this.GetObservable(DataContextProperty)
.Subscribe(x => this.ViewModel = (LogicalTreeViewModel)x);
}
public LogicalTreeViewModel ViewModel
{
get { return this.GetValue(ViewModelProperty); }
private set { this.SetValue(ViewModelProperty, value); }
}
private void InitializeComponent()
{
TreeView tree;
this.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 TreeDataTemplate<LogicalTreeNode>(this.GetHeader, x => x.Children),
},
[!TreeView.ItemsProperty] = this.WhenAnyValue(x => x.ViewModel.Nodes),
}),
new GridSplitter
{
[Grid.ColumnProperty] = 1,
Width = 4,
},
new ContentControl
{
[!ContentControl.ContentProperty] = this.WhenAnyValue(x => x.ViewModel.Details),
[Grid.ColumnProperty] = 2,
}
}
};
tree.GetObservable(TreeView.SelectedItemProperty)
.OfType<LogicalTreeNode>()
.Subscribe(x => this.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 += this.AddAdorner;
result.PointerLeave += this.RemoveAdorner;
return result;
}
}
}

46
Perspex.Diagnostics/Views/TreePage.cs

@ -0,0 +1,46 @@
// -----------------------------------------------------------------------
// <copyright file="DevTools.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Diagnostics.Views
{
using Perspex.Controls;
using Perspex.Controls.Primitives;
using Perspex.Controls.Shapes;
using Perspex.Diagnostics.ViewModels;
using Perspex.Input;
using Perspex.Media;
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)
{
this.adorner = new Rectangle
{
Fill = new SolidColorBrush(0x80a0c5e8),
[AdornerLayer.AdornedElementProperty] = node.Control,
};
layer.Children.Add(this.adorner);
}
}
protected void RemoveAdorner(object sender, PointerEventArgs e)
{
if (this.adorner != null)
{
((Panel)this.adorner.Parent).Children.Remove(this.adorner);
this.adorner = null;
}
}
}
}

100
Perspex.Diagnostics/Views/VisualTreeView.cs

@ -0,0 +1,100 @@
// -----------------------------------------------------------------------
// <copyright file="VisualTreeView.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Diagnostics.Views
{
using System;
using System.Reactive.Linq;
using Perspex.Controls;
using Perspex.Diagnostics.ViewModels;
using Perspex.Media;
using ReactiveUI;
internal class VisualTreeView : TreePage
{
private static readonly PerspexProperty<VisualTreeViewModel> ViewModelProperty =
PerspexProperty.Register<VisualTreeView, VisualTreeViewModel>("ViewModel");
public VisualTreeView()
{
this.InitializeComponent();
this.GetObservable(DataContextProperty)
.Subscribe(x => this.ViewModel = (VisualTreeViewModel)x);
}
public VisualTreeViewModel ViewModel
{
get { return this.GetValue(ViewModelProperty); }
private set { this.SetValue(ViewModelProperty, value); }
}
private void InitializeComponent()
{
TreeView tree;
this.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 TreeDataTemplate<VisualTreeNode>(this.GetHeader, x => x.Children),
},
[!TreeView.ItemsProperty] = this.WhenAnyValue(x => x.ViewModel.Nodes),
}),
new GridSplitter
{
[Grid.ColumnProperty] = 1,
Width = 4,
},
new ContentControl
{
[!ContentControl.ContentProperty] = this.WhenAnyValue(x => x.ViewModel.Details),
[Grid.ColumnProperty] = 2,
}
}
};
tree.GetObservable(TreeView.SelectedItemProperty)
.OfType<VisualTreeNode>()
.Subscribe(x => this.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 += this.AddAdorner;
result.PointerLeave += this.RemoveAdorner;
return result;
}
}
}

2
Perspex.Themes.Default/DropDownStyle.cs

@ -98,7 +98,7 @@ namespace Perspex.Themes.Default
},
PlacementTarget = control,
StaysOpen = false,
[~~Popup.IsOpenProperty] = control[~~DropDown.IsDropDownOpenProperty],
[!Popup.IsOpenProperty] = control[!DropDown.IsDropDownOpenProperty],
[~Popup.MinWidthProperty] = control[~DropDown.BoundsProperty].Cast<Rect>().Select(x => (object)x.Width),
}
},

Loading…
Cancel
Save