-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use AutomaticGraphLayout.WpfGraphControl
This replaces the use of images generated by WinGraphViz with a WPF control that allows interaction. A few features have been removed, such as the creation of DOT files and the ability to save to PNG files.
- Loading branch information
1 parent
8dd5eeb
commit 2f6ec6c
Showing
44 changed files
with
330 additions
and
1,459 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<Application | ||
x:Class="DependencyAnalyser.App" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
StartupUri="DependencyAnalyzerWindow.xaml" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using System.Windows; | ||
|
||
namespace DependencyAnalyser | ||
{ | ||
public partial class App : Application | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<Window x:Class="DependencyAnalyser.DependencyAnalyzerWindow" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
xmlns:mlsagl="http://mlsagl" | ||
mc:Ignorable="d" | ||
Title=".NET Dependency Analyzer" | ||
Height="450" | ||
Width="800"> | ||
|
||
<Window.Resources> | ||
|
||
<!-- Hide the icon area from menus (https://stackoverflow.com/a/17724242/24874) --> | ||
<ItemsPanelTemplate x:Key="MenuItemPanelTemplate"> | ||
<StackPanel Margin="-20,0,0,0" Background="White"/> | ||
</ItemsPanelTemplate> | ||
<Style TargetType="{x:Type MenuItem}"> | ||
<Setter Property="ItemsPanel" Value="{StaticResource MenuItemPanelTemplate}"/> | ||
</Style> | ||
<Style TargetType="{x:Type ContextMenu}"> | ||
<Setter Property="ItemsPanel" Value="{StaticResource MenuItemPanelTemplate}"/> | ||
</Style> | ||
|
||
</Window.Resources> | ||
|
||
<DockPanel> | ||
|
||
<Menu DockPanel.Dock="Top"> | ||
<!-- TODO convert to commands and handle CanExecute --> | ||
<!-- TODO bind keyboard shortcuts --> | ||
<MenuItem Header="_File"> | ||
<MenuItem Header="_Open..." Click="OnOpenClicked"></MenuItem> | ||
<MenuItem Header="_Merge..." Click="OnMergeClicked"></MenuItem> | ||
<!-- | ||
<MenuItem Header="Save _PNG..."></MenuItem> | ||
<MenuItem Header="Save _SVG..."></MenuItem> | ||
--> | ||
<Separator /> | ||
<MenuItem Header="E_xit" Click="OnExitClicked"></MenuItem> | ||
</MenuItem> | ||
<MenuItem Header="_Simplify" Click="OnSimplifyClicked"></MenuItem> | ||
<MenuItem Header="F_ilter..." Click="OnFilterClicked"></MenuItem> | ||
<MenuItem Header="_About..." Click="OnAboutClicked"></MenuItem> | ||
</Menu> | ||
|
||
<TabControl TabStripPlacement="Bottom"> | ||
|
||
<TabItem Header="Graph"> | ||
<mlsagl:AutomaticGraphLayoutControl x:Name="_graphControl" /> | ||
</TabItem> | ||
|
||
<TabItem Header="Log"> | ||
<TextBox IsReadOnly="True" IsReadOnlyCaretVisible="True" x:Name="_log" HorizontalScrollBarVisibility="Auto" /> | ||
</TabItem> | ||
|
||
</TabControl> | ||
|
||
</DockPanel> | ||
|
||
</Window> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
using System; | ||
using System.Reflection; | ||
using System.Threading.Tasks; | ||
using System.Windows; | ||
using System.Windows.Input; | ||
using Microsoft.Win32; | ||
using Microsoft.Msagl.Drawing; | ||
|
||
namespace DependencyAnalyser | ||
{ | ||
public partial class DependencyAnalyzerWindow | ||
{ | ||
private readonly OpenFileDialog _openFileDialog; | ||
private readonly FilterPreferences _filterPreferences = new(); | ||
private readonly StringBuilderLogger _logger = new(); | ||
|
||
private DependencyGraph<string> _dependencyGraph = new(); | ||
|
||
public DependencyAnalyzerWindow() | ||
{ | ||
_openFileDialog = new OpenFileDialog() | ||
{ | ||
Filter = "All supported files|*.sln;*.csproj;*.vbproj;*.fsproj;*.exe;*.dll|" + | ||
"Solution files (*.sln)|*.sln|" + | ||
"Project files (*.csproj)|*.csproj;*.vbproj;*.fsproj|" + | ||
"Assemblies (*.dll)|*.dll|" + | ||
"Assemblies (*.exe)|*.exe|" + | ||
"All files|*.*", | ||
Title = "Open File" | ||
}; | ||
|
||
InitializeComponent(); | ||
} | ||
|
||
private void OnAboutClicked(object sender, RoutedEventArgs e) | ||
{ | ||
// TODO make this a dialog with a link button | ||
MessageBox.Show($".NET Assembly Dependency Analyser v{Assembly.GetExecutingAssembly().GetName().Version}\n\nCopyright Drew Noakes 2003-{DateTime.Now.Year}.\n\nThanks to John Maher.\n\nLatest version at http://drewnoakes.com/code/dependency-analyser/\nCharts provided using Dot & Wingraphviz."); | ||
} | ||
|
||
private void OnFilterClicked(object sender, RoutedEventArgs e) | ||
{ | ||
using (WaitCursor()) | ||
{ | ||
var filterForm = new FilterForm(_filterPreferences); | ||
|
||
if (filterForm.ShowDialog() == System.Windows.Forms.DialogResult.OK) | ||
UpdateDiagram(); | ||
} | ||
} | ||
|
||
private void OnExitClicked(object sender, RoutedEventArgs e) | ||
{ | ||
Application.Current.Shutdown(); | ||
} | ||
|
||
#region Wait cursor | ||
|
||
private IDisposable WaitCursor() | ||
{ | ||
var reverter = new CursorReverter(Cursor, this); | ||
Cursor = Cursors.Wait; | ||
return reverter; | ||
} | ||
|
||
private sealed class CursorReverter : IDisposable | ||
{ | ||
private readonly Cursor _cursor; | ||
private readonly Window _form; | ||
|
||
public CursorReverter(Cursor cursor, Window form) | ||
{ | ||
_cursor = cursor; | ||
_form = form; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
_form.Cursor = _cursor; | ||
} | ||
} | ||
|
||
#endregion | ||
|
||
private async void OnOpenClicked(object sender, RoutedEventArgs e) | ||
{ | ||
// Start a new graph | ||
_dependencyGraph = new DependencyGraph<string>(); | ||
|
||
await MergeFileAsync(); | ||
} | ||
|
||
private async void OnMergeClicked(object sender, RoutedEventArgs e) | ||
{ | ||
await MergeFileAsync(); | ||
} | ||
|
||
private async Task MergeFileAsync() | ||
{ | ||
try | ||
{ | ||
if (_openFileDialog.ShowDialog() != true) | ||
{ | ||
return; | ||
} | ||
|
||
string fileName = _openFileDialog.FileName; | ||
|
||
using (WaitCursor()) | ||
{ | ||
if (fileName.EndsWith(".sln") || fileName.EndsWith("proj")) | ||
{ | ||
await SolutionAndProjectFileAnalyser.AnalyseAsync(fileName, _dependencyGraph, _logger); | ||
} | ||
else | ||
{ | ||
var assembly = Assembly.LoadFrom(fileName); | ||
AssemblyAnalyser.Analyze(assembly, _dependencyGraph, _logger); | ||
} | ||
|
||
_filterPreferences.SetAssemblyNames(_dependencyGraph.Nodes); | ||
// EnableAndDisableMenuItems(); | ||
UpdateDiagram(); | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
MessageBox.Show(e.ToString()); | ||
} | ||
finally | ||
{ | ||
_log.Text = _logger.ToString(); | ||
} | ||
} | ||
|
||
private void UpdateDiagram() | ||
{ | ||
var graph = new Graph(); | ||
|
||
// TODO style projects and assemblies differently | ||
// TODO style nodes with no dependents differently | ||
// TODO style nodes with no dependencies differently | ||
|
||
foreach (var depending in _dependencyGraph.Nodes) | ||
{ | ||
if (!_filterPreferences.IncludeInPlot(depending)) | ||
continue; | ||
|
||
foreach (var depended in _dependencyGraph.GetDependenciesForNode(depending)) | ||
{ | ||
if (!_filterPreferences.IncludeInPlot(depended)) | ||
continue; | ||
|
||
graph.AddEdge(depending, depended); | ||
} | ||
} | ||
|
||
graph.Attr.LayerDirection = LayerDirection.TB; | ||
graph.Attr.AspectRatio = _graphControl.ActualWidth / _graphControl.ActualHeight; | ||
|
||
_graphControl.Graph = null; | ||
_graphControl.Graph = graph; | ||
// _graphControl.InvalidateMeasure(); | ||
// _graphControl.InvalidateVisual(); | ||
} | ||
|
||
private void OnSimplifyClicked(object sender, RoutedEventArgs e) | ||
{ | ||
_dependencyGraph.TransitiveReduce(); | ||
|
||
UpdateDiagram(); | ||
} | ||
} | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
|
||
using Microsoft.Build.Locator; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.MSBuild; | ||
|
||
namespace DependencyAnalyser | ||
{ | ||
/// <summary> | ||
/// Builds a DependencyGraph from a given Visual Studio solution (.sln) or project (e.g. .csproj) file. | ||
/// </summary> | ||
public static class SolutionAndProjectFileAnalyser | ||
{ | ||
private static bool _isRegistered; | ||
|
||
public static async Task AnalyseAsync(string filePath, DependencyGraph<string> graph, ILogger logger) | ||
{ | ||
if (!_isRegistered) | ||
{ | ||
var instance = MSBuildLocator.QueryVisualStudioInstances().OrderByDescending(i => i.Version).First(); | ||
|
||
logger.WriteLine($"Located MSBuild at: {instance.MSBuildPath}"); | ||
|
||
MSBuildLocator.RegisterInstance(instance); | ||
|
||
_isRegistered = true; | ||
} | ||
|
||
using var workspace = MSBuildWorkspace.Create(); | ||
|
||
workspace.WorkspaceFailed += (o, e) => logger.WriteLine(e.Diagnostic.Message); | ||
|
||
if (filePath.EndsWith(".sln")) | ||
{ | ||
logger.WriteLine($"Loading solution: {filePath}"); | ||
await workspace.OpenSolutionAsync(filePath); | ||
logger.WriteLine($"Finished loading solution: {filePath}"); | ||
} | ||
else | ||
{ | ||
logger.WriteLine($"Loading project: {filePath}"); | ||
await workspace.OpenProjectAsync(filePath); | ||
logger.WriteLine($"Finished loading project: {filePath}"); | ||
} | ||
|
||
var projectById = new Dictionary<Guid, Project>(); | ||
|
||
foreach (var project in workspace.CurrentSolution.Projects) | ||
{ | ||
projectById.Add(project.Id.Id, project); | ||
} | ||
|
||
foreach (var project in workspace.CurrentSolution.Projects) | ||
{ | ||
foreach (var projectReference in project.ProjectReferences) | ||
{ | ||
var referencedProject = projectById[projectReference.ProjectId.Id]; | ||
|
||
graph.AddDependency(project.Name, referencedProject.Name); | ||
} | ||
} | ||
} | ||
} | ||
} |
Binary file not shown.
Binary file not shown.
This file was deleted.
Oops, something went wrong.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Oops, something went wrong.