Browse Source

Object graph visualizer can be opened from debugger tooltips.

Attemp to move Object graph visualizer to Pad - will stay in separate topmost Window until AbstractPadContent.IsVisible works properly.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4708 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Martin Koníček 16 years ago
parent
commit
ae9496cac2
  1. 9
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin
  2. 6
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj
  3. 80
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/ObjectGraphPad.cs
  4. 11
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Commands/ObjectGraphVisualizerCommand.cs
  5. 5
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Commands/ObjectGraphVisualizerMenuCommand.cs
  6. 50
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs
  7. 2
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/PositionedGraphNodeControl.xaml.cs
  8. 9
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/NodeControlCache.cs
  9. 6
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs
  10. 35
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs
  11. 51
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphControl.xaml
  12. 228
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphControl.xaml.cs
  13. 48
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml
  14. 191
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs
  15. 6
      src/Main/Base/Project/Src/Services/Debugger/Tooltips/DebuggerTooltipControl.xaml.cs

9
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin

@ -123,6 +123,15 @@ @@ -123,6 +123,15 @@
shortcut = "Control|Alt|W"
class = "ICSharpCode.SharpDevelop.Gui.Pads.WatchPad"
defaultPosition = "Bottom, Hidden" />
<!-- Hidden until DebuggerPad.IsVisible bug is fixed
<Pad id = "ObjectGraphPad"
category = "Debugger"
title = "${res:MainWindow.Windows.Debug.ObjectGraph}"
icon = "PadIcons.LocalVariables"
shortcut = "Control|Alt|O"
class = "ICSharpCode.SharpDevelop.Gui.Pads.ObjectGraphPad"
defaultPosition = "Bottom, Hidden" />
-->
</Path>
<Path name ="/SharpDevelop/Pads/WatchPad/ContextMenu">

6
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj

@ -93,6 +93,7 @@ @@ -93,6 +93,7 @@
<Compile Include="Src\Options\DebuggingSymbolsPanel.Designer.cs">
<DependentUpon>DebuggingSymbolsPanel.cs</DependentUpon>
</Compile>
<Compile Include="Src\Pads\ObjectGraphPad.cs" />
<Compile Include="Src\Pads\WatchPadCommands.cs" />
<Compile Include="Src\Pads\BreakPointsPad.cs" />
<Compile Include="Src\Pads\CallStackPad.cs" />
@ -144,6 +145,10 @@ @@ -144,6 +145,10 @@
<Compile Include="Src\Visualizers\Graph\Layout\ContentPropertyNode.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\ContentNode.cs" />
<Compile Include="Src\Visualizers\Graph\NodeControlCache.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraphControl.xaml.cs">
<DependentUpon>ObjectGraphControl.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Src\Visualizers\Graph\ObjectGraph\NamedEdge.cs" />
<Compile Include="Src\Visualizers\Graph\TreeModel\AbstractNode.cs" />
<Compile Include="Src\Visualizers\Graph\TreeModel\BaseClassNode.cs" />
@ -323,6 +328,7 @@ @@ -323,6 +328,7 @@
<ItemGroup>
<Page Include="Src\Visualizers\Graph\Drawing\PlusToggleButton.xaml" />
<Page Include="Src\Visualizers\Graph\Drawing\PositionedGraphNodeControl.xaml" />
<Page Include="Src\Visualizers\Graph\ObjectGraphControl.xaml" />
<Page Include="Src\Visualizers\GridVisualizer\GridVisualizerWindow.xaml" />
<ProjectReference Include="..\..\..\..\..\Libraries\NRefactory\Project\NRefactory.csproj">
<Project>{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}</Project>

80
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/ObjectGraphPad.cs

