diff --git a/Source/DependencyAnalyser.sln b/DependencyAnalyser.sln
similarity index 67%
rename from Source/DependencyAnalyser.sln
rename to DependencyAnalyser.sln
index 613f937..430e750 100644
--- a/Source/DependencyAnalyser.sln
+++ b/DependencyAnalyser.sln
@@ -4,13 +4,10 @@ VisualStudioVersion = 17.0.31412.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DependencyAnalyser", "DependencyAnalyser\DependencyAnalyser.csproj", "{C5BAF76A-B49F-40D3-A24B-26880A1352DD}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DependencyAnalyser.Tests", "DependencyAnalyser.Tests\DependencyAnalyser.Tests.csproj", "{962A3463-9C5E-4BA2-AA40-AF7AB120CEA8}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6F8C6B01-A2D4-4115-8F83-D6C9F5A77AF6}"
ProjectSection(SolutionItems) = preProject
..\.gitignore = ..\.gitignore
..\LICENSE.txt = ..\LICENSE.txt
- Notes.txt = Notes.txt
..\README.md = ..\README.md
EndProjectSection
EndProject
@@ -30,14 +27,6 @@ Global
{C5BAF76A-B49F-40D3-A24B-26880A1352DD}.Release|Any CPU.Build.0 = Release|Any CPU
{C5BAF76A-B49F-40D3-A24B-26880A1352DD}.Release|x86.ActiveCfg = Release|x86
{C5BAF76A-B49F-40D3-A24B-26880A1352DD}.Release|x86.Build.0 = Release|x86
- {962A3463-9C5E-4BA2-AA40-AF7AB120CEA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {962A3463-9C5E-4BA2-AA40-AF7AB120CEA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {962A3463-9C5E-4BA2-AA40-AF7AB120CEA8}.Debug|x86.ActiveCfg = Debug|x86
- {962A3463-9C5E-4BA2-AA40-AF7AB120CEA8}.Debug|x86.Build.0 = Debug|x86
- {962A3463-9C5E-4BA2-AA40-AF7AB120CEA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {962A3463-9C5E-4BA2-AA40-AF7AB120CEA8}.Release|Any CPU.Build.0 = Release|Any CPU
- {962A3463-9C5E-4BA2-AA40-AF7AB120CEA8}.Release|x86.ActiveCfg = Release|x86
- {962A3463-9C5E-4BA2-AA40-AF7AB120CEA8}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Source/DependencyAnalyser/App.ico b/DependencyAnalyser/App.ico
similarity index 100%
rename from Source/DependencyAnalyser/App.ico
rename to DependencyAnalyser/App.ico
diff --git a/DependencyAnalyser/App.xaml b/DependencyAnalyser/App.xaml
new file mode 100644
index 0000000..993d907
--- /dev/null
+++ b/DependencyAnalyser/App.xaml
@@ -0,0 +1,5 @@
+
diff --git a/DependencyAnalyser/App.xaml.cs b/DependencyAnalyser/App.xaml.cs
new file mode 100644
index 0000000..57d3206
--- /dev/null
+++ b/DependencyAnalyser/App.xaml.cs
@@ -0,0 +1,8 @@
+using System.Windows;
+
+namespace DependencyAnalyser
+{
+ public partial class App : Application
+ {
+ }
+}
diff --git a/Source/DependencyAnalyser/AssemblyAnalyser.cs b/DependencyAnalyser/AssemblyAnalyser.cs
similarity index 97%
rename from Source/DependencyAnalyser/AssemblyAnalyser.cs
rename to DependencyAnalyser/AssemblyAnalyser.cs
index 5850247..f7af2b8 100644
--- a/Source/DependencyAnalyser/AssemblyAnalyser.cs
+++ b/DependencyAnalyser/AssemblyAnalyser.cs
@@ -23,7 +23,7 @@ public static void Analyze(Assembly assembly, DependencyGraph graph, ILo
foreach (var dependantAssemblyName in dependencies)
{
- logger.WriteLine("- {dependantAssemblyName.Name}");
+ logger.WriteLine($"- {dependantAssemblyName.Name}");
if (!graph.AddDependency(assemblyName, dependantAssemblyName.Name))
{
diff --git a/Source/DependencyAnalyser/Controls/TriStateTreeView.cs b/DependencyAnalyser/Controls/TriStateTreeView.cs
similarity index 100%
rename from Source/DependencyAnalyser/Controls/TriStateTreeView.cs
rename to DependencyAnalyser/Controls/TriStateTreeView.cs
diff --git a/Source/DependencyAnalyser/Controls/TriStateTreeView.resx b/DependencyAnalyser/Controls/TriStateTreeView.resx
similarity index 100%
rename from Source/DependencyAnalyser/Controls/TriStateTreeView.resx
rename to DependencyAnalyser/Controls/TriStateTreeView.resx
diff --git a/Source/DependencyAnalyser/DependencyAnalyser.csproj b/DependencyAnalyser/DependencyAnalyser.csproj
similarity index 66%
rename from Source/DependencyAnalyser/DependencyAnalyser.csproj
rename to DependencyAnalyser/DependencyAnalyser.csproj
index d13d37f..f163265 100644
--- a/Source/DependencyAnalyser/DependencyAnalyser.csproj
+++ b/DependencyAnalyser/DependencyAnalyser.csproj
@@ -11,29 +11,17 @@
Displays dependencies between projects/assemblies as a directed graph.
https://drewnoakes.com
Drew Noakes 2003-2021
+ true
+ true
-
- <_Parameter1>$(AssemblyName).Tests
-
-
-
- {052DB09C-95F7-43BD-B7F8-492373D1151E}
- 1
- 0
- 0
- tlbimp
- False
- False
-
-
+
-
\ No newline at end of file
diff --git a/DependencyAnalyser/DependencyAnalyzerWindow.xaml b/DependencyAnalyser/DependencyAnalyzerWindow.xaml
new file mode 100644
index 0000000..be602f1
--- /dev/null
+++ b/DependencyAnalyser/DependencyAnalyzerWindow.xaml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DependencyAnalyser/DependencyAnalyzerWindow.xaml.cs b/DependencyAnalyser/DependencyAnalyzerWindow.xaml.cs
new file mode 100644
index 0000000..ace8ce5
--- /dev/null
+++ b/DependencyAnalyser/DependencyAnalyzerWindow.xaml.cs
@@ -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 _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();
+
+ 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();
+ }
+ }
+}
diff --git a/Source/DependencyAnalyser/DependencyGraph.cs b/DependencyAnalyser/DependencyGraph.cs
similarity index 100%
rename from Source/DependencyAnalyser/DependencyGraph.cs
rename to DependencyAnalyser/DependencyGraph.cs
diff --git a/Source/DependencyAnalyser/FilterForm.Designer.cs b/DependencyAnalyser/FilterForm.Designer.cs
similarity index 100%
rename from Source/DependencyAnalyser/FilterForm.Designer.cs
rename to DependencyAnalyser/FilterForm.Designer.cs
diff --git a/Source/DependencyAnalyser/FilterForm.cs b/DependencyAnalyser/FilterForm.cs
similarity index 100%
rename from Source/DependencyAnalyser/FilterForm.cs
rename to DependencyAnalyser/FilterForm.cs
diff --git a/Source/DependencyAnalyser/FilterForm.resx b/DependencyAnalyser/FilterForm.resx
similarity index 100%
rename from Source/DependencyAnalyser/FilterForm.resx
rename to DependencyAnalyser/FilterForm.resx
diff --git a/Source/DependencyAnalyser/FilterPreferences.cs b/DependencyAnalyser/FilterPreferences.cs
similarity index 100%
rename from Source/DependencyAnalyser/FilterPreferences.cs
rename to DependencyAnalyser/FilterPreferences.cs
diff --git a/Source/DependencyAnalyser/Graphics/Filter.ico b/DependencyAnalyser/Graphics/Filter.ico
similarity index 100%
rename from Source/DependencyAnalyser/Graphics/Filter.ico
rename to DependencyAnalyser/Graphics/Filter.ico
diff --git a/Source/DependencyAnalyser/ILogger.cs b/DependencyAnalyser/ILogger.cs
similarity index 100%
rename from Source/DependencyAnalyser/ILogger.cs
rename to DependencyAnalyser/ILogger.cs
diff --git a/DependencyAnalyser/SolutionAndProjectFileAnalyser.cs b/DependencyAnalyser/SolutionAndProjectFileAnalyser.cs
new file mode 100644
index 0000000..e35041c
--- /dev/null
+++ b/DependencyAnalyser/SolutionAndProjectFileAnalyser.cs
@@ -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
+{
+ ///
+ /// Builds a DependencyGraph from a given Visual Studio solution (.sln) or project (e.g. .csproj) file.
+ ///
+ public static class SolutionAndProjectFileAnalyser
+ {
+ private static bool _isRegistered;
+
+ public static async Task AnalyseAsync(string filePath, DependencyGraph 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();
+
+ 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);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Documentation/filter-window.png b/Documentation/filter-window.png
deleted file mode 100644
index 72ed650..0000000
Binary files a/Documentation/filter-window.png and /dev/null differ
diff --git a/Documentation/four-node-graph.png b/Documentation/four-node-graph.png
deleted file mode 100644
index 29b4ad1..0000000
Binary files a/Documentation/four-node-graph.png and /dev/null differ
diff --git a/Documentation/four-node-graph.svg b/Documentation/four-node-graph.svg
deleted file mode 100644
index 222d6fa..0000000
--- a/Documentation/four-node-graph.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/Documentation/many-node-graph.png b/Documentation/many-node-graph.png
deleted file mode 100644
index 9084c05..0000000
Binary files a/Documentation/many-node-graph.png and /dev/null differ
diff --git a/Documentation/ui-filtered.png b/Documentation/ui-filtered.png
deleted file mode 100644
index 6074dad..0000000
Binary files a/Documentation/ui-filtered.png and /dev/null differ
diff --git a/Documentation/ui-unfiltered.png b/Documentation/ui-unfiltered.png
deleted file mode 100644
index 87ff2b3..0000000
Binary files a/Documentation/ui-unfiltered.png and /dev/null differ
diff --git a/Libraries/WinGraphviz_v1.02.17.msi b/Libraries/WinGraphviz_v1.02.17.msi
deleted file mode 100644
index 6d44b30..0000000
Binary files a/Libraries/WinGraphviz_v1.02.17.msi and /dev/null differ
diff --git a/Libraries/WinGraphviz_v1.02.24.msi b/Libraries/WinGraphviz_v1.02.24.msi
deleted file mode 100644
index f6c3ccd..0000000
Binary files a/Libraries/WinGraphviz_v1.02.24.msi and /dev/null differ
diff --git a/README.md b/README.md
index ac05bb3..bc935d1 100644
--- a/README.md
+++ b/README.md
@@ -4,64 +4,23 @@ After manually drawing an assembly dependency graph for a twenty-something-proje
Run the application, and open a .NET assembly or solution from the _File|Open..._ menu (or just press Ctrl+O).
-The analyser will immediately generate a diagram such as the one shown:
+Let's see how it looks if we run the application on itself:
-![Example screenshot from .NET Assembly Dependency Analyser graph](https://raw.githubusercontent.com/drewnoakes/dependency-analyser/master/Documentation/ui-unfiltered.png)
+![Example screenshot from .NET Assembly Dependency Analyser graph](img/ui-unfiltered.png)
-Most assemblies will reference large numbers of system assemblies, either directly or indirectly.
-In this graph, both the `System` and `mscorlib` assemblies are referenced by almost all other assemblies.
-The diagram is clearer without these explicit references.
+As you can see there is a lot of information here and the graph is impossible to read as-is.
+It's possible to zoom and pan, and you can drag nodes around to make things easier to see,
+but most of the time you'll want to hide assemblies you're not interested in.
-See this graph, exported as a [PNG file](https://raw.githubusercontent.com/drewnoakes/dependency-analyser/master/Documentation/many-node-graph.png).
+Using the _Filter..._ command lets us exclude items we don't want to see. Removing several
+`System.*`and other framework assemblies gives a clearer picture:
-![Example of the exclude menu showing how to omit selected assemblies from the graph](https://raw.githubusercontent.com/drewnoakes/dependency-analyser/master/Documentation/filter-window.png)
+![A graph showing dependencies when most of the behind-the-scenes assemblies have been removed](img/ui-filtered.png)
-Select which assemblies you want to include in the plot and press OK.
-
-![A graph showing dependencies when most of the behind-the-scenes assemblies have been removed](https://raw.githubusercontent.com/drewnoakes/dependency-analyser/master/Documentation/ui-filtered.png)
-
-This plot tells us a great deal about the analysed assembly (in this case, `DependencyAnalyser.Tests.dll`).
-It requires three non-system assemblies: `DependencyAnalyser`, `DependencyAnalyser.Tests` and `nunit.framework`,
-even though `nunit.framework` is not referenced directly. We can also tell, unsurprisingly, that `DependencyAnalyser`
-uses WinForms assemblies.
-
-Note the [circular dependency between `System` and `System.Xml`](https://stackoverflow.com/q/1316518/24874)!!!
-
-Excluding all `System` assemblies shows all dependent assemblies that must be deployed with the
-selected assembly for it to operate properly.
-
-![A graph showing uncluttered core dependencies when all framework and other supporting assemblies have been removed](https://raw.githubusercontent.com/drewnoakes/dependency-analyser/master/Documentation/four-node-graph.png)
-
-Here's an example of the [SVG output](https://raw.githubusercontent.com/drewnoakes/dependency-analyser/master/Documentation/four-node-graph.svg).
-
-## The graph
-
-The graph is produced via a Dot script. WinGraphViz is a COM component that generates Dot
-images, and is used by this application. It must be installed for the .NET dependency analyser
-to work.
-
-A Dot script may look something like this:
-
- digraph G {
- size="100,50"
- center=""
- ratio=All
- node[width=.25,hight=.375,fontsize=12,color=lightblue2,style=filled]
- 1 -> 3;
- 1 -> 17;
- 3 -> 15;
- 1 [label="DependencyAnalyser.Tests"];
- 3 [label="DependencyAnalyser"];
- 15 [label="Interop.WINGRAPHVIZLib"];
- 17 [label="nunit.framework"];
- }
-
-This markup can be seen in the 'Dot Output' tab.
-
-More information on Dot can be found at http://www.graphviz.org/
+You can open either assemblies or MSBuild project/solution files. The latter produces richer data,
+including target frameworks which can be useful for multi-targeting projects.
## Installation
1. Download zipped binaries on the [releases page](https://github.com/drewnoakes/dependency-analyser/releases) and extract to a folder on your PC.
-2. Run the WinGraphviz installer included in the archive (if you do not already have it installed).
-3. Run `DependencyAnalyser.exe` to start the program.
\ No newline at end of file
+2. Run `DependencyAnalyser.exe` to start the program.
\ No newline at end of file
diff --git a/Source/DependencyAnalyser.Tests/DependencyAnalyser.Tests.csproj b/Source/DependencyAnalyser.Tests/DependencyAnalyser.Tests.csproj
deleted file mode 100644
index ba89e81..0000000
--- a/Source/DependencyAnalyser.Tests/DependencyAnalyser.Tests.csproj
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
- net48
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Source/DependencyAnalyser.Tests/DependencyGraphTest.cs b/Source/DependencyAnalyser.Tests/DependencyGraphTest.cs
deleted file mode 100644
index 475d28d..0000000
--- a/Source/DependencyAnalyser.Tests/DependencyGraphTest.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using Xunit;
-
-namespace DependencyAnalyser.Tests
-{
- public sealed class DependencyGraphTest
- {
- [Fact]
- public void AddDependencyTest()
- {
- var graph = new DependencyGraph();
- graph.AddDependency("A", "B");
-
- Assert.Equal(new[] { "A", "B" }, graph.Nodes);
- Assert.Equal(new[] { "B" }, graph.GetDependenciesForNode("A"));
- }
-
- [Fact]
- public void TransitiveReduceThreeNodes()
- {
- var graph = new DependencyGraph();
- graph.AddDependency('a', 'b');
- graph.AddDependency('b', 'c');
- graph.AddDependency('a', 'c');
-
- graph.TransitiveReduce();
-
- Assert.Equal(new[] { 'a', 'b', 'c' }, graph.Nodes);
- Assert.Equal(new[] { 'b' }, graph.GetDependenciesForNode('a'));
- Assert.Equal(new[] { 'c' }, graph.GetDependenciesForNode('b'));
- }
-
- [Fact]
- public void TransitiveReduceCascade()
- {
- var graph = new DependencyGraph();
-
- for (var c1 = 'a'; c1 < 'f'; c1++)
- for (var c2 = (char)(c1 + 1); c2 < 'f'; c2++)
- graph.AddDependency(c1, c2);
-
- graph.TransitiveReduce();
-
- for (var c = 'a'; c < 'f' - 1; c++)
- Assert.Equal(new[] { (char)(c + 1) }, graph.GetDependenciesForNode(c));
- }
-
- [Fact]
- public void TransitiveReduceCycles()
- {
- var graph = new DependencyGraph();
-
- graph.AddDependency('a', 'b'); // a <--> b
- graph.AddDependency('b', 'a');
-
- graph.AddDependency('a', 'c'); // a --> c
- graph.AddDependency('b', 'c'); // b --> c
-
- graph.TransitiveReduce();
-
- // NOTE result here depends upon order of enumeration from HashSet, making this test potentially fragile
-
- Assert.Equal(new[] { 'b' }, graph.GetDependenciesForNode('a'));
- Assert.Equal(new[] { 'a', 'c' }, graph.GetDependenciesForNode('b'));
- }
- }
-}
diff --git a/Source/DependencyAnalyser.Tests/DotCommandBuilderTest.cs b/Source/DependencyAnalyser.Tests/DotCommandBuilderTest.cs
deleted file mode 100644
index 402b98b..0000000
--- a/Source/DependencyAnalyser.Tests/DotCommandBuilderTest.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using Xunit;
-
-namespace DependencyAnalyser.Tests
-{
- public sealed class DotCommandBuilderTest
- {
- [Fact]
- public void GenerateDotCommand()
- {
- var graph = new DependencyGraph();
- graph.AddDependency("A", "B");
-
- var filterPreferences = new FilterPreferences();
- filterPreferences.SetAssemblyNames(graph.Nodes);
- var command = DotCommandBuilder.Generate(graph, filterPreferences);
-
- const string expected = @"digraph G {
- 1 -> 2;
- 1 [label=""A""];
- 2 [label=""B""];
-}";
-
- Assert.Equal(expected, command);
- }
-
- [Fact]
- public void GenerateDotCommand_ExclusionList()
- {
- var graph = new DependencyGraph();
- graph.AddDependency("A", "B");
- graph.AddDependency("B", "C");
- graph.AddDependency("C", "A");
-
- var filterPreferences = new FilterPreferences();
- filterPreferences.SetAssemblyNames(graph.Nodes);
- filterPreferences.Exclude("C");
-
- var command = DotCommandBuilder.Generate(graph, filterPreferences);
-
- const string expected = @"digraph G {
- 1 -> 2;
- 1 [label=""A""];
- 2 [label=""B""];
-}";
-
- Assert.Equal(expected, command);
- }
- }
-}
diff --git a/Source/DependencyAnalyser.Tests/TemporaryFileManagerTest.cs b/Source/DependencyAnalyser.Tests/TemporaryFileManagerTest.cs
deleted file mode 100644
index b5e6d02..0000000
--- a/Source/DependencyAnalyser.Tests/TemporaryFileManagerTest.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System.IO;
-
-using Xunit;
-
-namespace DependencyAnalyser.Tests
-{
- public sealed class TemporaryFileManagerTest
- {
- [Fact]
- public void CreateTemporaryFile_Format()
- {
- var filename = TemporaryFileManager.CreateTemporaryFile();
- Assert.StartsWith("DependencyAnaylser.", Path.GetFileName(filename));
- Assert.EndsWith(".tmp", Path.GetFileName(filename));
- Assert.Equal(Path.GetTempPath(), Path.GetDirectoryName(filename) + "\\");
- }
-
- [Fact]
- public void CreateTemporaryFile_CreatesZeroByteFile()
- {
- var filename = TemporaryFileManager.CreateTemporaryFile();
- Assert.True(File.Exists(filename));
- Assert.Equal(0L, new FileInfo(filename).Length);
- }
-
- [Fact]
- public void GetExistingTemporaryFilenames()
- {
- TemporaryFileManager.DeleteAllTemporaryFiles();
- var filename1 = TemporaryFileManager.CreateTemporaryFile();
- var filename2 = TemporaryFileManager.CreateTemporaryFile();
- var filenames = TemporaryFileManager.GetExistingTemporaryFilenames();
- Assert.Equal(2, filenames.Length);
- Assert.True(filename1!=filename2);
- Assert.True(filename1==filenames[0] || filename1==filenames[1]);
- Assert.True(filename2==filenames[0] || filename2==filenames[1]);
- }
-
- [Fact]
- public void DeleteAllTemporaryFiles()
- {
- TemporaryFileManager.DeleteAllTemporaryFiles();
- TemporaryFileManager.CreateTemporaryFile();
- TemporaryFileManager.CreateTemporaryFile();
- Assert.Equal(2, TemporaryFileManager.GetExistingTemporaryFilenames().Length);
- TemporaryFileManager.DeleteAllTemporaryFiles();
- Assert.Empty(TemporaryFileManager.GetExistingTemporaryFilenames());
- }
- }
-}
diff --git a/Source/DependencyAnalyser/DependencyAnalyserForm.Designer.cs b/Source/DependencyAnalyser/DependencyAnalyserForm.Designer.cs
deleted file mode 100644
index 9dd3a76..0000000
--- a/Source/DependencyAnalyser/DependencyAnalyserForm.Designer.cs
+++ /dev/null
@@ -1,299 +0,0 @@
-using System.Windows.Forms;
-
-namespace DependencyAnalyser
-{
- partial class DependencyAnalyserForm
- {
- ///
- /// Required designer variable.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Clean up any resources being used.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing)
- {
- if (disposing && (components != null))
- {
- components.Dispose();
- }
-
- // delete up our temporary files
- TemporaryFileManager.DeleteAllTemporaryFiles();
-
- base.Dispose(disposing);
- }
-
- #region Windows Form Designer generated code
-
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent()
- {
- this.components = new System.ComponentModel.Container();
- System.Windows.Forms.MenuItem _mnuFile;
- System.Windows.Forms.MenuItem _mnuFileOpen;
- System.Windows.Forms.MenuItem _mnuFileSeparator;
- System.Windows.Forms.MenuItem _mnuFileExit;
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DependencyAnalyserForm));
- this._mnuFileMerge = new System.Windows.Forms.MenuItem();
- this._mnuFileSavePng = new System.Windows.Forms.MenuItem();
- this._mnuFileSaveSvg = new System.Windows.Forms.MenuItem();
- this._mnuFilePrint = new System.Windows.Forms.MenuItem();
- this._mnuFilePrintPreview = new System.Windows.Forms.MenuItem();
- this._mnuAbout = new System.Windows.Forms.MenuItem();
- this._openFileDialog = new System.Windows.Forms.OpenFileDialog();
- this._menu = new System.Windows.Forms.MainMenu(this.components);
- this._mnuFilter = new System.Windows.Forms.MenuItem();
- this._savePngFileDialog = new System.Windows.Forms.SaveFileDialog();
- this._saveSvgFileDialog = new System.Windows.Forms.SaveFileDialog();
- this._printDialog = new System.Windows.Forms.PrintDialog();
- this._printPreviewDialog = new System.Windows.Forms.PrintPreviewDialog();
- this._tabPageMessages = new System.Windows.Forms.TabPage();
- this._txtMessage = new System.Windows.Forms.TextBox();
- this._tabPageDotOutput = new System.Windows.Forms.TabPage();
- this._txtDotScriptOutput = new System.Windows.Forms.TextBox();
- this._tabPageImage = new System.Windows.Forms.TabPage();
- this._imgDotDiagram = new System.Windows.Forms.PictureBox();
- this._tabControl = new System.Windows.Forms.TabControl();
- _mnuFile = new System.Windows.Forms.MenuItem();
- _mnuFileOpen = new System.Windows.Forms.MenuItem();
- _mnuFileSeparator = new System.Windows.Forms.MenuItem();
- _mnuFileExit = new System.Windows.Forms.MenuItem();
- this._tabPageMessages.SuspendLayout();
- this._tabPageDotOutput.SuspendLayout();
- this._tabPageImage.SuspendLayout();
- ((System.ComponentModel.ISupportInitialize)(this._imgDotDiagram)).BeginInit();
- this._tabControl.SuspendLayout();
- this.SuspendLayout();
- //
- // _mnuFile
- //
- _mnuFile.Index = 0;
- _mnuFile.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
- _mnuFileOpen,
- this._mnuFileMerge,
- this._mnuFileSavePng,
- this._mnuFileSaveSvg,
- this._mnuFilePrint,
- this._mnuFilePrintPreview,
- _mnuFileSeparator,
- _mnuFileExit});
- _mnuFile.Text = "&File";
- //
- // _mnuFileOpen
- //
- _mnuFileOpen.Index = 0;
- _mnuFileOpen.Shortcut = System.Windows.Forms.Shortcut.CtrlO;
- _mnuFileOpen.Text = "&Open...";
- _mnuFileOpen.Click += new System.EventHandler(this.menuFileOpen_Click);
- //
- // _mnuFileMerge
- //
- this._mnuFileMerge.Index = 1;
- this._mnuFileMerge.Shortcut = System.Windows.Forms.Shortcut.CtrlM;
- this._mnuFileMerge.Text = "Merge...";
- this._mnuFileMerge.Click += new System.EventHandler(this.menuFileMerge_Click);
- //
- // _mnuFileSavePng
- //
- this._mnuFileSavePng.Index = 2;
- this._mnuFileSavePng.Shortcut = System.Windows.Forms.Shortcut.CtrlS;
- this._mnuFileSavePng.Text = "Save &PNG...";
- this._mnuFileSavePng.Click += new System.EventHandler(this.menuFileSavePng_Click);
- //
- // _mnuFileSaveSvg
- //
- this._mnuFileSaveSvg.Index = 3;
- this._mnuFileSaveSvg.Text = "Save &SVG...";
- this._mnuFileSaveSvg.Click += new System.EventHandler(this.menuFileSaveSvg_Click);
- //
- // _mnuFilePrint
- //
- this._mnuFilePrint.Index = 4;
- this._mnuFilePrint.Text = "Print...";
- this._mnuFilePrint.Click += new System.EventHandler(this.menuFilePrint_Click);
- //
- // _mnuFilePrintPreview
- //
- this._mnuFilePrintPreview.Index = 5;
- this._mnuFilePrintPreview.Text = "Print preview...";
- this._mnuFilePrintPreview.Click += new System.EventHandler(this.menuFilePrintPreview_Click);
- //
- // _mnuFileSeparator
- //
- _mnuFileSeparator.Index = 6;
- _mnuFileSeparator.Text = "-";
- //
- // _mnuFileExit
- //
- _mnuFileExit.Index = 7;
- _mnuFileExit.Text = "E&xit";
- _mnuFileExit.Click += new System.EventHandler(this.menuFileExit_Click);
- //
- // _mnuAbout
- //
- this._mnuAbout.Index = 2;
- this._mnuAbout.Text = "About...";
- this._mnuAbout.Click += new System.EventHandler(this.mnuAbout_Click);
- //
- // _openFileDialog
- //
- this._openFileDialog.Filter = "All supported files|*.sln;*.exe;*.dll|Solution files (*.sln)|*.sln|Assemblies (*." +
- "dll)|*.dll|Assemblies (*.exe)|*.exe|All files|*.exe";
- this._openFileDialog.Title = "Open File";
- //
- // _menu
- //
- this._menu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
- _mnuFile,
- this._mnuFilter,
- this._mnuAbout});
- //
- // _mnuFilter
- //
- this._mnuFilter.Index = 1;
- this._mnuFilter.Text = "F&ilter...";
- this._mnuFilter.Click += new System.EventHandler(this.menuFilter_Click);
- //
- // _savePngFileDialog
- //
- this._savePngFileDialog.DefaultExt = "png";
- this._savePngFileDialog.FileName = "DependencyGraph.png";
- this._savePngFileDialog.Filter = "PNG Images|*.png|All files|*.*";
- //
- // _saveSvgFileDialog
- //
- this._saveSvgFileDialog.FileName = "DependencyGraph.svg";
- this._saveSvgFileDialog.Filter = "SVG files (*.svg)|*.svg|All files|*.*";
- //
- // _printPreviewDialog
- //
- this._printPreviewDialog.AutoScrollMargin = new System.Drawing.Size(0, 0);
- this._printPreviewDialog.AutoScrollMinSize = new System.Drawing.Size(0, 0);
- this._printPreviewDialog.ClientSize = new System.Drawing.Size(400, 300);
- this._printPreviewDialog.Enabled = true;
- this._printPreviewDialog.Icon = ((System.Drawing.Icon)(resources.GetObject("_printPreviewDialog.Icon")));
- this._printPreviewDialog.Name = "_printPreviewDialog";
- this._printPreviewDialog.Visible = false;
- //
- // _tabPageMessages
- //
- this._tabPageMessages.Controls.Add(this._txtMessage);
- this._tabPageMessages.Location = new System.Drawing.Point(4, 4);
- this._tabPageMessages.Name = "_tabPageMessages";
- this._tabPageMessages.Size = new System.Drawing.Size(680, 343);
- this._tabPageMessages.TabIndex = 2;
- this._tabPageMessages.Text = "Messages";
- //
- // _txtMessage
- //
- this._txtMessage.Dock = System.Windows.Forms.DockStyle.Fill;
- this._txtMessage.Location = new System.Drawing.Point(0, 0);
- this._txtMessage.Multiline = true;
- this._txtMessage.Name = "_txtMessage";
- this._txtMessage.ScrollBars = System.Windows.Forms.ScrollBars.Both;
- this._txtMessage.Size = new System.Drawing.Size(680, 343);
- this._txtMessage.TabIndex = 0;
- //
- // _tabPageDotOutput
- //
- this._tabPageDotOutput.Controls.Add(this._txtDotScriptOutput);
- this._tabPageDotOutput.Location = new System.Drawing.Point(4, 4);
- this._tabPageDotOutput.Name = "_tabPageDotOutput";
- this._tabPageDotOutput.Size = new System.Drawing.Size(680, 343);
- this._tabPageDotOutput.TabIndex = 0;
- this._tabPageDotOutput.Text = "Dot Output";
- //
- // _txtDotScriptOutput
- //
- this._txtDotScriptOutput.Dock = System.Windows.Forms.DockStyle.Fill;
- this._txtDotScriptOutput.Location = new System.Drawing.Point(0, 0);
- this._txtDotScriptOutput.Multiline = true;
- this._txtDotScriptOutput.Name = "_txtDotScriptOutput";
- this._txtDotScriptOutput.ScrollBars = System.Windows.Forms.ScrollBars.Both;
- this._txtDotScriptOutput.Size = new System.Drawing.Size(680, 343);
- this._txtDotScriptOutput.TabIndex = 0;
- //
- // _tabPageImage
- //
- this._tabPageImage.Controls.Add(this._imgDotDiagram);
- this._tabPageImage.Location = new System.Drawing.Point(4, 4);
- this._tabPageImage.Name = "_tabPageImage";
- this._tabPageImage.Size = new System.Drawing.Size(680, 343);
- this._tabPageImage.TabIndex = 1;
- this._tabPageImage.Text = "Image";
- //
- // _imgDotDiagram
- //
- this._imgDotDiagram.Dock = System.Windows.Forms.DockStyle.Fill;
- this._imgDotDiagram.Location = new System.Drawing.Point(0, 0);
- this._imgDotDiagram.Name = "_imgDotDiagram";
- this._imgDotDiagram.Size = new System.Drawing.Size(680, 343);
- this._imgDotDiagram.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
- this._imgDotDiagram.TabIndex = 0;
- this._imgDotDiagram.TabStop = false;
- //
- // _tabControl
- //
- this._tabControl.Alignment = System.Windows.Forms.TabAlignment.Bottom;
- this._tabControl.Controls.Add(this._tabPageImage);
- this._tabControl.Controls.Add(this._tabPageDotOutput);
- this._tabControl.Controls.Add(this._tabPageMessages);
- this._tabControl.Dock = System.Windows.Forms.DockStyle.Fill;
- this._tabControl.Location = new System.Drawing.Point(0, 0);
- this._tabControl.Name = "_tabControl";
- this._tabControl.SelectedIndex = 0;
- this._tabControl.Size = new System.Drawing.Size(688, 369);
- this._tabControl.TabIndex = 0;
- //
- // DependencyAnalyserForm
- //
- this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
- this.ClientSize = new System.Drawing.Size(688, 369);
- this.Controls.Add(this._tabControl);
- this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
- this.Menu = this._menu;
- this.Name = "DependencyAnalyserForm";
- this.Text = ".NET Assembly Dependency Analyser";
- this._tabPageMessages.ResumeLayout(false);
- this._tabPageMessages.PerformLayout();
- this._tabPageDotOutput.ResumeLayout(false);
- this._tabPageDotOutput.PerformLayout();
- this._tabPageImage.ResumeLayout(false);
- ((System.ComponentModel.ISupportInitialize)(this._imgDotDiagram)).EndInit();
- this._tabControl.ResumeLayout(false);
- this.ResumeLayout(false);
-
- }
-
- private OpenFileDialog _openFileDialog;
- private SaveFileDialog _savePngFileDialog;
- private SaveFileDialog _saveSvgFileDialog;
- private PrintDialog _printDialog;
- private PrintPreviewDialog _printPreviewDialog;
-
- private MainMenu _menu;
- private MenuItem _mnuAbout;
- private MenuItem _mnuFileSavePng;
- private MenuItem _mnuFileSaveSvg;
- private MenuItem _mnuFilePrint;
- private MenuItem _mnuFilePrintPreview;
- private MenuItem _mnuFileMerge;
- private MenuItem _mnuFilter;
- private TabPage _tabPageMessages;
- private TextBox _txtMessage;
- private TabPage _tabPageDotOutput;
- private TextBox _txtDotScriptOutput;
- private TabPage _tabPageImage;
- private PictureBox _imgDotDiagram;
- private TabControl _tabControl;
-
- #endregion
-
- }
-}
\ No newline at end of file
diff --git a/Source/DependencyAnalyser/DependencyAnalyserForm.cs b/Source/DependencyAnalyser/DependencyAnalyserForm.cs
deleted file mode 100644
index e54af37..0000000
--- a/Source/DependencyAnalyser/DependencyAnalyserForm.cs
+++ /dev/null
@@ -1,232 +0,0 @@
-using System;
-using System.Drawing.Imaging;
-using System.Drawing.Printing;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-
-namespace DependencyAnalyser
-{
- public partial class DependencyAnalyserForm : Form
- {
- private readonly FilterPreferences _filterPreferences = new();
- private readonly StringBuilderLogger _logger = new();
-
- private DependencyGraph _graph = new();
- private PlotResult? _plotResult;
-
- public DependencyAnalyserForm()
- {
- InitializeComponent();
- EnableAndDisableMenuItems();
- }
-
- private void EnableAndDisableMenuItems()
- {
- var hasGraph = _graph.Nodes.Any();
-
- _mnuFileSavePng.Enabled = hasGraph;
- _mnuFileSaveSvg.Enabled = hasGraph;
- _mnuFilter.Enabled = hasGraph;
- _mnuFilePrint.Enabled = hasGraph;
- _mnuFilePrintPreview.Enabled = hasGraph;
- _mnuFileMerge.Enabled = hasGraph;
- }
-
- #region Event handlers
-
- private async void menuFileOpen_Click(object sender, EventArgs e)
- {
- // Start a new graph
- _graph = new DependencyGraph();
-
- await MergeFileAsync();
- }
-
- private async void menuFileMerge_Click(object sender, EventArgs e)
- {
- await MergeFileAsync();
- }
-
- private async Task MergeFileAsync()
- {
- try
- {
- if (_openFileDialog.ShowDialog() != DialogResult.OK)
- {
- return;
- }
-
- string fileName = _openFileDialog.FileName;
-
- using (WaitCursor())
- {
- if (fileName.EndsWith(".sln"))
- {
- await SolutionFileAnalyser.AnalyseAsync(fileName, _graph, _logger);
- }
- else
- {
- var assembly = Assembly.LoadFrom(fileName);
- AssemblyAnalyser.Analyze(assembly, _graph, _logger);
- }
-
- _filterPreferences.SetAssemblyNames(_graph.Nodes);
- EnableAndDisableMenuItems();
- UpdateImage();
- }
- }
- catch (Exception e)
- {
- MessageBox.Show(e.ToString());
- }
- }
-
- private void menuFileSavePng_Click(object sender, EventArgs e)
- {
- if (_savePngFileDialog.ShowDialog() != DialogResult.OK)
- return;
-
- using (WaitCursor())
- using (var fileStream = (FileStream)_savePngFileDialog.OpenFile())
- SavePngImage(fileStream);
- }
-
- private void menuFileExit_Click(object sender, EventArgs e)
- {
- Application.Exit();
- }
-
- private void menuFileSaveSvg_Click(object sender, EventArgs e)
- {
- if (_saveSvgFileDialog.ShowDialog() != DialogResult.OK)
- return;
-
- using (var fileStream = (FileStream)_saveSvgFileDialog.OpenFile())
- SaveSvgImage(fileStream);
- }
-
- private void mnuAbout_Click(object sender, EventArgs 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 menuFilter_Click(object sender, EventArgs e)
- {
- using (WaitCursor())
- {
- var filterForm = new FilterForm(_filterPreferences);
-
- if (filterForm.ShowDialog() == DialogResult.OK)
- UpdateImage();
- }
- }
-
- #endregion
-
- protected override void OnFormClosed(FormClosedEventArgs e)
- {
- base.OnFormClosed(e);
-
- TemporaryFileManager.DeleteAllTemporaryFiles();
- }
-
- #region Image handling
-
- private void SavePngImage(Stream fileStream)
- {
- _imgDotDiagram.Image.Save(fileStream, ImageFormat.Png);
- MessageBox.Show("PNG file saved.");
- }
-
- private void SaveSvgImage(Stream fileStream)
- {
- if (_plotResult?.SvgXml == null)
- throw new Exception("Unable to save SVG image.");
-
- using (var writer = new StreamWriter(fileStream))
- writer.Write(_plotResult.SvgXml);
- MessageBox.Show("SVG file saved.");
- }
-
- private void UpdateImage()
- {
- var aspectRatio = _imgDotDiagram.Width / (double)_imgDotDiagram.Height;
-
- _plotResult = DependencyPlotter.CalculatePlot(aspectRatio, _graph, _filterPreferences);
-
- _txtMessage.Text = _logger.ToString();
- _txtDotScriptOutput.Text = _plotResult.DotCommand;
- _imgDotDiagram.Image = _plotResult.Image;
-
- _imgDotDiagram.Invalidate();
- }
-
- #endregion
-
- #region GUI support and feedback to user
-
- private IDisposable WaitCursor()
- {
- var reverter = new CursorReverter(Cursor, this);
- Cursor = Cursors.WaitCursor;
- return reverter;
- }
-
- private sealed class CursorReverter : IDisposable
- {
- private readonly Cursor _cursor;
- private readonly Form _form;
-
- public CursorReverter(Cursor cursor, Form form)
- {
- _cursor = cursor;
- _form = form;
- }
-
- public void Dispose()
- {
- _form.Cursor = _cursor;
- }
- }
-
- #endregion
-
- #region Printing
-
- private void menuFilePrint_Click(object sender, EventArgs e)
- {
- var printDocument = CreatePrintDocument();
-
- _printDialog.Document = printDocument;
-
- if (_printDialog.ShowDialog() != DialogResult.OK)
- return;
-
- using (WaitCursor())
- printDocument.Print();
- }
-
- private void menuFilePrintPreview_Click(object sender, EventArgs e)
- {
- using (WaitCursor())
- {
- _printPreviewDialog.Document = CreatePrintDocument();
- _printPreviewDialog.ShowDialog();
- }
- }
-
- private PrintDocument CreatePrintDocument()
- {
- if (_plotResult?.Image == null)
- throw new Exception("unable to create print document when dot image is null");
-
- return new DependencyGraphPrintDocument(_plotResult.Image);
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/Source/DependencyAnalyser/DependencyAnalyserForm.resx b/Source/DependencyAnalyser/DependencyAnalyserForm.resx
deleted file mode 100644
index 0e8b70e..0000000
--- a/Source/DependencyAnalyser/DependencyAnalyserForm.resx
+++ /dev/null
@@ -1,348 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- False
-
-
- False
-
-
- False
-
-
- False
-
-
- 17, 17
-
-
- 786, 17
-
-
- 895, 17
-
-
- 17, 54
-
-
- 157, 54
-
-
- 642, 17
-
-
-
-
- AAABAAYAICAQAAAAAADoAgAAZgAAABAQEAAAAAAAKAEAAE4DAAAgIAAAAQAIAKgIAAB2BAAAEBAAAAEA
- CABoBQAAHg0AACAgAAABACAAqBAAAIYSAAAQEAAAAQAgAGgEAAAuIwAAKAAAACAAAABAAAAAAQAEAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA
- /wAA/wAAAP//AP8AAAD/AP8A//8AAP///wAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIoiI
- iIiIiIiIiIiIiIiIiIiCIigiIiIozMzMzMzMyCIogiIoIiIiKM7m5ubm5sgiKIIiKCIiIijObm5ubm7I
- IiiCIigiIiIozubm5ubmyCIogiIoIiIiKM5ubm5ubsgiKIIiKCIiIijO5ubm5ubIIiiIiIiIiIiIzm5u
- bm5uyCIogRERERERGM7u7u7u7sgiKIHZWVlZWRjMzMzMzMzIIiiB1ZWVlZUYiIiIiIiIiIiIgdlZWVlZ
- GDMzMzMzMzMzOIHVlZWVlRg/uLi4uLi4uDiB2VlZWVkYP7uLi4uLi4s4gdWVlZWVGD+4uLi4uLi4OIHZ
- WVlZWRg/u4uLi4uLiziB1ZWVlZUYP7i4uLi4uLg4gdlZWVlZGD+7i4uLi4uLOIHVlZWVlRg/uLi4uLi4
- uDiB3d3d3d0YP7uLi4uLi4s4gRERERERGD+4uLi4uLi4OIiIiIiIiIg/u4uLi4uLiziCIiIiIiIoP7i4
- uLi4uLg4giIiIiIiKD+7i4uLi4uLOIIiIiIiIig/uLi4uLi4uDiCIiIiIiIoP7u7u7u7u7s4giIiIiIi
- KD//////////OIIiIiIiIigzMzMzMzMzMziIiIiIiIiIiIiIiIiIiIiIIiIiIiIiIiIiIiIiIiIiIv//
- ////////AAAAAHv4AA57+AAOe/gADnv4AA57+AAOe/gADgAAAA4AAAAOAAAADgAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/4AAB/+AAAf/gAAH/4AAB/+AAAf/gAAAAA
- AAD/////KAAAABAAAAAgAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAACA
- gACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP///wAiIiIiIiIiIoiI
- iIiIiIiIgigijMzMyCiCKCKM5mbIKIiIiIzu7sgogRERjMzMyCiB2ZGIiIiIiIHZkYMzMzM4gdmRg/u7
- uziB3dGD+7u7OIEREYP7u7s4iIiIg/u7uziCIiKD+7u7OIIiIoP///84giIigzMzMziIiIiIiIiIiP//
- KCIAACjObALm5mwCIigAAoiIAAKIzgAAbm4AACIoAAAREQAAGM4AAO7uAAAiKHwAWVl8ABjMfADMzAAA
- IigoAAAAIAAAAEAAAAABAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAgAAAAICAAIAA
- AACAAIAAgIAAAICAgADA3MAA8MqmAKo/KgD/PyoAAF8qAFVfKgCqXyoA/18qAAB/KgBVfyoAqn8qAP9/
- KgAAnyoAVZ8qAKqfKgD/nyoAAL8qAFW/KgCqvyoA/78qAADfKgBV3yoAqt8qAP/fKgAA/yoAVf8qAKr/
- KgD//yoAAABVAFUAVQCqAFUA/wBVAAAfVQBVH1UAqh9VAP8fVQAAP1UAVT9VAKo/VQD/P1UAAF9VAFVf
- VQCqX1UA/19VAAB/VQBVf1UAqn9VAP9/VQAAn1UAVZ9VAKqfVQD/n1UAAL9VAFW/VQCqv1UA/79VAADf
- VQBV31UAqt9VAP/fVQAA/1UAVf9VAKr/VQD//1UAAAB/AFUAfwCqAH8A/wB/AAAffwBVH38Aqh9/AP8f
- fwAAP38AVT9/AKo/fwD/P38AAF9/AFVffwCqX38A/19/AAB/fwBVf38Aqn9/AP9/fwAAn38AVZ9/AKqf
- fwD/n38AAL9/AFW/fwCqv38A/79/AADffwBV338Aqt9/AP/ffwAA/38AVf9/AKr/fwD//38AAACqAFUA
- qgCqAKoA/wCqAAAfqgBVH6oAqh+qAP8fqgAAP6oAVT+qAKo/qgD/P6oAAF+qAFVfqgCqX6oA/1+qAAB/
- qgBVf6oAqn+qAP9/qgAAn6oAVZ+qAKqfqgD/n6oAAL+qAFW/qgCqv6oA/7+qAADfqgBV36oAqt+qAP/f
- qgAA/6oAVf+qAKr/qgD//6oAAADUAFUA1ACqANQA/wDUAAAf1ABVH9QAqh/UAP8f1AAAP9QAVT/UAKo/
- 1AD/P9QAAF/UAFVf1ACqX9QA/1/UAAB/1ABVf9QAqn/UAP9/1AAAn9QAVZ/UAKqf1AD/n9QAAL/UAFW/
- 1ACqv9QA/7/UAADf1ABV39QAqt/UAP/f1AAA/9QAVf/UAKr/1AD//9QAVQD/AKoA/wAAH/8AVR//AKof
- /wD/H/8AAD//AFU//wCqP/8A/z//AABf/wBVX/8Aql//AP9f/wAAf/8AVX//AKp//wD/f/8AAJ//AFWf
- /wCqn/8A/5//AAC//wBVv/8Aqr//AP+//wAA3/8AVd//AKrf/wD/3/8AVf//AKr//wD/zMwA/8z/AP//
- MwD//2YA//+ZAP//zAAAfwAAVX8AAKp/AAD/fwAAAJ8AAFWfAACqnwAA/58AAAC/AABVvwAAqr8AAP+/
- AAAA3wAAVd8AAKrfAAD/3wAAVf8AAKr/AAAAACoAVQAqAKoAKgD/ACoAAB8qAFUfKgCqHyoA/x8qAAA/
- KgBVPyoA8Pv/AKSgoACAgIAAAAD/AAD/AAAA//8A/wAAAAAAAAD//wAA////AP39/f39/f39/f39/f39
- /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39
- /f39/f39/f39/f39/f39/f39/f39/f39qoYIqoYIhqoIqgiqCaoIqgiqhqqGhoYIhoYIqv39/f0I/f39
- /ar9/f39/YY2Ng4yDg4ODgoOCgoKCgqG/f39/Yb9/f39CP39/f39qjY7Ozs3Nzc3NjMSMjIOCqr9/f39
- qv39/f2G/f39/f0IN19fOzs3Nzc3NjcODg4KCP39/f0I/f39/ar9/f39/ao6X19fXzs7Ozc3NzY3NgqG
- /f39/Yb9/f39CP39/f39hl9jY19jX187Ozs7Nzc3Dqr9/f39qv39/f2G/f39/f0IOodjh19jX19fXztf
- OzcOCP39/f0ICAmqCAiqCKoICapfCYdjh2ODY19fXzs7Ow6q/f39/QhITEwoSCUoKSQoqmMJCYcJCWNj
- Y2NfY19fNgj9/f39qkyZmZmYmJRwlCmqX19fXl9fX186WzY3Njc2gv39/f0JcJ2dmZmZlJmUJAmqCaoJ
- hggIqggICKoIqggI/f39/YZwnp2dnZmZmJVMqnx8fHx8fFR8VHhUVFRUVKr9/f39CHChoZ2dnZ2ZmUwJ
- fKSkxqSkxqSkpKSkpKBUCP39/f2qcKLDoqGdnZ2ZTKp8ysakxqSkxqSkxqSkpFSq/f39/QiUpqbDoqHE
- nZ1Mq3ykqMakyqSkxqSkpKSkVAj9/f39hpTIyKbHoqGhoXAIfMrLpMqkxqSkxqTGpKRUqv39/f0IlMym
- yKbIpcShcAh8y6jKpMqkxsqkpKSkxlQI/f39/aqUzMzMyKbIpqJwqnzLy8qpxsqkpMakxqSkeAj9/f39
- CJSUlJSUlJSUlJQJgMupy8qpysqkyqSkxqRUqv39/f2GCKoIqgiqCKoIhgigrcvPqcuoy8qkxsqkxnyG
- /f39/ar9/f39/f39/f39qnzPz6nLy8uoyqnKpKTKVAj9/f39CP39/f39/f39/f0IfNDPz8+py8upyqjG
- yqR8hv39/f2G/f39/f39/f39/Qik0K7P0M+ty8vLy6jKpXyq/f39/ar9/f39/f39/f39CHzQ09Ctz8/P
- qcupy6jKeAj9/f39CP39/f39/f39/f2qoNPQ0NPQ0M/Qz8vLy6l8CP39/f2G/f39/f39/f39/QmkfKR8
- oHx8fHx8fHx8fHyG/f39/aoIqgiqCKoIqgiqCKoIqgiqCKoIqgiqCKoIqgj9/f39/f39/f39/f39/f39
- /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3/////////////
- ///AAAAD3vgAA974AAPe+AAD3vgAA974AAPe+AADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AA
- AAPAAAADwAAAA8AAAAPAAAADwAAAA9/4AAPf+AAD3/gAA9/4AAPf+AAD3/gAA8AAAAP//////////ygA
- AAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACAAAAAgIAAgAAAAIAA
- gACAgAAAgICAAMDcwADwyqYAqj8qAP8/KgAAXyoAVV8qAKpfKgD/XyoAAH8qAFV/KgCqfyoA/38qAACf
- KgBVnyoAqp8qAP+fKgAAvyoAVb8qAKq/KgD/vyoAAN8qAFXfKgCq3yoA/98qAAD/KgBV/yoAqv8qAP//
- KgAAAFUAVQBVAKoAVQD/AFUAAB9VAFUfVQCqH1UA/x9VAAA/VQBVP1UAqj9VAP8/VQAAX1UAVV9VAKpf
- VQD/X1UAAH9VAFV/VQCqf1UA/39VAACfVQBVn1UAqp9VAP+fVQAAv1UAVb9VAKq/VQD/v1UAAN9VAFXf
- VQCq31UA/99VAAD/VQBV/1UAqv9VAP//VQAAAH8AVQB/AKoAfwD/AH8AAB9/AFUffwCqH38A/x9/AAA/
- fwBVP38Aqj9/AP8/fwAAX38AVV9/AKpffwD/X38AAH9/AFV/fwCqf38A/39/AACffwBVn38Aqp9/AP+f
- fwAAv38AVb9/AKq/fwD/v38AAN9/AFXffwCq338A/99/AAD/fwBV/38Aqv9/AP//fwAAAKoAVQCqAKoA
- qgD/AKoAAB+qAFUfqgCqH6oA/x+qAAA/qgBVP6oAqj+qAP8/qgAAX6oAVV+qAKpfqgD/X6oAAH+qAFV/
- qgCqf6oA/3+qAACfqgBVn6oAqp+qAP+fqgAAv6oAVb+qAKq/qgD/v6oAAN+qAFXfqgCq36oA/9+qAAD/
- qgBV/6oAqv+qAP//qgAAANQAVQDUAKoA1AD/ANQAAB/UAFUf1ACqH9QA/x/UAAA/1ABVP9QAqj/UAP8/
- 1AAAX9QAVV/UAKpf1AD/X9QAAH/UAFV/1ACqf9QA/3/UAACf1ABVn9QAqp/UAP+f1AAAv9QAVb/UAKq/
- 1AD/v9QAAN/UAFXf1ACq39QA/9/UAAD/1ABV/9QAqv/UAP//1ABVAP8AqgD/AAAf/wBVH/8Aqh//AP8f
- /wAAP/8AVT//AKo//wD/P/8AAF//AFVf/wCqX/8A/1//AAB//wBVf/8Aqn//AP9//wAAn/8AVZ//AKqf
- /wD/n/8AAL//AFW//wCqv/8A/7//AADf/wBV3/8Aqt//AP/f/wBV//8Aqv//AP/MzAD/zP8A//8zAP//
- ZgD//5kA///MAAB/AABVfwAAqn8AAP9/AAAAnwAAVZ8AAKqfAAD/nwAAAL8AAFW/AACqvwAA/78AAADf
- AABV3wAAqt8AAP/fAABV/wAAqv8AAAAAKgBVACoAqgAqAP8AKgAAHyoAVR8qAKofKgD/HyoAAD8qAFU/
- KgDw+/8ApKCgAICAgAAAAP8AAP8AAAD//wD/AAAAAAAAAP//AAD///8A/f39/f39/f39/f39/f39/f0I
- hgiqCKoICKoICKaGCP39qv39hv2GNg4ODjII/ar9/Yb9/ar9qjdjXzsOCP2G/f0IhquGCAleCWNfNob9
- qv39qkxMTEgIX19fX18I/Qj9/QhwnZlMqoYIqggIqgiG/f2qcKadcAl8fFQDVFQDqv39CHDMpnCqfMvL
- ysrKVAj9/QiUlHBwCYDPy8/LylSG/f2GqoYIqgig0M/Py8t8qv39CP39/f2GpNDQ0M/PfAn9/ar9/f39
- qqT20NDQ0Hyq/f2G/f39/QmkpKSloKR8CP39CKoIhgiqCIYIqgiGCKr9/f39/f39/f39/f39/f39/f//
- hv2AAf0ItAX9/bQFX2OABWNfgAU7O4ABNzeAAf39gAGq/YAB/YaAAf39vAE6h7wBX2O8AV9fgAE7N///
- /f0oAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAADCv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/
- wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/
- wf/Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/wAAAAAAAAAAAAAAAAAA
- AAAAAAAAwr/B/7Z3Sf+zckT/rm0//6toO/+nYjb/pF4y/6BZLv+dVCr/mlEn/5dNI/+VSiH/kkce/5FE
- HP+RRBz/kUUb/8K/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/wAAAAAAAAAAAAAAAAAAAADCv8H/AAAAAAAA
- AAAAAAAAAAAAAAAAAADCv8H/v4JS//+aZv//lWD/+5Bc//WLV//uh1P/54FO/997S//Wdkb/zXBD/8Vr
- QP+9Zj3/tGI5/65dN/+RRRz/wr/B/wAAAAAAAAAAAAAAAAAAAADCv8H/AAAAAAAAAAAAAAAAAAAAAMK/
- wf8AAAAAAAAAAAAAAAAAAAAAAAAAAMK/wf/GjFv//6Rz//+fbf//m2f//5Zh//yRXf/3jVj/8IhV/+mD
- UP/hfUz/2HhI/9ByRP/HbED/v2c9/5VJIf/Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf8AAAAAAAAAAAAA
- AAAAAAAAwr/B/wAAAAAAAAAAAAAAAAAAAAAAAAAAwr/B/86WZP//r4L//6p7//+mdf//oW7//5xo//+X
- Yv/9kl7/+I5a//KJVf/rhFH/4n5N/9t4SP/Sc0X/mlEm/8K/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/wAA
- AAAAAAAAAAAAAAAAAADCv8H/AAAAAAAAAAAAAAAAAAAAAAAAAADCv8H/1J9s//+4kf//tIv//6+E//+r
- ff//p3f//6Jw//+eav//mWT//pRf//qQWv/0i1b/7IVS/+V/Tv+gWC7/wr/B/wAAAAAAAAAAAAAAAAAA
- AADCv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf8AAAAAAAAAAAAAAAAAAAAAAAAAAMK/wf/apnP//7+d//+7
- mP//uJL//7WM//+whv//rH///6d4//+jcf//n2v//5ll//+VYP/6kVv/9YxY/6diN//Cv8H/AAAAAAAA
- AAAAAAAAAAAAAMK/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/96t
- eP//wqL//8Gi//+/nv//vJn//7mT//+2jv//sYj//66A//+pev//pHP//6Bt//+bZ///l2L/r20//8K/
- wf8AAAAAAAAAAAAAAAAAAAAAwr/B/xYXev8XF3b/GBVx/xkUbf8ZFGr/GhNm/xoSY/8bEV//HBFd/xwQ
- W//Cv8H/4K96///Cov//wqL//8Ki///Cov//wJ///72b//+6lf//t4///7KJ//+ugv//qnv//6V0//+h
- bv+3d0n/wr/B/wAAAAAAAAAAAAAAAAAAAADCv8H/FRqE/0dN1v8/RNL/Nz3Q/y40zv8nLcz/ISfK/xwh
- yf8WHMf/GxJh/8K/wf/gr3r/4K96/+Cvev/gr3r/3614/9yqdf/apnL/16Nw/9Sea//Rmmj/zZZk/8qR
- X//GjFz/w4dW/7+CUv/Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf8SHZD/WF3a/05U1/9FS9X/PUPS/zU7
- 0P8uM83/JyzL/yAmyf8aFGn/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/
- wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/xAfnP9obt7/YGTc/1Zb
- 2f9NU9f/RUrU/ztB0v80OdD/LDHO/xgWcv/Cv8H/Dn+n/w18pP8MeqH/DHie/wt1m/8Kc5j/CXGV/wlv
- k/8JbJD/CGqN/wdpi/8HZ4j/BmWH/wZkhf8GYoP/wr/B/wAAAAAAAAAAAAAAAAAAAADCv8H/DiKp/3l+
- 4/9vdeH/Zmze/11i2/9UWtn/S1HW/0NI1P86P9H/Fhh9/8K/wf8Ogar/Barp/wGo6P8Apef/AKPm/wCi
- 5P8An+L/AJ7h/wCd3/8AnN7/AJnc/wCY2/8AmNn/AJbX/wZjhP/Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/
- wf8MJbX/iI7n/4CF5v93fOP/bnPg/2Vr3f9bYdv/UljY/0lP1v8UGoj/wr/B/w+Erf8Lrur/Bqvq/wOo
- 6f8Apuf/AKTm/wCi5f8AoOT/AJ/i/wCd4f8AnN//AJrd/wCZ2/8AmNr/BmWH/8K/wf8AAAAAAAAAAAAA
- AAAAAAAAwr/B/wkowP+WnOz/jpTq/4aL6P9+hOX/dXri/2xx4P9jaN3/WV/b/xEek//Cv8H/EIaw/xay
- 7P8Or+z/Cavr/wWq6v8Bp+j/AKbn/wCj5f8AoeT/AJ/j/wCe4f8AnOD/AJve/wCa3f8HZ4n/wr/B/wAA
- AAAAAAAAAAAAAAAAAADCv8H/CCrK/6Ko7/+coe7/lZrr/42T6f+Fiub/fIHl/3N54v9rcN//ECGg/8K/
- wf8QiLP/I7nu/xq07f8Ssez/C63r/war6v8Cqen/AKbo/wCk5v8AouX/AKHk/wCf4f8AneH/AJzf/who
- i//Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf8GLNP/q7Hy/6as8P+hpu//mp/u/5OY6/+LkOj/g4nm/3qA
- 5P8NI6z/wr/B/xCKtv8xvvD/J7rv/x627f8Vsuz/Dq/s/wmr6/8Equn/Aafo/wCl5/8Ao+X/AKHk/wCf
- 4v8AnuH/CGqO/8K/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/wUu2/+vtPP/r7Tz/6qv8v+mq/D/oKXv/5me
- 7f+Sl+v/io/p/wsmt//Cv8H/Eo24/0HF8f82wfD/LLzv/yK47v8atO3/EbHs/wut6/8Gq+r/A6np/wCm
- 6P8Apeb/AKLl/wCh5P8IbJD/wr/B/wAAAAAAAAAAAAAAAAAAAADCv8H/BC/h/wQv3/8FL9z/BS3Z/wYt
- 1v8GLNL/ByvP/wgqy/8IKcb/CSnC/8K/wf8Sjrv/Uszy/0fH8f87w/H/Mb7v/ye67/8et+7/FbPt/w6v
- 6/8IrOv/BKnp/wGo6P8Apef/AKPl/wluk//Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf/Cv8H/wr/B/8K/
- wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/xKRvf9j0/P/WM/z/0zK8f9BxfH/N8Hw/yy8
- 7/8iuO7/GbTt/xGx7P8Lruv/Bqrq/wOo6f8Apuf/CnGV/8K/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/wAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCv8H/E5LA/3Ta8/9q1fP/XtHz/1LM
- 8v9Hx/H/O8Pw/zG+7/8nu+//Hrbt/xay7f8Or+v/CKzq/wSq6f8Kc5j/wr/B/wAAAAAAAAAAAAAAAAAA
- AADCv8H/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMK/wf8UlMH/hOD1/3rc
- 9f9v2PP/ZNTy/1jO8v9NyvH/Qsbx/zbB8P8svO//I7ju/xm07f8SsOz/C67r/wt2m//Cv8H/AAAAAAAA
- AAAAAAAAAAAAAMK/wf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwr/B/xSW
- w/+T5vb/iuL1/3/e9P912vT/adbz/13R8/9SzPL/R8jx/zzD8P8xvvD/J7rv/x627v8Vsuz/C3ie/8K/
- wf8AAAAAAAAAAAAAAAAAAAAAwr/B/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AADCv8H/FJbG/57r9/+X6Pb/juT1/4Th9f963fX/b9j0/2PT8/9Yz/L/TMrx/0HF8f83wO//LLzv/yK4
- 7v8MeqH/wr/B/wAAAAAAAAAAAAAAAAAAAADCv8H/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAMK/wf8VmMf/qO/3/6Lt9/+b6vb/kub2/4rj9f9/3vX/dNrz/2rV8/9d0fP/Uszy/0fI
- 8f88w/D/Mr7v/w19pP/Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAwr/B/xWZyP8UmMf/FZfF/xSVw/8TlML/E5K//xOQvf8Sjrv/EYy4/xGK
- tv8QiLL/D4Ww/w+Erf8Pgar/Dn+n/8K/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/8K/wf/Cv8H/wr/B/8K/
- wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/
- wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//
- /////////////8AAAAPe+AAD3vgAA974AAPe+AAD3vgAA974AAPAAAADwAAAA8AAAAPAAAADwAAAA8AA
- AAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAAD3/gAA9/4AAPf+AAD3/gAA9/4AAPf+AADwAAAA///
- ////////KAAAABAAAAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDA/8DA
- wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP8AAAAAAAAAAMDA
- wP8AAAAAAAAAAMDAwP8AAAAAwMDA/8F2R/+9bj//umc6/7diNf+3YjX/wMDA/wAAAADAwMD/AAAAAAAA
- AADAwMD/AAAAAAAAAADAwMD/AAAAAMDAwP/RkmD//7aP//+ldP/8kl3/vW0//8DAwP8AAAAAwMDA/wAA
- AAAAAAAAwMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/3ap2///Cov//to7//6V0/8uJWP/AwMD/AAAAAMDA
- wP8AAAAAAAAAAMDAwP8THI7/FBqF/xYYfP8XFnP/wMDA/+Cvev/gr3r/4K96/92qdv/ao3D/wMDA/wAA
- AADAwMD/AAAAAAAAAADAwMD/ECCd/2Fn3P8zOc//FRmC/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA
- wP/AwMD/wMDA/wAAAAAAAAAAwMDA/w0krP+Pler/YWbd/xIcj//AwMD/DHmf/wpzmP8Ib5L/B2uO/wdq
- jf8Gao3/B2qN/8DAwP8AAAAAAAAAAMDAwP8KJrv/r7Tz/5CU6v8PIJ//wMDA/w+Dq/87y/z/Kcb8/xrD
- /P8QwPv/EMD7/wdqjf/AwMD/AAAAAAAAAADAwMD/CCrI/woowP8LJrf/DSSu/8DAwP8Sjbj/Zdb9/0/Q
- /P88y/v/Kcf7/xrC+/8IbZD/wMDA/wAAAAAAAAAAwMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/FpfG/43h
- /f962/3/Zdb8/0/Q/P87zPz/CXSZ/8DAwP8AAAAAAAAAAMDAwP8AAAAAAAAAAAAAAAAAAAAAwMDA/xif
- z/+u6f7/n+X9/47h/f953P3/ZNb9/w19pP/AwMD/AAAAAAAAAADAwMD/AAAAAAAAAAAAAAAAAAAAAMDA
- wP8apNX/uez+/7ns/v+u6f7/oOX9/43h/f8Rh7H/wMDA/wAAAAAAAAAAwMDA/wAAAAAAAAAAAAAAAAAA
- AADAwMD/GqTV/xqk1f8apNX/GaHR/xecy/8WmMb/FJK+/8DAwP8AAAAAAAAAAMDAwP/AwMD/wMDA/8DA
- wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/AAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAgAEAALQF
- wf+0BQAAgAUAAIAFAACAAQAAgAHB/4ABAACAAQAAgAEAALwBAAC8AQAAvAHB/4ABbP///5H/
-
-
-
-
- AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQUUBAQEYgQE
- BHgEBARiBQUFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEhISYJmZ
- mb/k5OT/mZmZvxISEmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYGBk0FBQVmBQUFZiUl
- JXPy8vL/8vLy//Ly8v8lJSVzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDw9mAAAAAAAA
- AAA7Oztcurq6vP39/f+6urq8Ozs7XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhoaZgAA
- AAAAAAAASEhIEklJSVpJSUluSUlJWkhISBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgo
- KGYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAA1NTVmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADWWTcp2ls50NpbOf/aWznQ1lk3KQAA
- AAAAAAAAQUFBZgAAAAAAAAAADg4OFAsLC2ELCwt3CwsLYQ4ODhQAAAAAuEkn0NSJcf/ov7L/1Ilx/7hJ
- J9AAAAAAAAAAAExMTGYAAAAAAAAAACUlJV6fn5+95OTk/5+fn70lJSVeAAAAAK1IJv/wx7r/8Me6//DH
- uv+tSCb/VFRUZlRUVGZUVFRmVFRUZlRUVGZFRUVv8vLy//Ly8v/y8vL/RUVFbwAAAADIZELQ456H//fO
- wf/jnof/yGRC0AAAAAAAAAAAAAAAAAAAAAAAAAAAZWVlV8rKyrj9/f3/ysrKuGVlZVcAAAAA5oBeKemD
- YdDpg2H/6YNh0OaAXikAAAAAAAAAAAAAAAAAAAAAAAAAAHp6ehF8fHxUfHx8Z3x8fFR6enoRAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAA//8AAP//AAD//wAA/+MAAP/jAAD/4wAA//8AAP//AAD//wAAj/8AAAfjAAAH4wAAB+MAAI//
- AAD//wAA//8AAA==
-
-
-
\ No newline at end of file
diff --git a/Source/DependencyAnalyser/DependencyGraphPrintDocument.cs b/Source/DependencyAnalyser/DependencyGraphPrintDocument.cs
deleted file mode 100644
index ab37b92..0000000
--- a/Source/DependencyAnalyser/DependencyGraphPrintDocument.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System.Drawing;
-using System.Drawing.Printing;
-
-namespace DependencyAnalyser
-{
- ///
- /// Logic for printing dependency graphs.
- ///
- public class DependencyGraphPrintDocument : PrintDocument
- {
- private Image _dotImage;
-
- public DependencyGraphPrintDocument(Image dotImage)
- {
- _dotImage = dotImage;
- }
-
-// protected override void OnBeginPrint(PrintEventArgs ev)
-// {
-// base.OnBeginPrint(ev) ;
-// }
-
- protected override void OnPrintPage(PrintPageEventArgs args)
- {
- base.OnPrintPage(args);
-
- float leftMargin = args.MarginBounds.Left;
- float topMargin = args.MarginBounds.Top;
- float bottomMargin = args.MarginBounds.Bottom;
-
- float availableHeight = args.MarginBounds.Height;
- float availableWidth = args.MarginBounds.Width;
-
- using (var font = new Font("Arial", 8))
- {
- args.Graphics.DrawString("Created using .NET Dependency Analyser - http://drewnoakes.com/code/dependency-analyser/", font, Brushes.Black, leftMargin, bottomMargin - font.Height, new StringFormat());
-
- // maximise image on page without distorting dimensions
- args.Graphics.DrawImage(_dotImage, leftMargin, topMargin, availableWidth, availableHeight - font.Height);
- }
-
- args.HasMorePages = false;
- }
- }
-}
diff --git a/Source/DependencyAnalyser/DependencyPlotter.cs b/Source/DependencyAnalyser/DependencyPlotter.cs
deleted file mode 100644
index 4d848ee..0000000
--- a/Source/DependencyAnalyser/DependencyPlotter.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System.Drawing;
-using WINGRAPHVIZLib;
-
-namespace DependencyAnalyser
-{
- public static class DependencyPlotter
- {
- public static PlotResult CalculatePlot(double aspectRatio, DependencyGraph graph, FilterPreferences filterPreferences)
- {
- const double widthInches = 100;
- var heightInches = (double)(int)(widthInches / aspectRatio);
-
- // node [color=lightblue2, style=filled];
- // page=""8.5,11""
- // size=""7.5, 10""
- // ratio=All
- // widthInches = 75;
- // heightInches = 100;
-
- var extraCommands = $"size=\"{widthInches},{heightInches}\"\r\n center=\"\"\r\n ratio=All\r\n node[width=.25,hight=.375,fontsize=12,color=lightblue2,style=filled]";
- var dotCommand = DotCommandBuilder.Generate(graph, filterPreferences, extraCommands);
-
- // a temp file to store image
- var tempFile = TemporaryFileManager.CreateTemporaryFile();
-
- // generate dot image
- var dot = new DOTClass();
- dot.ToPNG(dotCommand).Save(tempFile);
- var dotImage = Image.FromFile(tempFile);
-
- // generate SVG
- var svgXml = dot.ToSvg(dotCommand);
-
- return new PlotResult(dotCommand, dotImage, svgXml);
- }
- }
-
- public class PlotResult
- {
- public string DotCommand { get; }
- public Image Image { get; }
- public string SvgXml { get; }
-
- public PlotResult(string dotCommand, Image image, string svgXml)
- {
- DotCommand = dotCommand;
- Image = image;
- SvgXml = svgXml;
- }
- }
-}
\ No newline at end of file
diff --git a/Source/DependencyAnalyser/DotCommandBuilder.cs b/Source/DependencyAnalyser/DotCommandBuilder.cs
deleted file mode 100644
index e22aec2..0000000
--- a/Source/DependencyAnalyser/DotCommandBuilder.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System.Collections.Generic;
-using System.Text;
-
-namespace DependencyAnalyser
-{
- ///
- /// Builds a Dot command for a given dependency graph.
- ///
- public static class DotCommandBuilder
- {
- public static string Generate(DependencyGraph graph, FilterPreferences filterPreferences)
- {
- return Generate(graph, filterPreferences, string.Empty);
- }
-
- public static string Generate(DependencyGraph graph, FilterPreferences filterPreferences, string extraCommands)
- {
- // TODO can this first loop be replaced with LINQ, maybe with a zip?
- var idsByNameMap = new Dictionary();
- var id = 1;
- foreach (var nodeName in graph.Nodes)
- {
- idsByNameMap.Add(nodeName, id);
- id++;
- }
-
- var commandText = new StringBuilder();
- commandText.Append("digraph G {\r\n");
-
- // handle extra commands
- if (extraCommands.Trim().Length > 0)
- {
- commandText.Append(" ");
- commandText.Append(extraCommands.Trim());
- commandText.Append("\r\n");
- }
-
- var nodeLabels = new StringBuilder();
-
- foreach (var dependant in graph.Nodes)
- {
- // make sure the dependant should be plotted
- if (!filterPreferences.IncludeInPlot(dependant))
- continue;
-
- var dependantId = idsByNameMap[dependant];
-
- // 1 [label="SampleProject",shape=circle,hight=0.12,width=0.12,fontsize=1];
- nodeLabels.AppendFormat(" {0} [label=\"{1}\"];\r\n", dependantId, dependant);
-
- foreach (var dependency in graph.GetDependenciesForNode(dependant))
- {
- var dependencyId = idsByNameMap[dependency];
- if (!filterPreferences.IncludeInPlot(dependency))
- continue;
- commandText.AppendFormat(" {0} -> {1};\r\n", dependantId, dependencyId);
- }
- }
-
- commandText.Append(nodeLabels);
- commandText.Append("}");
-
- return commandText.ToString();
- }
- }
-}
\ No newline at end of file
diff --git a/Source/DependencyAnalyser/InternalsVisibleTo.cs b/Source/DependencyAnalyser/InternalsVisibleTo.cs
deleted file mode 100644
index d891f0b..0000000
--- a/Source/DependencyAnalyser/InternalsVisibleTo.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("DependencyAnalyser.Tests")]
\ No newline at end of file
diff --git a/Source/DependencyAnalyser/Program.cs b/Source/DependencyAnalyser/Program.cs
deleted file mode 100644
index 72a9bfb..0000000
--- a/Source/DependencyAnalyser/Program.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-using System.Windows.Forms;
-
-namespace DependencyAnalyser
-{
- public static class Program
- {
- ///
- /// The main entry point for the application.
- ///
- [STAThread]
- private static void Main()
- {
- Application.EnableVisualStyles();
- Application.Run(new DependencyAnalyserForm());
- }
- }
-}
diff --git a/Source/DependencyAnalyser/SolutionFileAnalyser.cs b/Source/DependencyAnalyser/SolutionFileAnalyser.cs
deleted file mode 100644
index ab4261f..0000000
--- a/Source/DependencyAnalyser/SolutionFileAnalyser.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-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
-{
- ///
- /// Builds a DependencyGraph from a given Visual Studio solution (.sln) file.
- ///
- public static class SolutionFileAnalyser
- {
- public static async Task AnalyseAsync(string solutionPath, DependencyGraph graph, ILogger logger)
- {
- var instance = MSBuildLocator.QueryVisualStudioInstances().OrderByDescending(i => i.Version).First();
-
- logger.WriteLine($"Located MSBuild at: {instance.MSBuildPath}");
-
- MSBuildLocator.RegisterInstance(instance);
-
- using var workspace = MSBuildWorkspace.Create();
-
- workspace.WorkspaceFailed += (o, e) => logger.WriteLine(e.Diagnostic.Message);
-
- logger.WriteLine($"Loading solution: {solutionPath}");
-
- var solution = await workspace.OpenSolutionAsync(solutionPath);
-
- logger.WriteLine($"Finished loading solution: {solutionPath}");
-
- var projectById = new Dictionary();
-
- foreach (var project in solution.Projects)
- {
- projectById.Add(project.Id.Id, project);
- }
-
- foreach (var project in solution.Projects)
- {
- foreach (var projectReference in project.ProjectReferences)
- {
- var referencedProject = projectById[projectReference.ProjectId.Id];
-
- graph.AddDependency(project.Name, referencedProject.Name);
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/DependencyAnalyser/TemporaryFileManager.cs b/Source/DependencyAnalyser/TemporaryFileManager.cs
deleted file mode 100644
index cf26e3b..0000000
--- a/Source/DependencyAnalyser/TemporaryFileManager.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System;
-using System.IO;
-
-namespace DependencyAnalyser
-{
- public static class TemporaryFileManager
- {
- private static readonly Random _random = new Random();
-
- public static string CreateTemporaryFile()
- {
- var tempFilename = GenerateTemporaryFilename();
- while (File.Exists(tempFilename))
- tempFilename = GenerateTemporaryFilename();
- File.Create(tempFilename).Close();
- return tempFilename;
- }
-
- private static string GenerateTemporaryFilename()
- {
- // TODO loop until file not found
- return Path.Combine(Path.GetTempPath(), $"DependencyAnaylser.{_random.Next(int.MaxValue >> 4):X}.tmp");
- }
-
- public static void DeleteAllTemporaryFiles()
- {
- var filenames = GetExistingTemporaryFilenames();
- foreach (var filename in filenames)
- {
- try
- {
- File.Delete(filename);
- }
- catch
- {
- // file is probably locked
- }
- }
- }
-
- public static string[] GetExistingTemporaryFilenames()
- {
- var fileInfoArray = new DirectoryInfo(Path.GetTempPath()).GetFiles("DependencyAnaylser.*.tmp");
- var filenames = new string[fileInfoArray.Length];
- for (var index = 0; index < fileInfoArray.Length; index++)
- filenames[index] = fileInfoArray[index].FullName;
- return filenames;
- }
- }
-}
\ No newline at end of file
diff --git a/Source/Notes.txt b/Source/Notes.txt
deleted file mode 100644
index 4f6b3ca..0000000
--- a/Source/Notes.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-TODO:
-
-- compare references as dictated by compiler, versus those specified in projects/solution
-- recent files list
-
-- analyser constructors to take filenames
-
-- support vs 8.0 sln files
-- remember user's preferences for exclusion
-
-- support merging of multiple solution files
-- allow multiple select in Open dialog
-- allow printing of the image
-- use colouring/shapes on graph
- - indicate assemblies loaded from GAC, and those loaded from root directory
- - colour for those with more/less/no dependencies/dependants
-- reintroduce 'extra commands' to DotCommandBuilder
-- progress bar
-- show number of dependants/dependencies in exclusion list
-- menu items to show/hide all System.*
-- colour coding for nodes using wildcards (e.g. System.* = blue)
-
-DONE:
-
-- support merging additional .dll/.exe asseblies when started with a .exe or .dll
-- fix 'save png' to save to temp file and copy (needed to close FileStream before calling GDI+ save method)
-- use template for temp file names, so can clean up behind Wingraphviz (or dispose)
-- tidy code for loading of assemblies
-- extract class to create DependencyGraph (build graph in a single object instance)
-- allow exclusion of selected assembly names, eg 'mscorlib' (maintain exclusion list in DotCommandBuilder)
-- populate exclusion list from actual nodes in diagram
-- sort exclusion list alphabetically
-- set cursor to hourglass while calculating
-- use DialogResult==OK instead of handling OK click event for open/save dialogs
diff --git a/img/ui-filtered.png b/img/ui-filtered.png
new file mode 100644
index 0000000..d18a2e1
Binary files /dev/null and b/img/ui-filtered.png differ
diff --git a/img/ui-unfiltered.png b/img/ui-unfiltered.png
new file mode 100644
index 0000000..f3bef00
Binary files /dev/null and b/img/ui-unfiltered.png differ