@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using ICSharpCode.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Debugger;
using Debugger.AddIn.Visualizers.Graph;
namespace ICSharpCode.SharpDevelop.Gui.Pads
{
/// <summary>
/// Description of ObjectGraphPad.
/// </summary>
public class ObjectGraphPad : DebuggerPad
{
Process debuggedProcess;
ObjectGraphControl objectGraphControl;
static ObjectGraphPad instance;
public ObjectGraphPad()
{
instance = this;
}
/// <remarks>Always check if Instance is null, might be null if pad is not opened!</remarks>
public static ObjectGraphPad Instance {
get { return instance; }
}
protected override void InitializeComponents()
{
objectGraphControl = new ObjectGraphControl();
}
public override object Control {
get {
return objectGraphControl;
}
}
public override void RefreshPad()
{
// BUG: if pad window is undocked and floats standalone, IsVisible == false (so pad won't refresh)
// REQUEST: need to refresh pad
if (!this.IsVisible)
{
LoggingService.Info("skipped refresh");
return;
}
if (debuggedProcess == null || debuggedProcess.IsRunning || debuggedProcess.SelectedStackFrame == null) {
this.objectGraphControl.Clear();
return;
}
this.objectGraphControl.Refresh();
}
protected override void SelectProcess(Process process)
{
if (debuggedProcess != null) {
debuggedProcess.Paused -= debuggedProcess_Paused;
}
debuggedProcess = process;
if (debuggedProcess != null) {
debuggedProcess.Paused += debuggedProcess_Paused;
}
RefreshPad();
}
void debuggedProcess_Paused(object sender, ProcessEventArgs e)
{
RefreshPad();
}
}
}

11
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Commands/ObjectGraphVisualizerCommand.cs

@ -4,10 +4,11 @@ @@ -4,10 +4,11 @@
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using Debugger.AddIn.TreeModel;
using Debugger.AddIn.Visualizers.Graph;
using System;
using System.Collections.Generic;
using System.Linq;
using Debugger.AddIn.TreeModel;
namespace Debugger.AddIn.Visualizers
{
@ -32,7 +33,13 @@ namespace Debugger.AddIn.Visualizers @@ -32,7 +33,13 @@ namespace Debugger.AddIn.Visualizers
public override void Execute()
{
if (this.Node != null && this.Node.Expression != null)
{
var objectGraphWindow = VisualizerWPFWindow.EnsureShown();
// is this ok with different languages than C#?
// - Prettyprint an expression and then call WindowsDebugger.GetValueFromName, which is C# only
objectGraphWindow.ShownExpression = this.Node.Expression.PrettyPrint();
}
}
}
}

5
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Commands/ObjectGraphVisualizerMenuCommand.cs

@ -20,10 +20,7 @@ namespace Debugger.AddIn.Visualizers @@ -20,10 +20,7 @@ namespace Debugger.AddIn.Visualizers
{
public override void Run()
{
VisualizerWPFWindow window = new VisualizerWPFWindow();
window.Topmost = true;
window.Show();
//WorkbenchSingleton.Workbench.ShowView(new DebuggerVisualizerViewContent());
VisualizerWPFWindow.EnsureShown();
}
}
}

50
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
@ -42,6 +43,20 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -42,6 +43,20 @@ namespace Debugger.AddIn.Visualizers.Graph
/// <param name="diff"></param>
public void StartAnimation(PositionedGraph oldGraph, PositionedGraph newGraph, GraphDiff diff)
{
if (oldGraph != null)
{
foreach (var oldNode in oldGraph.Nodes)
{
foreach (var newNode in newGraph.Nodes)
{
if (oldNode.NodeVisualControl == newNode.NodeVisualControl)
{
ClearCanvas();
}
}
}
}
this.canvas.Width = newGraph.BoundingRect.Width;
this.canvas.Height = newGraph.BoundingRect.Height;
@ -112,6 +127,19 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -112,6 +127,19 @@ namespace Debugger.AddIn.Visualizers.Graph
{
canvas.Children.Clear();
/*try
{
// why do the controls disappear?
var n1 = posGraph.Nodes.First().NodeVisualControl;
var n2 = posGraph.Nodes.Skip(1).First().NodeVisualControl;
var n3 = posGraph.Nodes.Skip(2).First().NodeVisualControl;
if (n1 == n2 || n1 == n3 || n2 == n3)
{
ClearCanvas();
}
}
catch{}*/
// draw nodes
foreach (PositionedGraphNode node in posGraph.Nodes)
{
@ -129,6 +157,14 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -129,6 +157,14 @@ namespace Debugger.AddIn.Visualizers.Graph
canvas.Children.Add(edgeTooltip);
}
/// <summary>
/// Clears the drawing Canvas.
/// </summary>
public void ClearCanvas()
{
canvas.Children.Clear();
}
private PositionedGraphNodeControl addNodeToCanvas(PositionedGraphNode node)
{
canvas.Children.Add(node.NodeVisualControl);
@ -160,21 +196,21 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -160,21 +196,21 @@ namespace Debugger.AddIn.Visualizers.Graph
pathInVisible.StrokeThickness = 16;
pathInVisible.Data = geometryInVisible;
pathInVisible.MouseEnter += delegate(object sender, MouseEventArgs e)
{
pathVisible.StrokeThickness = 2;
pathInVisible.MouseEnter += delegate(object sender, MouseEventArgs e)
{
pathVisible.StrokeThickness = 2;
this.edgeTooltip.Text = ((PositionedEdge)pathVisible.Tag).Name;
Point mousePos = e.GetPosition(this.canvas);
Canvas.SetLeft(this.edgeTooltip, mousePos.X - 5);
Canvas.SetTop(this.edgeTooltip, mousePos.Y - 20);
this.edgeTooltip.Visibility = Visibility.Visible;
};
pathInVisible.MouseLeave += delegate(object sender, MouseEventArgs e)
{
pathVisible.StrokeThickness = 1;
pathInVisible.MouseLeave += delegate(object sender, MouseEventArgs e)
{
pathVisible.StrokeThickness = 1;
this.edgeTooltip.Visibility = Visibility.Hidden;
};
pathInVisible.MouseMove += delegate(object sender, MouseEventArgs e)
pathInVisible.MouseMove += delegate(object sender, MouseEventArgs e)
{
Point mousePos = e.GetPosition(this.canvas);
Canvas.SetLeft(this.edgeTooltip, mousePos.X - 5);

2
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/PositionedGraphNodeControl.xaml.cs

@ -124,7 +124,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Drawing @@ -124,7 +124,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Drawing
PropertyCollapsed = null;
ContentNodeExpanded = null;
ContentNodeCollapsed = null;
//this.listView.ItemsSource = null;
this.listView.ItemsSource = null;
}
void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)

9
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/NodeControlCache.cs

@ -33,12 +33,13 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -33,12 +33,13 @@ namespace Debugger.AddIn.Visualizers.Graph
public PositionedGraphNodeControl GetNodeControl()
{
controlsReturned++;
return new PositionedGraphNodeControl();
var control = controls.Count == 0 ? new PositionedGraphNodeControl() : controls.Pop();
// bugs in drawing
/*var control = controls.Count == 0 ? new PositionedGraphNodeControl() : controls.Pop();
control.Init();
//control.InvalidateVisual();
controlsReturned++;
return control;
control.InvalidateVisual();
return control;*/
}
public void Clear()

6
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs

@ -11,12 +11,12 @@ using System.Text; @@ -11,12 +11,12 @@ using System.Text;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Object graph built by <see cref="ObjectGraphBuilder"/>
/// Object graph built by <see cref="ObjectGraphBuilder"/>. The graph is never empty.
/// </summary>
public class ObjectGraph
{
/// <summary>
/// Root of the graph.
/// Root of the graph. Should never be null.
/// </summary>
public ObjectGraphNode Root { get; internal set; }
@ -31,7 +31,7 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -31,7 +31,7 @@ namespace Debugger.AddIn.Visualizers.Graph
private List<ObjectGraphNode> _nodes = new List<ObjectGraphNode>();
/// <summary>
/// All nodes in the graph.
/// All nodes in the graph. Should always contain at least one node.
/// </summary>
public IEnumerable<ObjectGraphNode> Nodes
{

35
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs

@ -80,27 +80,30 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -80,27 +80,30 @@ namespace Debugger.AddIn.Visualizers.Graph
/// <returns>Object graph</returns>
public ObjectGraph BuildGraphForExpression(string expression, ExpandedExpressions expandedNodes)
{
if (string.IsNullOrEmpty(expression))
{
throw new DebuggerVisualizerException("Expression is empty.");
if (string.IsNullOrEmpty(expression)) {
throw new DebuggerVisualizerException("Please specify an expression.");
}
resultGraph = new ObjectGraph();
Value rootValue = debuggerService.GetValueFromName(expression);
if (rootValue == null)
{
var debuggedProcess = this.debuggerService.DebuggedProcess;
if (debuggedProcess == null || debuggedProcess.IsRunning || debuggedProcess.SelectedStackFrame == null) {
throw new DebuggerVisualizerException("Please use the visualizer when debugging.");
}
// empty graph for null expression
if (!rootValue.IsNull)
{
//resultGraph.Root = buildGraphRecursive(debuggerService.GetValueFromName(expression).GetPermanentReference(), expandedNodes);
resultGraph.Root = createNewNode(debuggerService.GetValueFromName(expression).GetPermanentReference());
loadContent(resultGraph.Root);
loadNeighborsRecursive(resultGraph.Root, expandedNodes);
// TODO GetValueFromName calls evaluator with SupportedLanguage.CSharp, is that ok?
Value rootValue = debuggerService.GetValueFromName(expression);
if (rootValue.IsNull) {
throw new DebuggerVisualizerException(expression + " is null.");
}
return buildGraphForValue(rootValue, expandedNodes);
}
private ObjectGraph buildGraphForValue(Value rootValue, ExpandedExpressions expandedNodes)
{
resultGraph = new ObjectGraph();
//resultGraph.Root = buildGraphRecursive(debuggerService.GetValueFromName(expression).GetPermanentReference(), expandedNodes);
resultGraph.Root = createNewNode(rootValue.GetPermanentReference());
loadContent(resultGraph.Root);
loadNeighborsRecursive(resultGraph.Root, expandedNodes);
return resultGraph;
}

51
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphControl.xaml

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
<UserControl x:Class="Debugger.AddIn.Visualizers.Graph.ObjectGraphControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dv="clr-namespace:Debugger.AddIn.Visualizers.Graph"
xmlns:controls="clr-namespace:Debugger.AddIn.Visualizers.Controls">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Background="AliceBlue" VerticalAlignment="Top">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="0 0 8 0" />
</Style>
<Style TargetType="Button">
<Setter Property="Margin" Value="8 0 0 0" />
<Setter Property="Padding" Value="8 0 8 0" />
</Style>
</StackPanel.Resources>
<Border Margin="3" Padding="3">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center">Expression:</TextBlock>
<TextBox Name="txtExpression" VerticalAlignment="Center" Width="100"></TextBox>
<Button Click="Inspect_Button_Click">Inspect</Button>
<TextBlock Margin="12 0 6 0" VerticalAlignment="Center">Layout:</TextBlock>
<ComboBox Name="cmbLayoutDirection" Width="150" ItemsSource="{Binding Path=EnumValues}" SelectedValue="{Binding Path=SelectedEnumValue}" SelectedValuePath="EnumValue" DisplayMemberPath="DisplayValue"></ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Name="pnlError" Visibility="Collapsed" Margin="0 6 0 0">
<TextBlock Margin="0 0 4 0" Name="lblError" FontStyle="Italic">Error: </TextBlock>
<TextBlock Margin="0 0 0 0" Name="txtError" FontStyle="Italic"></TextBlock>
</StackPanel>
</StackPanel>
</Border>
<Border Margin="3" Padding="3">
<StackPanel Orientation="Horizontal">
</StackPanel>
</Border>
</StackPanel>
<controls:DragScrollViewer x:Name="scroller" Margin="0 4 0 0" Grid.Row="1" Grid.Column="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Canvas HorizontalAlignment="Left" VerticalAlignment="Top" Name="canvas" Margin="4">
</Canvas>
</controls:DragScrollViewer>
</Grid>
</UserControl>

228
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphControl.xaml.cs

@ -0,0 +1,228 @@ @@ -0,0 +1,228 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using Debugger.AddIn.Visualizers.Graph.Layout;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Services;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Interaction logic for ObjectGraphControl.xaml
/// </summary>
public partial class ObjectGraphControl : UserControl
{
private WindowsDebugger debuggerService;
private EnumViewModel<LayoutDirection> layoutViewModel;
private ObjectGraph objectGraph;
private ObjectGraphBuilder objectGraphBuilder;
private PositionedGraph oldPosGraph;
private PositionedGraph currentPosGraph;
private GraphDrawer graphDrawer;
private Layout.TreeLayouter layouter;
/// <summary> Long-lived map telling which graph nodes and content nodes the user expanded. </summary>
private Expanded expanded = new Expanded();
public ObjectGraphControl()
{
InitializeComponent();
debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger;
if (debuggerService == null)
throw new ApplicationException("Only windows debugger is currently supported");
this.layoutViewModel = new EnumViewModel<LayoutDirection>();
this.layoutViewModel.PropertyChanged += new PropertyChangedEventHandler(layoutViewModel_PropertyChanged);
this.cmbLayoutDirection.DataContext = this.layoutViewModel;
this.layouter = new TreeLayouter();
this.graphDrawer = new GraphDrawer(this.canvas);
}
public void Clear()
{
txtExpression.Text = string.Empty;
}
public void Refresh()
{
refreshGraph();
}
public string ShownExpression
{
get {
return this.txtExpression.Text;
}
set {
if (value != this.txtExpression.Text) {
this.txtExpression.Text = value;
}
}
}
/// <summary>
/// ObjectGraph visualizer caches UI controls, this clears the cache.
/// </summary>
public void ClearUIControlCache()
{
NodeControlCache.Instance.Clear();
}
private void Inspect_Button_Click(object sender, RoutedEventArgs e)
{
this.Refresh();
}
void refreshGraph()
{
clearErrorMessage();
if (string.IsNullOrEmpty(txtExpression.Text))
{
this.graphDrawer.ClearCanvas();
return;
}
if (debuggerService.IsProcessRunning) // TODO "Process not paused" exception still occurs
{
showErrorMessage("Cannot inspect when the process is running.");
return;
}
bool graphBuiltOk = true;
try
{
this.objectGraph = rebuildGraph(txtExpression.Text);
}
catch(DebuggerVisualizerException ex)
{
graphBuiltOk = false;
showErrorMessage(ex.Message);
}
catch(Debugger.GetValueException ex)
{
graphBuiltOk = false;
showErrorMessage("Cannot evaluate: " + ex.Message);
}
if (graphBuiltOk)
{
layoutGraph(this.objectGraph);
}
else
{
this.graphDrawer.ClearCanvas();
}
}
ObjectGraph rebuildGraph(string expression)
{
this.objectGraphBuilder = new ObjectGraphBuilder(debuggerService);
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text);
return this.objectGraphBuilder.BuildGraphForExpression(expression, this.expanded.Expressions);
}
void layoutGraph(ObjectGraph graph)
{
if (this.oldPosGraph != null)
{
foreach (var oldNode in this.oldPosGraph.Nodes)
{
// controls from old graph would be garbage collected, reuse them
NodeControlCache.Instance.ReturnForReuse(oldNode.NodeVisualControl);
}
}
this.oldPosGraph = this.currentPosGraph;
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout");
this.currentPosGraph = this.layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue, this.expanded);
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Graph layout done");
registerExpandCollapseEvents(this.currentPosGraph);
var graphDiff = new GraphMatcher().MatchGraphs(oldPosGraph, currentPosGraph);
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: starting graph animation");
this.graphDrawer.StartAnimation(oldPosGraph, currentPosGraph, graphDiff);
//this.graphDrawer.Draw(this.currentPosGraph); // buggy layout with NodeControlCache
}
void layoutViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedEnumValue") // TODO special event for enum value change
{
if (this.objectGraph != null)
{
layoutGraph(this.objectGraph);
}
}
}
void clearErrorMessage()
{
this.pnlError.Visibility = Visibility.Collapsed;
}
void showErrorMessage(string message)
{
this.txtError.Text = message;
this.pnlError.Visibility = Visibility.Visible;
//MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
}
void registerExpandCollapseEvents(PositionedGraph posGraph)
{
foreach (var node in posGraph.Nodes)
{
node.PropertyExpanded += new EventHandler<PositionedPropertyEventArgs>(node_PropertyExpanded);
node.PropertyCollapsed += new EventHandler<PositionedPropertyEventArgs>(node_PropertyCollapsed);
node.ContentNodeExpanded += new EventHandler<ContentNodeEventArgs>(node_ContentNodeExpanded);
node.ContentNodeCollapsed += new EventHandler<ContentNodeEventArgs>(node_ContentNodeCollapsed);
}
}
void node_ContentNodeExpanded(object sender, ContentNodeEventArgs e)
{
expanded.ContentNodes.SetExpanded(e.Node);
layoutGraph(this.objectGraph);
}
void node_ContentNodeCollapsed(object sender, ContentNodeEventArgs e)
{
expanded.ContentNodes.SetCollapsed(e.Node);
layoutGraph(this.objectGraph);
}
void node_PropertyExpanded(object sender, PositionedPropertyEventArgs e)
{
// remember this property is expanded (for later graph rebuilds)
expanded.Expressions.SetExpanded(e.Property.Expression);
// add edge (+ possibly nodes) to underlying object graph (no need to fully rebuild)
// TODO can add more nodes if they are expanded - now this adds always one node
e.Property.ObjectGraphProperty.TargetNode = this.objectGraphBuilder.ObtainNodeForExpression(e.Property.Expression);
layoutGraph(this.objectGraph);
}
void node_PropertyCollapsed(object sender, PositionedPropertyEventArgs e)
{
// remember this property is collapsed (for later graph rebuilds)
expanded.Expressions.SetCollapsed(e.Property.Expression);
// just remove edge from underlying object graph (no need to fully rebuild)
e.Property.ObjectGraphProperty.TargetNode = null;
layoutGraph(this.objectGraph);
}
}
}

48
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml

@ -4,50 +4,6 @@ @@ -4,50 +4,6 @@
xmlns:dv="clr-namespace:Debugger.AddIn.Visualizers.Graph"
xmlns:controls="clr-namespace:Debugger.AddIn.Visualizers.Controls"
Title="Object graph visualizer" Height="400" Width="600">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Background="AliceBlue" VerticalAlignment="Top">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="0 0 8 0" />
</Style>
<Style TargetType="Button">
<Setter Property="Margin" Value="8 0 0 0" />
<Setter Property="Padding" Value="8 0 8 0" />
</Style>
</StackPanel.Resources>
<Border Margin="3" Padding="3">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center">Expression:</TextBlock>
<TextBox Name="txtExpression" VerticalAlignment="Center" Width="100"></TextBox>
<Button Click="Inspect_Button_Click">Inspect</Button>
<TextBlock Margin="12 0 6 0" VerticalAlignment="Center">Layout:</TextBlock>
<ComboBox Name="cmbLayoutDirection" Width="150" ItemsSource="{Binding Path=EnumValues}" SelectedValue="{Binding Path=SelectedEnumValue}" SelectedValuePath="EnumValue" DisplayMemberPath="DisplayValue"></ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Name="pnlError" Visibility="Collapsed" Margin="0 6 0 0">
<TextBlock Margin="0 0 4 0" Name="lblError" FontStyle="Italic">Error: </TextBlock>
<TextBlock Margin="0 0 0 0" Name="txtError" FontStyle="Italic"></TextBlock>
</StackPanel>
</StackPanel>
</Border>
<Border Margin="3" Padding="3">
<StackPanel Orientation="Horizontal">
</StackPanel>
</Border>
</StackPanel>
<controls:DragScrollViewer x:Name="scroller" Margin="0 4 0 0" Grid.Row="1" Grid.Column="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Canvas HorizontalAlignment="Left" VerticalAlignment="Top" Name="canvas" Margin="4">
</Canvas>
</controls:DragScrollViewer>
</Grid>
<dv:ObjectGraphControl x:Name="objectGraphControl"></dv:ObjectGraphControl>
</Window>

191
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs

@ -29,20 +29,7 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -29,20 +29,7 @@ namespace Debugger.AddIn.Visualizers.Graph
public partial class VisualizerWPFWindow : Window
{
private WindowsDebugger debuggerService;
private EnumViewModel<LayoutDirection> layoutViewModel;
private ObjectGraph objectGraph;
private ObjectGraphBuilder objectGraphBuilder;
private PositionedGraph oldPosGraph;
private PositionedGraph currentPosGraph;
private GraphDrawer graphDrawer;
private Layout.TreeLayouter layouter;
/// <summary>
/// Long-lived map telling which graph nodes and content nodes the user expanded.
/// </summary>
private Expanded expanded = new Expanded();
public VisualizerWPFWindow()
{
InitializeComponent();
@ -51,181 +38,69 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -51,181 +38,69 @@ namespace Debugger.AddIn.Visualizers.Graph
if (debuggerService == null)
throw new ApplicationException("Only windows debugger is currently supported");
this.Closed += new EventHandler(VisualizerWPFWindow_Closed);
registerEvents();
this.layoutViewModel = new EnumViewModel<LayoutDirection>();
this.layoutViewModel.PropertyChanged += new PropertyChangedEventHandler(layoutViewModel_PropertyChanged);
this.DataContext = this.layoutViewModel;
this.layouter = new TreeLayouter();
this.graphDrawer = new GraphDrawer(this.canvas);
}
void VisualizerWPFWindow_Closed(object sender, EventArgs e)
{
unregisterEvents();
NodeControlCache.Instance.Clear();
instance = this;
}
private void registerEvents()
protected override void OnClosed(EventArgs e)
{
debuggerService.IsProcessRunningChanged += new EventHandler(debuggerService_IsProcessRunningChanged);
debuggerService.DebugStopped += new EventHandler(_debuggerService_DebugStopped);
}
private void unregisterEvents()
{
debuggerService.IsProcessRunningChanged -= new EventHandler(debuggerService_IsProcessRunningChanged);
debuggerService.DebugStopped -= new EventHandler(_debuggerService_DebugStopped);
}
public void debuggerService_IsProcessRunningChanged(object sender, EventArgs e)
{
// on step or breakpoint hit
if (!debuggerService.IsProcessRunning)
{
refreshGraph();
}
base.OnClosed(e);
unregisterEvents();
this.objectGraphControl.ClearUIControlCache();
instance = null;
}
public void _debuggerService_DebugStopped(object sender, EventArgs e)
static VisualizerWPFWindow instance;
public static VisualizerWPFWindow Instance
{
this.Close();
get { return instance; }
}
private void Inspect_Button_Click(object sender, RoutedEventArgs e)
public string ShownExpression
{
clearErrorMessage();
if (debuggerService.IsProcessRunning) // TODO "Process not paused" exception still occurs
{
showErrorMessage("Cannot inspect when the process is running.");
get {
return this.objectGraphControl.ShownExpression;
}
else
{
refreshGraph();
}
}
void layoutViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedEnumValue") // TODO special event for enum value change
{
if (this.objectGraph != null)
{
layoutGraph(this.objectGraph);
set {
if (value != this.objectGraphControl.ShownExpression) {
this.objectGraphControl.ShownExpression = value;
this.objectGraphControl.Refresh();
}
}
}
void refreshGraph()
{
bool graphBuiltOk = true;
clearErrorMessage();
try
{
this.objectGraph = rebuildGraph();
}
catch(DebuggerVisualizerException ex)
{
graphBuiltOk = false;
showErrorMessage(ex.Message);
}
catch(Debugger.GetValueException ex)
{
graphBuiltOk = false;
showErrorMessage(ex.Message);
}
if (graphBuiltOk)
{
layoutGraph(this.objectGraph);
}
}
ObjectGraph rebuildGraph()
public static VisualizerWPFWindow EnsureShown()
{
this.objectGraphBuilder = new ObjectGraphBuilder(debuggerService);
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text);
return this.objectGraphBuilder.BuildGraphForExpression(txtExpression.Text, this.expanded.Expressions);
var window = VisualizerWPFWindow.Instance ?? new VisualizerWPFWindow();
window.Topmost = true;
window.Show();
return window;
}
void layoutGraph(ObjectGraph graph)
{
if (this.oldPosGraph != null)
{
foreach (var oldNode in this.oldPosGraph.Nodes)
{
// controls from old graph would be garbage collected, reuse them
NodeControlCache.Instance.ReturnForReuse(oldNode.NodeVisualControl);
}
}
this.oldPosGraph = this.currentPosGraph;
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout");
this.currentPosGraph = this.layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue, this.expanded);
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Graph layout done");
registerExpandCollapseEvents(this.currentPosGraph);
var graphDiff = new GraphMatcher().MatchGraphs(oldPosGraph, currentPosGraph);
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: starting graph animation");
this.graphDrawer.StartAnimation(oldPosGraph, currentPosGraph, graphDiff);
//this.graphDrawer.Draw(this.currentPosGraph);
}
void clearErrorMessage()
private void registerEvents()
{
this.pnlError.Visibility = Visibility.Collapsed;
debuggerService.IsProcessRunningChanged += new EventHandler(debuggerService_IsProcessRunningChanged);
debuggerService.DebugStopped += new EventHandler(debuggerService_DebugStopped);
}
void showErrorMessage(string message)
private void unregisterEvents()
{
this.txtError.Text = message;
this.pnlError.Visibility = Visibility.Visible;
//MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
debuggerService.IsProcessRunningChanged -= new EventHandler(debuggerService_IsProcessRunningChanged);
debuggerService.DebugStopped -= new EventHandler(debuggerService_DebugStopped);
}
void registerExpandCollapseEvents(PositionedGraph posGraph)
public void debuggerService_IsProcessRunningChanged(object sender, EventArgs e)
{
foreach (var node in posGraph.Nodes)
// on step or breakpoint hit
if (!debuggerService.IsProcessRunning)
{
node.PropertyExpanded += new EventHandler<PositionedPropertyEventArgs>(node_PropertyExpanded);
node.PropertyCollapsed += new EventHandler<PositionedPropertyEventArgs>(node_PropertyCollapsed);
node.ContentNodeExpanded += new EventHandler<ContentNodeEventArgs>(node_ContentNodeExpanded);
node.ContentNodeCollapsed += new EventHandler<ContentNodeEventArgs>(node_ContentNodeCollapsed);
this.objectGraphControl.Refresh();
}
}
void node_ContentNodeExpanded(object sender, ContentNodeEventArgs e)
public void debuggerService_DebugStopped(object sender, EventArgs e)
{
expanded.ContentNodes.SetExpanded(e.Node);
layoutGraph(this.objectGraph);
}
void node_ContentNodeCollapsed(object sender, ContentNodeEventArgs e)
{
expanded.ContentNodes.SetCollapsed(e.Node);
layoutGraph(this.objectGraph);
}
void node_PropertyExpanded(object sender, PositionedPropertyEventArgs e)
{
// remember this property is expanded (for later graph rebuilds)
expanded.Expressions.SetExpanded(e.Property.Expression);
// add edge (+ possibly nodes) to underlying object graph (no need to fully rebuild)
// TODO can add more nodes if they are expanded - now this adds always one node
e.Property.ObjectGraphProperty.TargetNode = this.objectGraphBuilder.ObtainNodeForExpression(e.Property.Expression);
layoutGraph(this.objectGraph);
}
void node_PropertyCollapsed(object sender, PositionedPropertyEventArgs e)
{
// remember this property is collapsed (for later graph rebuilds)
expanded.Expressions.SetCollapsed(e.Property.Expression);
// just remove edge from underlying object graph (no need to fully rebuild)
e.Property.ObjectGraphProperty.TargetNode = null;
layoutGraph(this.objectGraph);
this.Close();
}
}
}

6
src/Main/Base/Project/Src/Services/Debugger/Tooltips/DebuggerTooltipControl.xaml.cs

@ -25,6 +25,8 @@ namespace ICSharpCode.SharpDevelop.Debugging @@ -25,6 +25,8 @@ namespace ICSharpCode.SharpDevelop.Debugging
{
private readonly double ChildPopupOpenXOffet = 16;
private readonly double ChildPopupOpenYOffet = 15;
private readonly int InitialItemsCount = 12;
private readonly int VisibleItemsCount = 11;
public DebuggerTooltipControl()
{
@ -65,7 +67,7 @@ namespace ICSharpCode.SharpDevelop.Debugging @@ -65,7 +67,7 @@ namespace ICSharpCode.SharpDevelop.Debugging
set
{
this.itemsSource = value;
this.lazyGrid = new LazyItemsControl<ITreeNode>(this.dataGrid, 12);
this.lazyGrid = new LazyItemsControl<ITreeNode>(this.dataGrid, InitialItemsCount);
lazyGrid.ItemsSource = new VirtualizingIEnumerable<ITreeNode>(value);
this.dataGrid.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(handleScroll));
@ -73,7 +75,7 @@ namespace ICSharpCode.SharpDevelop.Debugging @@ -73,7 +75,7 @@ namespace ICSharpCode.SharpDevelop.Debugging
{
// hide up/down buttons if too few items
btnUp.Visibility = btnDown.Visibility =
this.lazyGrid.ItemsSourceTotalCount.Value <= 10 ? Visibility.Collapsed : Visibility.Visible;
this.lazyGrid.ItemsSourceTotalCount.Value <= VisibleItemsCount ? Visibility.Collapsed : Visibility.Visible;
}
}
get

Loading…
Cancel
Save