diff --git a/Output/SharpVectors.Core.dll b/Output/SharpVectors.Core.dll
index 3f7754ab4..6913e4781 100644
Binary files a/Output/SharpVectors.Core.dll and b/Output/SharpVectors.Core.dll differ
diff --git a/Output/SharpVectors.Dom.dll b/Output/SharpVectors.Dom.dll
index f8e5d2154..eee72c822 100644
Binary files a/Output/SharpVectors.Dom.dll and b/Output/SharpVectors.Dom.dll differ
diff --git a/Output/SharpVectors.Runtime.Wpf.dll b/Output/SharpVectors.Runtime.Wpf.dll
index 21b904896..e330df52b 100644
Binary files a/Output/SharpVectors.Runtime.Wpf.dll and b/Output/SharpVectors.Runtime.Wpf.dll differ
diff --git a/Samples/WpfTestSvgControl/App.ico b/Samples/WpfTestSvgControl/App.ico
new file mode 100644
index 000000000..bf398870c
Binary files /dev/null and b/Samples/WpfTestSvgControl/App.ico differ
diff --git a/Samples/WpfTestSvgControl/App.xaml b/Samples/WpfTestSvgControl/App.xaml
new file mode 100644
index 000000000..ce9839f33
--- /dev/null
+++ b/Samples/WpfTestSvgControl/App.xaml
@@ -0,0 +1,562 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Samples/WpfTestSvgControl/App.xaml.cs b/Samples/WpfTestSvgControl/App.xaml.cs
new file mode 100644
index 000000000..0dde326ea
--- /dev/null
+++ b/Samples/WpfTestSvgControl/App.xaml.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Windows;
+
+namespace WpfTestSvgControl
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/Samples/WpfTestSvgControl/DebugPage.xaml b/Samples/WpfTestSvgControl/DebugPage.xaml
new file mode 100644
index 000000000..a6f8b8afa
--- /dev/null
+++ b/Samples/WpfTestSvgControl/DebugPage.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/Samples/WpfTestSvgControl/DebugPage.xaml.cs b/Samples/WpfTestSvgControl/DebugPage.xaml.cs
new file mode 100644
index 000000000..70594d377
--- /dev/null
+++ b/Samples/WpfTestSvgControl/DebugPage.xaml.cs
@@ -0,0 +1,53 @@
+using System;
+
+using System.Windows.Controls;
+
+namespace WpfTestSvgControl
+{
+ ///
+ /// Interaction logic for DebugPage.xaml
+ ///
+ public partial class DebugPage : Page
+ {
+ private MainWindow _mainWindow;
+
+ public DebugPage()
+ {
+ InitializeComponent();
+ }
+
+ public MainWindow MainWindow
+ {
+ get {
+ return _mainWindow;
+ }
+ set {
+ _mainWindow = value;
+ }
+ }
+
+ public void Startup()
+ {
+ if (traceDocument != null)
+ {
+ traceDocument.Startup();
+ }
+ }
+
+ public void Shutdown()
+ {
+ if (traceDocument != null)
+ {
+ traceDocument.Shutdown();
+ }
+ }
+
+ public void PageSelected(bool isSelected)
+ {
+ if (isSelected)
+ {
+ debugBox.Focus();
+ }
+ }
+ }
+}
diff --git a/Samples/WpfTestSvgControl/DrawingHelpWindow.xaml b/Samples/WpfTestSvgControl/DrawingHelpWindow.xaml
new file mode 100644
index 000000000..970e72644
--- /dev/null
+++ b/Samples/WpfTestSvgControl/DrawingHelpWindow.xaml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Samples/WpfTestSvgControl/DrawingHelpWindow.xaml.cs b/Samples/WpfTestSvgControl/DrawingHelpWindow.xaml.cs
new file mode 100644
index 000000000..b14fe6100
--- /dev/null
+++ b/Samples/WpfTestSvgControl/DrawingHelpWindow.xaml.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace WpfTestSvgControl
+{
+ ///
+ /// Interaction logic for DrawingHelpWindow.xaml
+ ///
+ public partial class DrawingHelpWindow : Window
+ {
+ public DrawingHelpWindow()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Samples/WpfTestSvgControl/DrawingPage.xaml b/Samples/WpfTestSvgControl/DrawingPage.xaml
new file mode 100644
index 000000000..083fead1c
--- /dev/null
+++ b/Samples/WpfTestSvgControl/DrawingPage.xaml
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Samples/WpfTestSvgControl/DrawingPage.xaml.cs b/Samples/WpfTestSvgControl/DrawingPage.xaml.cs
new file mode 100644
index 000000000..b46ca6027
--- /dev/null
+++ b/Samples/WpfTestSvgControl/DrawingPage.xaml.cs
@@ -0,0 +1,1403 @@
+using System;
+using System.IO;
+using System.Diagnostics;
+using System.Globalization;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Markup;
+using System.Windows.Controls;
+using System.Windows.Threading;
+
+using SharpVectors.Runtime;
+using SharpVectors.Converters;
+using SharpVectors.Renderers.Wpf;
+
+using ICSharpCode.AvalonEdit;
+using ICSharpCode.AvalonEdit.Folding;
+using ICSharpCode.AvalonEdit.Highlighting;
+
+namespace WpfTestSvgControl
+{
+ ///
+ /// Interaction logic for DrawingPage.xaml
+ ///
+ public partial class DrawingPage : Page
+ {
+ #region Public Fields
+
+ public const string TemporalDirName = "_Drawings";
+
+ #endregion
+
+ #region Private Fields
+
+ private const double ZoomChange = 0.1;
+
+ private bool _isLoadingDrawing;
+ private bool _saveXaml;
+
+ private string _drawingDir;
+ private string _svgFilePath;
+ private DirectoryInfo _directoryInfo;
+
+ private FileSvgReader _fileReader;
+ private WpfDrawingSettings _wpfSettings;
+
+ private DirectoryInfo _workingDir;
+
+ ///
+ /// Specifies the current state of the mouse handling logic.
+ ///
+ private ZoomPanMouseHandlingMode _mouseHandlingMode;
+
+ ///
+ /// The point that was clicked relative to the ZoomAndPanControl.
+ ///
+ private Point _origZoomAndPanControlMouseDownPoint;
+
+ ///
+ /// The point that was clicked relative to the content that is contained within the ZoomAndPanControl.
+ ///
+ private Point _origContentMouseDownPoint;
+
+ ///
+ /// Records which mouse button clicked during mouse dragging.
+ ///
+ private MouseButton _mouseButtonDown;
+
+ ///
+ /// Saves the previous zoom rectangle, pressing the backspace key jumps back to this zoom rectangle.
+ ///
+ private Rect _prevZoomRect;
+
+ ///
+ /// Save the previous content scale, pressing the backspace key jumps back to this scale.
+ ///
+ private double _prevZoomScale;
+
+ ///
+ /// Set to 'true' when the previous zoom rect is saved.
+ ///
+ private bool _prevZoomRectSet;
+
+ ///
+ /// Saves the next zoom rectangle, pressing the backspace key jumps back to this zoom rectangle.
+ ///
+ private Rect _nextZoomRect;
+
+ ///
+ /// Save the next content scale, pressing the backspace key jumps back to this scale.
+ ///
+ private double _nextZoomScale;
+
+ ///
+ /// Set to 'true' when the previous zoom rect is saved.
+ ///
+ private bool _nextZoomRectSet;
+
+ private Cursor _panToolCursor;
+ private Cursor _panToolDownCursor;
+
+ private Cursor _canvasCursor;
+
+ private MainWindow _mainWindow;
+ private OptionSettings _optionSettings;
+
+ private DispatcherTimer _dispatcherTimer;
+
+ private string _selectedName;
+
+ private WpfDrawingDocument _drawingDocument;
+
+ private EmbeddedImageSerializerVisitor _embeddedImageVisitor;
+ private IList _embeddedImages;
+
+ private FoldingManager _foldingManager;
+ private XmlFoldingStrategy _foldingStrategy;
+
+ #endregion
+
+ #region Constructors and Destructor
+
+ public DrawingPage()
+ {
+ InitializeComponent();
+
+ _saveXaml = true;
+ _wpfSettings = new WpfDrawingSettings();
+ _wpfSettings.CultureInfo = _wpfSettings.NeutralCultureInfo;
+
+ _fileReader = new FileSvgReader(_wpfSettings);
+ _fileReader.SaveXaml = _saveXaml;
+ _fileReader.SaveZaml = false;
+
+ _mouseHandlingMode = ZoomPanMouseHandlingMode.None;
+
+ string workDir = Path.Combine(Path.GetDirectoryName(
+ System.Reflection.Assembly.GetExecutingAssembly().Location), TemporalDirName);
+
+ _workingDir = new DirectoryInfo(workDir);
+
+ _embeddedImages = new List();
+
+ _embeddedImageVisitor = new EmbeddedImageSerializerVisitor(true);
+ _wpfSettings.Visitors.ImageVisitor = _embeddedImageVisitor;
+
+ _embeddedImageVisitor.ImageCreated += OnEmbeddedImageCreated;
+
+ textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("XML");
+ TextEditorOptions options = textEditor.Options;
+ if (options != null)
+ {
+ //options.AllowScrollBelowDocument = true;
+ options.EnableHyperlinks = true;
+ options.EnableEmailHyperlinks = true;
+ options.EnableVirtualSpace = false;
+ options.HighlightCurrentLine = true;
+ options.ShowSpaces = true;
+ options.ShowTabs = true;
+ options.ShowEndOfLine = true;
+ }
+
+ textEditor.ShowLineNumbers = true;
+ textEditor.WordWrap = true;
+
+ _foldingManager = FoldingManager.Install(textEditor.TextArea);
+ _foldingStrategy = new XmlFoldingStrategy();
+
+ this.Loaded += OnPageLoaded;
+ this.Unloaded += OnPageUnloaded;
+ this.SizeChanged += OnPageSizeChanged;
+ }
+
+ private void OnEmbeddedImageCreated(object sender, EmbeddedImageSerializerArgs args)
+ {
+ if (args == null)
+ {
+ return;
+ }
+ if (_embeddedImages == null)
+ {
+ _embeddedImages = new List();
+ }
+ _embeddedImages.Add(args);
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ public bool IsLoadingDrawing
+ {
+ get {
+ return _isLoadingDrawing;
+ }
+ }
+
+ public string WorkingDrawingDir
+ {
+ get {
+ return _drawingDir;
+ }
+ set {
+ _drawingDir = value;
+
+ if (!string.IsNullOrWhiteSpace(_drawingDir))
+ {
+ _directoryInfo = new DirectoryInfo(_drawingDir);
+
+ if (_fileReader != null)
+ {
+ _fileReader.SaveXaml = Directory.Exists(_drawingDir);
+ }
+ }
+ }
+ }
+
+ public bool SaveXaml
+ {
+ get {
+ return _saveXaml;
+ }
+ set {
+ _saveXaml = value;
+ if (_fileReader != null)
+ {
+ _fileReader.SaveXaml = _saveXaml;
+ _fileReader.SaveZaml = false;
+ }
+ }
+ }
+
+ public WpfDrawingSettings ConversionSettings
+ {
+ get {
+ return _wpfSettings;
+ }
+ set {
+ if (value != null)
+ {
+ _wpfSettings = value;
+
+ // Recreated the conveter
+ _fileReader = new FileSvgReader(_wpfSettings);
+ _fileReader.SaveXaml = true;
+ _fileReader.SaveZaml = false;
+
+ if (!string.IsNullOrWhiteSpace(_drawingDir) &&
+ Directory.Exists(_drawingDir))
+ {
+ _fileReader.SaveXaml = Directory.Exists(_drawingDir);
+ }
+ }
+ }
+ }
+
+ public OptionSettings OptionSettings
+ {
+ get {
+ return _optionSettings;
+ }
+ set {
+ if (value != null)
+ {
+ _optionSettings = value;
+ this.ConversionSettings = value.ConversionSettings;
+ }
+ }
+ }
+
+ public MainWindow MainWindow
+ {
+ get {
+ return _mainWindow;
+ }
+ set {
+ _mainWindow = value;
+
+ if (_optionSettings == null)
+ {
+ this.OptionSettings = _mainWindow.OptionSettings;
+ }
+ }
+ }
+
+ //
+ // Definitions for dependency properties.
+ //
+ ///
+ /// This allows the same property name to be used for direct and indirect access to the ZoomPanelControl control.
+ ///
+ public ZoomPanControl ZoomPanContent {
+ get {
+ return zoomPanControl;
+ }
+ }
+
+ ///
+ /// This allows the same property name to be used for direct and indirect access to the SVG Canvas control.
+ ///
+ public SvgDrawingCanvas Viewer
+ {
+ get {
+ return svgViewer;
+ }
+ }
+
+ public WpfDrawingDocument DrawingDocument
+ {
+ get {
+ return _drawingDocument;
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ public void SelectElement(string selectedName)
+ {
+ _selectedName = selectedName;
+ if (string.IsNullOrWhiteSpace(selectedName) || _drawingDocument == null)
+ {
+ elementImage.Source = null;
+ textEditor.Text = string.Empty;
+
+ return;
+ }
+
+ var selecteElement = _drawingDocument.GetSvgById(selectedName);
+ if (selecteElement != null)
+ {
+ textEditor.Text = selecteElement.OuterXml;
+ }
+ else
+ {
+ textEditor.Text = string.Empty;
+ }
+
+ var selectedDrawing = _drawingDocument.GetById(selectedName);
+ if (selectedDrawing != null)
+ {
+ elementImage.Source = new DrawingImage(selectedDrawing);
+ }
+ else
+ {
+ elementImage.Source = null;
+ }
+ }
+
+ public bool LoadDocument(string svgFilePath)
+ {
+ if (string.IsNullOrWhiteSpace(svgFilePath) || !File.Exists(svgFilePath))
+ {
+ return false;
+ }
+
+ DirectoryInfo workingDir = _workingDir;
+ if (_directoryInfo != null)
+ {
+ workingDir = _directoryInfo;
+ }
+
+ this.UnloadDocument(true);
+
+ _svgFilePath = svgFilePath;
+ _saveXaml = _optionSettings.ShowOutputFile;
+
+ string fileExt = Path.GetExtension(svgFilePath);
+
+ if (string.Equals(fileExt, SvgConverter.SvgExt, StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(fileExt, SvgConverter.CompressedSvgExt, StringComparison.OrdinalIgnoreCase))
+ {
+ if (_fileReader != null)
+ {
+ _fileReader.SaveXaml = _saveXaml;
+ _fileReader.SaveZaml = false;
+
+ _embeddedImageVisitor.SaveImages = !_wpfSettings.IncludeRuntime;
+ _embeddedImageVisitor.SaveDirectory = _drawingDir;
+ _wpfSettings.Visitors.ImageVisitor = _embeddedImageVisitor;
+
+ DrawingGroup drawing = _fileReader.Read(svgFilePath, workingDir);
+ _drawingDocument = _fileReader.DrawingDocument;
+ if (drawing != null)
+ {
+ svgViewer.UnloadDiagrams();
+ svgViewer.RenderDiagrams(drawing);
+
+ Rect bounds = svgViewer.Bounds;
+
+ if (bounds.IsEmpty)
+ {
+ bounds = new Rect(0, 0, zoomPanControl.ActualWidth, zoomPanControl.ActualHeight);
+ }
+
+ zoomPanControl.AnimatedZoomTo(bounds);
+ CommandManager.InvalidateRequerySuggested();
+
+ return true;
+ }
+ }
+ }
+ else if (string.Equals(fileExt, SvgConverter.XamlExt, StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(fileExt, SvgConverter.CompressedXamlExt, StringComparison.OrdinalIgnoreCase))
+ {
+ svgViewer.LoadDiagrams(svgFilePath);
+
+ svgViewer.InvalidateMeasure();
+
+ return true;
+ }
+
+ _svgFilePath = null;
+
+ return false;
+ }
+
+ public Task LoadDocumentAsync(string svgFilePath)
+ {
+ if (_isLoadingDrawing || string.IsNullOrWhiteSpace(svgFilePath) || !File.Exists(svgFilePath))
+ {
+ return Task.FromResult(false);
+ }
+
+ string fileExt = Path.GetExtension(svgFilePath);
+
+ if (!(string.Equals(fileExt, SvgConverter.SvgExt, StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(fileExt, SvgConverter.CompressedSvgExt, StringComparison.OrdinalIgnoreCase)))
+ {
+ _svgFilePath = null;
+ return Task.FromResult(false);
+ }
+
+ _isLoadingDrawing = true;
+
+ this.UnloadDocument(true);
+
+ DirectoryInfo workingDir = _workingDir;
+ if (_directoryInfo != null)
+ {
+ workingDir = _directoryInfo;
+ }
+
+ _svgFilePath = svgFilePath;
+ _saveXaml = _optionSettings.ShowOutputFile;
+
+ _embeddedImageVisitor.SaveImages = !_wpfSettings.IncludeRuntime;
+ _embeddedImageVisitor.SaveDirectory = _drawingDir;
+ _wpfSettings.Visitors.ImageVisitor = _embeddedImageVisitor;
+
+ if (_fileReader == null)
+ {
+ _fileReader = new FileSvgReader(_wpfSettings);
+ _fileReader.SaveXaml = _saveXaml;
+ _fileReader.SaveZaml = false;
+ }
+
+ var drawingStream = new MemoryStream();
+
+ // Get the UI thread's context
+ var context = TaskScheduler.FromCurrentSynchronizationContext();
+
+ return Task.Factory.StartNew(() =>
+ {
+// var saveXaml = _fileReader.SaveXaml;
+// _fileReader.SaveXaml = true; // For threaded, we will save to avoid loading issue later...
+ DrawingGroup drawing = _fileReader.Read(svgFilePath, workingDir);
+// _fileReader.SaveXaml = saveXaml;
+ _drawingDocument = _fileReader.DrawingDocument;
+ if (drawing != null)
+ {
+ XamlWriter.Save(drawing, drawingStream);
+ drawingStream.Seek(0, SeekOrigin.Begin);
+
+ return true;
+ }
+ _svgFilePath = null;
+ return false;
+ }).ContinueWith((t) => {
+ try
+ {
+ if (!t.Result)
+ {
+ _isLoadingDrawing = false;
+ _svgFilePath = null;
+ return false;
+ }
+ if (drawingStream.Length != 0)
+ {
+ DrawingGroup drawing = (DrawingGroup)XamlReader.Load(drawingStream);
+
+ svgViewer.UnloadDiagrams();
+ svgViewer.RenderDiagrams(drawing);
+
+ Rect bounds = svgViewer.Bounds;
+
+ if (bounds.IsEmpty)
+ {
+ bounds = new Rect(0, 0, svgViewer.ActualWidth, svgViewer.ActualHeight);
+ }
+
+ zoomPanControl.AnimatedZoomTo(bounds);
+ CommandManager.InvalidateRequerySuggested();
+
+ // The drawing changed, update the source...
+ _fileReader.Drawing = drawing;
+ }
+
+ _isLoadingDrawing = false;
+
+ return true;
+ }
+ catch
+ {
+ _isLoadingDrawing = false;
+ throw;
+ }
+ }, context);
+ }
+
+ public void UnloadDocument(bool displayMessage = false)
+ {
+ try
+ {
+ elementImage.Source = null;
+ textEditor.Text = string.Empty;
+
+ _svgFilePath = null;
+ _drawingDocument = null;
+
+ if (svgViewer != null)
+ {
+ svgViewer.UnloadDiagrams();
+
+ if (displayMessage)
+ {
+ var drawing = this.DrawText("Loading...");
+
+ svgViewer.RenderDiagrams(drawing);
+
+ Rect bounds = svgViewer.Bounds;
+ if (bounds.IsEmpty)
+ {
+ bounds = drawing.Bounds;
+ }
+
+ zoomPanControl.ZoomTo(bounds);
+ return;
+ }
+ }
+
+ var drawRect = this.DrawRect();
+ svgViewer.RenderDiagrams(drawRect);
+
+ zoomPanControl.ZoomTo(drawRect.Bounds);
+ ClearPrevZoomRect();
+ ClearNextZoomRect();
+ }
+ finally
+ {
+ if (_embeddedImages != null && _embeddedImages.Count != 0)
+ {
+ foreach (var embeddedImage in _embeddedImages)
+ {
+ try
+ {
+ if (embeddedImage.Image != null)
+ {
+ if (embeddedImage.Image.StreamSource != null)
+ {
+ embeddedImage.Image.StreamSource.Dispose();
+ }
+ }
+
+ var imagePath = embeddedImage.ImagePath;
+ if (!string.IsNullOrWhiteSpace(imagePath) && File.Exists(imagePath))
+ {
+ File.Delete(imagePath);
+ }
+ }
+ catch (IOException ex)
+ {
+ Trace.TraceError(ex.ToString());
+ // Image this, WPF will typically cache and/or lock loaded images
+ }
+ }
+
+ _embeddedImages.Clear();
+ }
+ }
+ }
+
+ public bool SaveDocument(string fileName)
+ {
+ if (string.IsNullOrWhiteSpace(fileName))
+ {
+ return false;
+ }
+
+ if (_fileReader == null || _fileReader.Drawing == null)
+ {
+ return false;
+ }
+ return _fileReader.Save(fileName, true, false);
+ }
+
+ public void PageSelected(bool isSelected)
+ {
+ if (isSelected)
+ {
+ svgViewer.Focus();
+
+ if (zoomPanControl.IsKeyboardFocusWithin)
+ {
+ Keyboard.Focus(zoomPanControl);
+ }
+ }
+ }
+
+ public void SaveZoom()
+ {
+ if (zoomPanControl != null)
+ {
+ SavePrevZoomRect();
+
+ ClearNextZoomRect();
+ }
+ }
+
+ #endregion
+
+ #region Protected Methods
+
+ protected override void OnInitialized(EventArgs e)
+ {
+ base.OnInitialized(e);
+ }
+
+ #endregion
+
+ #region Private Event Handlers (Page)
+
+ private void OnPageLoaded(object sender, RoutedEventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(_svgFilePath) || !File.Exists(_svgFilePath))
+ {
+ zoomPanControl.ContentScale = 1.0;
+
+ if (zoomPanControl != null)
+ {
+ zoomPanControl.IsMouseWheelScrollingEnabled = true;
+ }
+
+ if (string.IsNullOrWhiteSpace(_svgFilePath))
+ {
+ this.UnloadDocument();
+ }
+ }
+
+ try
+ {
+ if (_panToolCursor == null)
+ {
+ var panToolStream = Application.GetResourceStream(new Uri("Resources/PanTool.cur", UriKind.Relative));
+ using (panToolStream.Stream)
+ {
+ _panToolCursor = new Cursor(panToolStream.Stream);
+ }
+ }
+ if (_panToolDownCursor == null)
+ {
+ var panToolDownStream = Application.GetResourceStream(new Uri("Resources/PanToolDown.cur", UriKind.Relative));
+ using (panToolDownStream.Stream)
+ {
+ _panToolDownCursor = new Cursor(panToolDownStream.Stream);
+ }
+ }
+
+ // DispatcherTimer setup
+ if (_dispatcherTimer == null)
+ {
+ _dispatcherTimer = new DispatcherTimer();
+ _dispatcherTimer.Tick += OnUpdateUITick;
+ _dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
+ }
+ }
+ catch (Exception ex)
+ {
+ Trace.TraceError(ex.ToString());
+ }
+
+ if (zoomPanControl != null && zoomPanControl.ScrollOwner == null)
+ {
+ if (canvasScroller != null)
+ {
+ zoomPanControl.ScrollOwner = canvasScroller;
+ }
+ }
+
+ if (_dispatcherTimer != null)
+ {
+ _dispatcherTimer.Start();
+ }
+ }
+
+ private void OnPageUnloaded(object sender, RoutedEventArgs e)
+ {
+ if (_dispatcherTimer != null)
+ {
+ _dispatcherTimer.Stop();
+ }
+ }
+
+ private void OnPageSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ if (zoomPanControl != null && svgViewer != null)
+ {
+ svgViewer.InvalidateMeasure();
+ svgViewer.UpdateLayout();
+
+ Rect bounds = svgViewer.Bounds;
+
+ if (bounds.IsEmpty)
+ {
+ bounds = new Rect(0, 0, svgViewer.ActualWidth, svgViewer.ActualHeight);
+ }
+
+ //zoomPanControl.AnimatedZoomTo(bounds);
+ zoomPanControl.AnimatedZoomTo(this.FitZoomValue);
+ CommandManager.InvalidateRequerySuggested();
+ }
+ }
+
+ private async void OnOpenFileClick(object sender, RoutedEventArgs e)
+ {
+ if (_mainWindow != null)
+ {
+ await _mainWindow.BrowseForFile();
+ }
+ }
+
+ private void OnOpenFolderClick(object sender, RoutedEventArgs e)
+ {
+ }
+
+ private void OnShowHelp(object sender, RoutedEventArgs e)
+ {
+ var helpDialog = new DrawingHelpWindow();
+
+ if (_mainWindow != null)
+ {
+ helpDialog.Left = _mainWindow.Left + _mainWindow.ActualWidth - helpDialog.Width;
+ helpDialog.Top = _mainWindow.Top + _mainWindow.ActualHeight - helpDialog.Height;
+ helpDialog.Owner = _mainWindow;
+ helpDialog.WindowStartupLocation = WindowStartupLocation.Manual;
+ }
+
+ helpDialog.Show();
+ }
+
+ ///
+ /// Updates the current seconds display and calls InvalidateRequerySuggested on the
+ /// CommandManager to force the Command to raise the CanExecuteChanged event.
+ ///
+ ///
+ ///
+ private void OnUpdateUITick(object sender, EventArgs e)
+ {
+ // Forcing the CommandManager to raise the RequerySuggested event
+ CommandManager.InvalidateRequerySuggested();
+ }
+
+ #endregion
+
+ #region Private Zoom Panel Handlers
+
+ ///
+ /// Event raised on mouse down in the ZoomAndPanControl.
+ ///
+ private void OnZoomPanMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ zoomPanControl.Focus();
+ Keyboard.Focus(zoomPanControl);
+
+ _mouseButtonDown = e.ChangedButton;
+ _origZoomAndPanControlMouseDownPoint = e.GetPosition(zoomPanControl);
+ _origContentMouseDownPoint = e.GetPosition(svgViewer);
+
+ if (_mouseHandlingMode == ZoomPanMouseHandlingMode.SelectPoint ||
+ _mouseHandlingMode == ZoomPanMouseHandlingMode.SelectRectangle)
+ {
+ }
+ else
+ {
+ if ((Keyboard.Modifiers & ModifierKeys.Shift) != 0 &&
+ (e.ChangedButton == MouseButton.Left || e.ChangedButton == MouseButton.Right))
+ {
+ // Shift + left- or right-down initiates zooming mode.
+ _mouseHandlingMode = ZoomPanMouseHandlingMode.Zooming;
+
+ if (zoomPanControl != null && _canvasCursor != null)
+ {
+ zoomPanControl.Cursor = _canvasCursor;
+ }
+ }
+ else if (_mouseButtonDown == MouseButton.Left)
+ {
+ // Just a plain old left-down initiates panning mode.
+ _mouseHandlingMode = ZoomPanMouseHandlingMode.Panning;
+ }
+
+ if (_mouseHandlingMode != ZoomPanMouseHandlingMode.None)
+ {
+ // Capture the mouse so that we eventually receive the mouse up event.
+ zoomPanControl.CaptureMouse();
+ e.Handled = true;
+ }
+ }
+
+ }
+
+ ///
+ /// Event raised on mouse up in the ZoomAndPanControl.
+ ///
+ private void OnZoomPanMouseUp(object sender, MouseButtonEventArgs e)
+ {
+ if (_mouseHandlingMode == ZoomPanMouseHandlingMode.SelectPoint ||
+ _mouseHandlingMode == ZoomPanMouseHandlingMode.SelectRectangle)
+ {
+ }
+ else
+ {
+ if (_mouseHandlingMode != ZoomPanMouseHandlingMode.None)
+ {
+ if (_mouseHandlingMode == ZoomPanMouseHandlingMode.Zooming)
+ {
+ if (_mouseButtonDown == MouseButton.Left)
+ {
+ // Shift + left-click zooms in on the content.
+ ZoomIn(_origContentMouseDownPoint);
+ }
+ else if (_mouseButtonDown == MouseButton.Right)
+ {
+ // Shift + left-click zooms out from the content.
+ ZoomOut(_origContentMouseDownPoint);
+ }
+ }
+ else if (_mouseHandlingMode == ZoomPanMouseHandlingMode.DragZooming)
+ {
+ // When drag-zooming has finished we zoom in on the rectangle that was highlighted by the user.
+ ApplyDragZoomRect();
+ }
+
+ zoomPanControl.ReleaseMouseCapture();
+ _mouseHandlingMode = ZoomPanMouseHandlingMode.None;
+ e.Handled = true;
+ }
+
+ if (zoomPanControl != null && _canvasCursor != null)
+ {
+ zoomPanControl.Cursor = _canvasCursor;
+ }
+ }
+ }
+
+ ///
+ /// Event raised on mouse move in the ZoomAndPanControl.
+ ///
+ private void OnZoomPanMouseMove(object sender, MouseEventArgs e)
+ {
+ if (_mouseHandlingMode == ZoomPanMouseHandlingMode.SelectPoint ||
+ _mouseHandlingMode == ZoomPanMouseHandlingMode.SelectRectangle)
+ {
+ }
+ else
+ {
+ if (_mouseHandlingMode == ZoomPanMouseHandlingMode.Panning)
+ {
+ if (zoomPanControl != null)
+ {
+ zoomPanControl.Cursor = _panToolCursor;
+ }
+
+ //
+ // The user is left-dragging the mouse.
+ // Pan the viewport by the appropriate amount.
+ //
+ Point curContentMousePoint = e.GetPosition(svgViewer);
+ Vector dragOffset = curContentMousePoint - _origContentMouseDownPoint;
+
+ zoomPanControl.ContentOffsetX -= dragOffset.X;
+ zoomPanControl.ContentOffsetY -= dragOffset.Y;
+
+ e.Handled = true;
+ }
+ else if (_mouseHandlingMode == ZoomPanMouseHandlingMode.Zooming)
+ {
+ if (zoomPanControl != null && _canvasCursor != null)
+ {
+ zoomPanControl.Cursor = _canvasCursor;
+ }
+
+ Point curZoomAndPanControlMousePoint = e.GetPosition(zoomPanControl);
+ Vector dragOffset = curZoomAndPanControlMousePoint - _origZoomAndPanControlMouseDownPoint;
+ double dragThreshold = 10;
+ if (_mouseButtonDown == MouseButton.Left &&
+ (Math.Abs(dragOffset.X) > dragThreshold ||
+ Math.Abs(dragOffset.Y) > dragThreshold))
+ {
+ //
+ // When Shift + left-down zooming mode and the user drags beyond the drag threshold,
+ // initiate drag zooming mode where the user can drag out a rectangle to select the area
+ // to zoom in on.
+ //
+ _mouseHandlingMode = ZoomPanMouseHandlingMode.DragZooming;
+
+ Point curContentMousePoint = e.GetPosition(svgViewer);
+ InitDragZoomRect(_origContentMouseDownPoint, curContentMousePoint);
+ }
+
+ e.Handled = true;
+ }
+ else if (_mouseHandlingMode == ZoomPanMouseHandlingMode.DragZooming)
+ {
+ if (zoomPanControl != null && _canvasCursor != null)
+ {
+ zoomPanControl.Cursor = _canvasCursor;
+ }
+
+ //
+ // When in drag zooming mode continously update the position of the rectangle
+ // that the user is dragging out.
+ //
+ Point curContentMousePoint = e.GetPosition(svgViewer);
+ SetDragZoomRect(_origContentMouseDownPoint, curContentMousePoint);
+
+ e.Handled = true;
+ }
+ }
+ }
+
+ ///
+ /// Event raised by rotating the mouse wheel
+ ///
+ private void OnZoomPanMouseWheel(object sender, MouseWheelEventArgs e)
+ {
+ e.Handled = true;
+
+ Point curContentMousePoint = e.GetPosition(svgViewer);
+ //if (e.Delta > 0)
+ //{
+ // ZoomIn(curContentMousePoint);
+ //}
+ //else if (e.Delta < 0)
+ //{
+ // ZoomOut(curContentMousePoint);
+ //}
+ this.Zoom(curContentMousePoint, e.Delta);
+
+ if (svgViewer.IsKeyboardFocusWithin)
+ {
+ Keyboard.Focus(zoomPanControl);
+ }
+ }
+
+ ///
+ /// Event raised by double-left click
+ ///
+ private void OnZoomPanMouseDoubleClick(object sender, MouseButtonEventArgs e)
+ {
+ if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0)
+ {
+ SavePrevZoomRect();
+
+ zoomPanControl.AnimatedSnapTo(e.GetPosition(svgViewer));
+
+ ClearNextZoomRect();
+
+ e.Handled = true;
+ }
+ }
+
+ ///
+ /// The 'Pan' command (bound to the plus key) was executed.
+ ///
+ private void OnPanMode(object sender, RoutedEventArgs e)
+ {
+ }
+
+ ///
+ /// Determines whether the 'Pan' command can be executed.
+ ///
+ private void OnCanPanMode(object sender, CanExecuteRoutedEventArgs e)
+ {
+ e.CanExecute = false;
+ }
+
+ ///
+ /// The 'ZoomReset' command (bound to the plus key) was executed.
+ ///
+ private void OnZoomReset(object sender, RoutedEventArgs e)
+ {
+ SavePrevZoomRect();
+
+ zoomPanControl.AnimatedZoomTo(1.0);
+
+ ClearNextZoomRect();
+ }
+
+ ///
+ /// Determines whether the 'ZoomReset' command can be executed.
+ ///
+ private void OnCanZoomReset(object sender, CanExecuteRoutedEventArgs e)
+ {
+ if (zoomPanControl == null)
+ {
+ e.CanExecute = false;
+ return;
+ }
+ e.CanExecute = !zoomPanControl.ContentScale.Equals(1.0);
+ }
+
+ ///
+ /// The 'ZoomFit/Fill' command (bound to the plus key) was executed.
+ ///
+ private void OnZoomFit(object sender, RoutedEventArgs e)
+ {
+ SavePrevZoomRect();
+
+ //zoomPanControl.AnimatedScaleToFit();
+ zoomPanControl.AnimatedZoomTo(this.FitZoomValue);
+
+ ClearNextZoomRect();
+ }
+
+ ///
+ /// Determines whether the 'ZoomFit' command can be executed.
+ ///
+ private void OnCanZoomFit(object sender, CanExecuteRoutedEventArgs e)
+ {
+ if (zoomPanControl == null)
+ {
+ e.CanExecute = false;
+ return;
+ }
+
+ var fitValue = this.FitZoomValue;
+
+ e.CanExecute = !IsWithinOnePercent(zoomPanControl.ContentScale, fitValue)
+ && fitValue >= zoomPanControl.MinContentScale;
+ }
+
+ ///
+ /// The 'ZoomIn' command (bound to the plus key) was executed.
+ ///
+ private void OnZoomIn(object sender, RoutedEventArgs e)
+ {
+ SavePrevZoomRect();
+
+ ZoomIn(new Point(zoomPanControl.ContentZoomFocusX, zoomPanControl.ContentZoomFocusY));
+
+ ClearNextZoomRect();
+ }
+
+ ///
+ /// Determines whether the 'ZoomIn' command can be executed.
+ ///
+ private void OnCanZoomIn(object sender, CanExecuteRoutedEventArgs e)
+ {
+ if (zoomPanControl == null)
+ {
+ e.CanExecute = false;
+ return;
+ }
+ e.CanExecute = zoomPanControl.ContentScale < zoomPanControl.MaxContentScale;
+ }
+
+ ///
+ /// The 'ZoomOut' command (bound to the minus key) was executed.
+ ///
+ private void OnZoomOut(object sender, RoutedEventArgs e)
+ {
+ SavePrevZoomRect();
+
+ ZoomOut(new Point(zoomPanControl.ContentZoomFocusX, zoomPanControl.ContentZoomFocusY));
+
+ ClearNextZoomRect();
+ }
+
+ ///
+ /// Determines whether the 'UndoZoom' command can be executed.
+ ///
+ private void OnCanZoomOut(object sender, CanExecuteRoutedEventArgs e)
+ {
+ if (zoomPanControl == null)
+ {
+ e.CanExecute = false;
+ return;
+ }
+ e.CanExecute = zoomPanControl.ContentScale > zoomPanControl.MinContentScale;
+ }
+
+ ///
+ /// The 'UndoZoom' command was executed.
+ ///
+ private void OnUndoZoom(object sender, ExecutedRoutedEventArgs e)
+ {
+ UndoZoom();
+ }
+
+ ///
+ /// Determines whether the 'UndoZoom' command can be executed.
+ ///
+ private void OnCanUndoZoom(object sender, CanExecuteRoutedEventArgs e)
+ {
+ e.CanExecute = _prevZoomRectSet;
+ }
+
+ ///
+ /// The 'RedoZoom' command was executed.
+ ///
+ private void OnRedoZoom(object sender, ExecutedRoutedEventArgs e)
+ {
+ RedoZoom();
+ }
+
+ ///
+ /// Determines whether the 'RedoZoom' command can be executed.
+ ///
+ private void OnCanRedoZoom(object sender, CanExecuteRoutedEventArgs e)
+ {
+ e.CanExecute = _nextZoomRectSet;
+ }
+
+ ///
+ /// Jump back to the previous zoom level.
+ ///
+ private void UndoZoom()
+ {
+ SaveNextZoomRect();
+
+ zoomPanControl.AnimatedZoomTo(_prevZoomScale, _prevZoomRect);
+
+ ClearPrevZoomRect();
+ }
+
+ ///
+ /// Jump back to the next zoom level.
+ ///
+ private void RedoZoom()
+ {
+ SavePrevZoomRect();
+
+ zoomPanControl.AnimatedZoomTo(_nextZoomScale, _nextZoomRect);
+
+ ClearNextZoomRect();
+ }
+
+ private void Zoom(Point contentZoomCenter, int wheelMouseDelta)
+ {
+ SavePrevZoomRect();
+
+ // Found the division by 3 gives a little smoothing effect
+ var zoomFactor = zoomPanControl.ContentScale + ZoomChange * wheelMouseDelta / (120 * 3);
+
+ zoomPanControl.ZoomAboutPoint(zoomFactor, contentZoomCenter);
+
+ ClearNextZoomRect();
+ }
+
+ ///
+ /// Zoom the viewport out, centering on the specified point (in content coordinates).
+ ///
+ private void ZoomOut(Point contentZoomCenter)
+ {
+ SavePrevZoomRect();
+
+ zoomPanControl.ZoomAboutPoint(zoomPanControl.ContentScale - ZoomChange, contentZoomCenter);
+
+ ClearNextZoomRect();
+ }
+
+ ///
+ /// Zoom the viewport in, centering on the specified point (in content coordinates).
+ ///
+ private void ZoomIn(Point contentZoomCenter)
+ {
+ SavePrevZoomRect();
+
+ zoomPanControl.ZoomAboutPoint(zoomPanControl.ContentScale + ZoomChange, contentZoomCenter);
+
+ ClearNextZoomRect();
+ }
+
+ ///
+ /// Initialise the rectangle that the use is dragging out.
+ ///
+ private void InitDragZoomRect(Point pt1, Point pt2)
+ {
+ SetDragZoomRect(pt1, pt2);
+
+ dragZoomCanvas.Visibility = Visibility.Visible;
+ dragZoomBorder.Opacity = 0.5;
+ }
+
+ ///
+ /// Update the position and size of the rectangle that user is dragging out.
+ ///
+ private void SetDragZoomRect(Point pt1, Point pt2)
+ {
+ double x, y, width, height;
+
+ //
+ // Deterine x,y,width and height of the rect inverting the points if necessary.
+ //
+
+ if (pt2.X < pt1.X)
+ {
+ x = pt2.X;
+ width = pt1.X - pt2.X;
+ }
+ else
+ {
+ x = pt1.X;
+ width = pt2.X - pt1.X;
+ }
+
+ if (pt2.Y < pt1.Y)
+ {
+ y = pt2.Y;
+ height = pt1.Y - pt2.Y;
+ }
+ else
+ {
+ y = pt1.Y;
+ height = pt2.Y - pt1.Y;
+ }
+
+ //
+ // Update the coordinates of the rectangle that is being dragged out by the user.
+ // The we offset and rescale to convert from content coordinates.
+ //
+ Canvas.SetLeft(dragZoomBorder, x);
+ Canvas.SetTop(dragZoomBorder, y);
+ dragZoomBorder.Width = width;
+ dragZoomBorder.Height = height;
+ }
+
+ ///
+ /// When the user has finished dragging out the rectangle the zoom operation is applied.
+ ///
+ private void ApplyDragZoomRect()
+ {
+ //
+ // Record the previous zoom level, so that we can jump back to it when the backspace key is pressed.
+ //
+ SavePrevZoomRect();
+
+ //
+ // Retreive the rectangle that the user draggged out and zoom in on it.
+ //
+ double contentX = Canvas.GetLeft(dragZoomBorder);
+ double contentY = Canvas.GetTop(dragZoomBorder);
+ double contentWidth = dragZoomBorder.Width;
+ double contentHeight = dragZoomBorder.Height;
+ zoomPanControl.AnimatedZoomTo(new Rect(contentX, contentY, contentWidth, contentHeight));
+
+ FadeOutDragZoomRect();
+
+ ClearNextZoomRect();
+ }
+
+ //
+ // Fade out the drag zoom rectangle.
+ //
+ private void FadeOutDragZoomRect()
+ {
+ ZoomPanAnimationHelper.StartAnimation(dragZoomBorder, OpacityProperty, 0.0, ZoomChange,
+ delegate (object sender, EventArgs e)
+ {
+ dragZoomCanvas.Visibility = Visibility.Collapsed;
+ });
+ }
+
+ //
+ // Record the previous zoom level, so that we can jump back to it when the backspace key is pressed.
+ //
+ private void SavePrevZoomRect()
+ {
+ _prevZoomRect = new Rect(zoomPanControl.ContentOffsetX, zoomPanControl.ContentOffsetY,
+ zoomPanControl.ContentViewportWidth, zoomPanControl.ContentViewportHeight);
+ _prevZoomScale = zoomPanControl.ContentScale;
+ _prevZoomRectSet = true;
+ }
+
+ //
+ // Record the next zoom level, so that we can jump back to it when the backspace key is pressed.
+ //
+ private void SaveNextZoomRect()
+ {
+ _nextZoomRect = new Rect(zoomPanControl.ContentOffsetX, zoomPanControl.ContentOffsetY,
+ zoomPanControl.ContentViewportWidth, zoomPanControl.ContentViewportHeight);
+ _nextZoomScale = zoomPanControl.ContentScale;
+ _nextZoomRectSet = true;
+ }
+
+ ///
+ /// Clear the memory of the previous zoom level.
+ ///
+ private void ClearPrevZoomRect()
+ {
+ _prevZoomRectSet = false;
+ }
+
+ ///
+ /// Clear the memory of the next zoom level.
+ ///
+ private void ClearNextZoomRect()
+ {
+ _nextZoomRectSet = false;
+ }
+
+ public double FitZoomValue
+ {
+ get {
+ if (zoomPanControl == null)
+ {
+ return 1;
+ }
+
+ var content = zoomPanControl.ContentElement;
+
+ return FitZoom(ActualWidth, ActualHeight, content?.ActualWidth, content?.ActualHeight);
+ }
+ }
+
+ private static bool IsWithinOnePercent(double value, double testValue)
+ {
+ return Math.Abs(value - testValue) < .01 * testValue;
+ }
+
+ private static double FitZoom(double actualWidth, double actualHeight, double? contentWidth, double? contentHeight)
+ {
+ if (!contentWidth.HasValue || !contentHeight.HasValue) return 1;
+ return Math.Min(actualWidth / contentWidth.Value, actualHeight / contentHeight.Value);
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private DrawingGroup DrawRect()
+ {
+ // Create a new DrawingGroup of the control.
+ DrawingGroup drawingGroup = new DrawingGroup();
+
+ // Open the DrawingGroup in order to access the DrawingContext.
+ using (DrawingContext drawingContext = drawingGroup.Open())
+ {
+ drawingContext.DrawRectangle(Brushes.White, null, new Rect(0, 0, 280, 300));
+ }
+ // Return the updated DrawingGroup content to be used by the control.
+ return drawingGroup;
+ }
+
+ // Convert the text string to a geometry and draw it to the control's DrawingContext.
+ private DrawingGroup DrawText(string textString)
+ {
+ // Create a new DrawingGroup of the control.
+ DrawingGroup drawingGroup = new DrawingGroup();
+
+ drawingGroup.Opacity = 0.8;
+
+ // Open the DrawingGroup in order to access the DrawingContext.
+ using (DrawingContext drawingContext = drawingGroup.Open())
+ {
+ // Create the formatted text based on the properties set.
+ var formattedText = new FormattedText(textString,
+ CultureInfo.CurrentUICulture, FlowDirection.LeftToRight,
+ new Typeface(new FontFamily("Tahoma"), FontStyles.Normal,
+ FontWeights.Normal, FontStretches.Normal), 72, Brushes.Black);
+
+ // Build the geometry object that represents the text.
+ Geometry textGeometry = formattedText.BuildGeometry(new Point(20, 0));
+
+ drawingContext.DrawRoundedRectangle(Brushes.Transparent, null,
+ new Rect(new Size(formattedText.Width + 50, formattedText.Height + 5)), 5.0, 5.0);
+
+ // Draw the outline based on the properties that are set.
+ drawingContext.DrawGeometry(null, new Pen(Brushes.DarkGray, 1.5), textGeometry);
+ }
+
+ // Return the updated DrawingGroup content to be used by the control.
+ return drawingGroup;
+ }
+
+ #endregion
+ }
+}
diff --git a/Samples/WpfTestSvgControl/DrawingPageHelp.xaml b/Samples/WpfTestSvgControl/DrawingPageHelp.xaml
new file mode 100644
index 000000000..9c5ce1a07
--- /dev/null
+++ b/Samples/WpfTestSvgControl/DrawingPageHelp.xaml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+ Mouse and Keyboard Controls
+
+
+
+ Control + Plus key = zoom in
+
+
+ Control + Minus key = zoom out
+
+
+ Left-drag = panning
+
+
+ Left-drag = drag the rectangles
+
+
+ Shift + left-drag = drag out a rectangle to zoom to
+
+
+ Control + Z = jump back to previous zoom level (Undo)
+
+
+ Control + Y = jump back to next zoom level (Redo)
+
+
+ Shift + left-click = zoom in
+
+
+ Shift + right-click = zoom out
+
+
+ Double-left-click = center on the clicked location
+
+
+ Mouse wheel forward = zoom in
+
+
+ Mouse wheel backward = zoom out
+
+
+
+ Overview Window
+
+
+
+ Left-drag = drag the overview mode rectangle about (can also drag the rectangles).
+
+
+ Double-left-click = snap the overview mode rectangle to a particular point.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/GridExpander.cs b/Samples/WpfTestSvgControl/GridExpander.cs
new file mode 100644
index 000000000..cd475dea0
--- /dev/null
+++ b/Samples/WpfTestSvgControl/GridExpander.cs
@@ -0,0 +1,625 @@
+//
+// Based on codes from
+// https://jefuri.wordpress.com/2010/09/15/gridexpander-for-wpf/
+//
+
+using System;
+using System.Windows;
+using System.Windows.Shapes;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Media.Animation;
+
+namespace WpfTestSvgControl
+{
+ ///
+ /// Specifies different collapse modes of a GridExpander.
+ ///
+ public enum GridExpanderDirection
+ {
+ ///
+ /// The GridExpander cannot be collapsed or expanded.
+ ///
+ None = 0,
+ ///
+ /// The column (or row) to the right (or below) the
+ /// splitter's column, will be collapsed.
+ ///
+ Next = 1,
+ ///
+ /// The column (or row) to the left (or above) the
+ /// splitter's column, will be collapsed.
+ ///
+ Previous = 2
+ }
+
+ ///
+ /// An updated version of the standard GridExpander control that includes a centered handle
+ /// which allows complete collapsing and expanding of the appropriate grid column or row.
+ ///
+ [TemplatePart(Name = ElementHandleName, Type = typeof(ToggleButton))]
+ [TemplatePart(Name = ElementTemplateName, Type = typeof(FrameworkElement))]
+ [TemplateVisualState(Name = "MouseOver", GroupName = "CommonStates")]
+ [TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
+ [TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")]
+ [TemplateVisualState(Name = "Focused", GroupName = "FocusStates")]
+ [TemplateVisualState(Name = "Unfocused", GroupName = "FocusStates")]
+ public class GridExpander : GridSplitter
+ {
+ #region Private Fields
+
+ ///
+ /// An enumeration that specifies the direction the GridExpander will
+ /// be collapased (Rows or Columns).
+ ///
+ private enum GridCollapseOrientation
+ {
+ Auto,
+ Columns,
+ Rows
+ }
+
+ private const string ElementHandleName = "ExpanderHandle";
+ private const string ElementTemplateName = "TheTemplate";
+ private const string ElementGridExpanderBackground = "GridExpanderBackground";
+
+ private ToggleButton _expanderButton;
+ private Rectangle _elementGridExpanderBackground;
+
+ private RowDefinition AnimatingRow;
+ private ColumnDefinition AnimatingColumn;
+
+ private GridCollapseOrientation _gridCollapseDirection = GridCollapseOrientation.Auto;
+ private GridLength _savedGridLength;
+ private double _savedActualValue;
+ private double _animationTimeMillis = 200;
+
+ #endregion
+
+ #region Dependency properties
+
+ ///
+ /// Identifies the Direction dependency property
+ ///
+ public static readonly DependencyProperty DirectionProperty = DependencyProperty.Register(
+ "Direction", typeof(GridExpanderDirection), typeof(GridExpander),
+ new PropertyMetadata(GridExpanderDirection.Next, new PropertyChangedCallback(OnDirectionPropertyChanged)));
+
+ ///
+ /// Identifies the HandleStyle dependency property
+ ///
+ public static readonly DependencyProperty HandleStyleProperty = DependencyProperty.Register(
+ "HandleStyle", typeof(Style), typeof(GridExpander), null);
+
+ ///
+ /// Identifies the IsAnimated dependency property
+ ///
+ public static readonly DependencyProperty IsAnimatedProperty = DependencyProperty.Register(
+ "IsAnimated", typeof(bool), typeof(GridExpander), null);
+
+ ///
+ /// Identifies the IsCollapsed dependency property
+ ///
+ public static readonly DependencyProperty IsCollapsedProperty = DependencyProperty.Register(
+ "IsCollapsed", typeof(bool), typeof(GridExpander),
+ new PropertyMetadata(new PropertyChangedCallback(OnIsCollapsedPropertyChanged)));
+
+ private static readonly DependencyProperty RowHeightAnimationProperty = DependencyProperty.Register(
+ "RowHeightAnimation", typeof(double), typeof(GridExpander),
+ new PropertyMetadata(new PropertyChangedCallback(RowHeightAnimationChanged)));
+
+ private static readonly DependencyProperty ColWidthAnimationProperty = DependencyProperty.Register(
+ "ColWidthAnimation", typeof(double), typeof(GridExpander),
+ new PropertyMetadata(new PropertyChangedCallback(ColWidthAnimationChanged)));
+
+ #endregion
+
+ #region Public Events
+
+ // Define Collapsed and Expanded evenets
+ public event EventHandler Collapsed;
+ public event EventHandler Expanded;
+
+ #endregion
+
+ #region Constructors and Destructor
+
+ ///
+ /// Initializes a new instance of the GridExpander class,
+ /// which inherits from System.Windows.Controls.GridExpander.
+ ///
+ public GridExpander()
+ {
+ // Set default values
+ //DefaultStyleKey = typeof(GridExpander);
+
+ VisualStateManager.GoToState(this, "Checked", false);
+
+ //Direction = GridExpanderDirection.None;
+ this.IsAnimated = true;
+ this.LayoutUpdated += delegate
+ {
+ _gridCollapseDirection = GetCollapseDirection();
+ };
+
+ // All GridExpander visual states are handled by the parent GridSplitter class.
+
+ this.Direction = GridExpanderDirection.Next;
+ }
+
+ static GridExpander()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(GridExpander),
+ new FrameworkPropertyMetadata(typeof(GridExpander)));
+ }
+
+ #endregion
+
+ #region Public and Private Properties
+
+ ///
+ /// Gets or sets a value that indicates the direction in which the row/colum
+ /// will be located that is to be expanded and collapsed.
+ ///
+ public GridExpanderDirection Direction
+ {
+ get { return (GridExpanderDirection)GetValue(DirectionProperty); }
+ set { SetValue(DirectionProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the style that customizes the appearance of the vertical handle
+ /// that is used to expand and collapse the GridExpander.
+ ///
+ public Style HandleStyle
+ {
+ get { return (Style)GetValue(HandleStyleProperty); }
+ set { SetValue(HandleStyleProperty, value); }
+ }
+
+ ///
+ /// Gets or sets a value that indicates if the collapse and
+ /// expanding actions should be animated.
+ ///
+ public bool IsAnimated
+ {
+ get { return (bool)GetValue(IsAnimatedProperty); }
+ set { SetValue(IsAnimatedProperty, value); }
+ }
+
+ ///
+ /// Gets or sets a value that indicates if the target column is
+ /// currently collapsed.
+ ///
+ public bool IsCollapsed
+ {
+ get { return (bool)GetValue(IsCollapsedProperty); }
+ set { SetValue(IsCollapsedProperty, value); }
+ }
+
+ private double RowHeightAnimation
+ {
+ get { return (double)GetValue(RowHeightAnimationProperty); }
+ set { SetValue(RowHeightAnimationProperty, value); }
+ }
+
+ private double ColWidthAnimation
+ {
+ get { return (double)GetValue(ColWidthAnimationProperty); }
+ set { SetValue(ColWidthAnimationProperty, value); }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ ///
+ /// This method is called when the tempalte should be applied to the control.
+ ///
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ _expanderButton = GetTemplateChild(ElementHandleName) as ToggleButton;
+ _elementGridExpanderBackground = GetTemplateChild(ElementGridExpanderBackground) as Rectangle;
+
+ // Wire up the Checked and Unchecked events of the VerticalGridExpanderHandle.
+ if (_expanderButton != null)
+ {
+ _expanderButton.Checked += GridExpanderButton_Checked;
+ _expanderButton.Unchecked += OnExpanderButtonUnchecked;
+ }
+
+ // Set default direction since we don't have all the components layed out yet.
+ _gridCollapseDirection = GridCollapseOrientation.Auto;
+
+ // Directely call these events so design-time view updates appropriately
+ OnDirectionChanged(Direction);
+ OnIsCollapsedChanged(IsCollapsed);
+ }
+
+ #endregion
+
+ #region Protected Methods
+
+ ///
+ /// Handles the property change event of the IsCollapsed property.
+ ///
+ /// The new value for the IsCollapsed property.
+ protected virtual void OnIsCollapsedChanged(bool isCollapsed)
+ {
+ _expanderButton.IsChecked = isCollapsed;
+ }
+
+ ///
+ /// Handles the property change event of the Direction property.
+ ///
+ /// The new value for the Direction property.
+ protected virtual void OnDirectionChanged(GridExpanderDirection direction)
+ {
+ if (_expanderButton == null)
+ {
+ // There is no expander button so don't attempt to modify it
+ return;
+ }
+
+ // TODO: Use triggers for setting visibility conditionally instead of doing it here
+ if (direction == GridExpanderDirection.None)
+ {
+ // Hide the handles if the Direction is set to None.
+ _expanderButton.Visibility = Visibility.Collapsed;
+ }
+ else
+ {
+ // Ensure the handle is Visible.
+ _expanderButton.Visibility = Visibility.Visible;
+ }
+ }
+
+ ///
+ /// Raises the Collapsed event.
+ ///
+ /// Contains event arguments.
+ protected virtual void OnCollapsed(EventArgs e)
+ {
+ this.Collapsed?.Invoke(this, e);
+ }
+
+ ///
+ /// Raises the Expanded event.
+ ///
+ /// Contains event arguments.
+ protected virtual void OnExpanded(EventArgs e)
+ {
+ this.Expanded?.Invoke(this, e);
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ ///
+ /// Collapses the target ColumnDefinition or RowDefinition.
+ ///
+ private void Collapse()
+ {
+ Grid parentGrid = base.Parent as Grid;
+ int splitterIndex = int.MinValue;
+
+ if (_gridCollapseDirection == GridCollapseOrientation.Rows)
+ {
+ // Get the index of the row containing the splitter
+ splitterIndex = (int)base.GetValue(Grid.RowProperty);
+
+ // Determing the curent Direction
+ if (this.Direction == GridExpanderDirection.Next)
+ {
+ // Save the next rows Height information
+ _savedGridLength = parentGrid.RowDefinitions[splitterIndex + 1].Height;
+ _savedActualValue = parentGrid.RowDefinitions[splitterIndex + 1].ActualHeight;
+
+ // Collapse the next row
+ if (IsAnimated)
+ AnimateCollapse(parentGrid.RowDefinitions[splitterIndex + 1]);
+ else
+ parentGrid.RowDefinitions[splitterIndex + 1].SetValue(RowDefinition.HeightProperty, new GridLength(0));
+ }
+ else
+ {
+ // Save the previous row's Height information
+ _savedGridLength = parentGrid.RowDefinitions[splitterIndex - 1].Height;
+ _savedActualValue = parentGrid.RowDefinitions[splitterIndex - 1].ActualHeight;
+
+ // Collapse the previous row
+ if (IsAnimated)
+ AnimateCollapse(parentGrid.RowDefinitions[splitterIndex - 1]);
+ else
+ parentGrid.RowDefinitions[splitterIndex - 1].SetValue(RowDefinition.HeightProperty, new GridLength(0));
+ }
+ }
+ else
+ {
+ // Get the index of the column containing the splitter
+ splitterIndex = (int)base.GetValue(Grid.ColumnProperty);
+
+ // Determing the curent Direction
+ if (this.Direction == GridExpanderDirection.Next)
+ {
+ // Save the next column's Width information
+ _savedGridLength = parentGrid.ColumnDefinitions[splitterIndex + 1].Width;
+ _savedActualValue = parentGrid.ColumnDefinitions[splitterIndex + 1].ActualWidth;
+
+ // Collapse the next column
+ if (IsAnimated)
+ AnimateCollapse(parentGrid.ColumnDefinitions[splitterIndex + 1]);
+ else
+ parentGrid.ColumnDefinitions[splitterIndex + 1].SetValue(ColumnDefinition.WidthProperty, new GridLength(0));
+ }
+ else
+ {
+ // Save the previous column's Width information
+ _savedGridLength = parentGrid.ColumnDefinitions[splitterIndex - 1].Width;
+ _savedActualValue = parentGrid.ColumnDefinitions[splitterIndex - 1].ActualWidth;
+
+ // Collapse the previous column
+ if (IsAnimated)
+ AnimateCollapse(parentGrid.ColumnDefinitions[splitterIndex - 1]);
+ else
+ parentGrid.ColumnDefinitions[splitterIndex - 1].SetValue(ColumnDefinition.WidthProperty, new GridLength(0));
+ }
+ }
+
+ }
+
+ ///
+ /// Expands the target ColumnDefinition or RowDefinition.
+ ///
+ private void Expand()
+ {
+ Grid parentGrid = base.Parent as Grid;
+ int splitterIndex = int.MinValue;
+
+ if (_gridCollapseDirection == GridCollapseOrientation.Rows)
+ {
+ // Get the index of the row containing the splitter
+ splitterIndex = (int)this.GetValue(Grid.RowProperty);
+
+ // Determine the curent Direction
+ if (this.Direction == GridExpanderDirection.Next)
+ {
+ // Expand the next row
+ if (IsAnimated)
+ AnimateExpand(parentGrid.RowDefinitions[splitterIndex + 1]);
+ else
+ parentGrid.RowDefinitions[splitterIndex + 1].SetValue(RowDefinition.HeightProperty, _savedGridLength);
+ }
+ else
+ {
+ // Expand the previous row
+ if (IsAnimated)
+ AnimateExpand(parentGrid.RowDefinitions[splitterIndex - 1]);
+ else
+ parentGrid.RowDefinitions[splitterIndex - 1].SetValue(RowDefinition.HeightProperty, _savedGridLength);
+ }
+ }
+ else
+ {
+ // Get the index of the column containing the splitter
+ splitterIndex = (int)this.GetValue(Grid.ColumnProperty);
+
+ // Determine the curent Direction
+ if (this.Direction == GridExpanderDirection.Next)
+ {
+ // Expand the next column
+ if (IsAnimated)
+ AnimateExpand(parentGrid.ColumnDefinitions[splitterIndex + 1]);
+ else
+ parentGrid.ColumnDefinitions[splitterIndex + 1].SetValue(ColumnDefinition.WidthProperty, _savedGridLength);
+ }
+ else
+ {
+ // Expand the previous column
+ if (IsAnimated)
+ AnimateExpand(parentGrid.ColumnDefinitions[splitterIndex - 1]);
+ else
+ parentGrid.ColumnDefinitions[splitterIndex - 1].SetValue(ColumnDefinition.WidthProperty, _savedGridLength);
+ }
+ }
+ }
+
+ ///
+ /// Determine the collapse direction based on the horizontal and vertical alignments
+ ///
+ private GridCollapseOrientation GetCollapseDirection()
+ {
+ if (base.HorizontalAlignment != HorizontalAlignment.Stretch)
+ {
+ return GridCollapseOrientation.Columns;
+ }
+
+ if ((base.VerticalAlignment == VerticalAlignment.Stretch) && (base.ActualWidth <= base.ActualHeight))
+ {
+ return GridCollapseOrientation.Columns;
+ }
+
+ return GridCollapseOrientation.Rows;
+ }
+
+ ///
+ /// Handles the Checked event of either the Vertical or Horizontal
+ /// GridExpanderHandle ToggleButton.
+ ///
+ /// An instance of the ToggleButton that fired the event.
+ /// Contains event arguments for the routed event that fired.
+ private void GridExpanderButton_Checked(object sender, RoutedEventArgs e)
+ {
+ if (IsCollapsed != true)
+ {
+ // In our case, Checked = Collapsed. Which means we want everything
+ // ready to be expanded.
+ Collapse();
+
+ IsCollapsed = true;
+
+ // Deactivate the background so the splitter can not be dragged.
+ _elementGridExpanderBackground.IsHitTestVisible = false;
+ //_elementGridExpanderBackground.Opacity = 0.5;
+
+ // Raise the Collapsed event.
+ OnCollapsed(EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Handles the Unchecked event of either the Vertical or Horizontal
+ /// GridExpanderHandle ToggleButton.
+ ///
+ /// An instance of the ToggleButton that fired the event.
+ /// Contains event arguments for the routed event that fired.
+ private void OnExpanderButtonUnchecked(object sender, RoutedEventArgs e)
+ {
+ if (IsCollapsed != false)
+ {
+ // In our case, Unchecked = Expanded. Which means we want everything
+ // ready to be collapsed.
+ Expand();
+
+ IsCollapsed = false;
+
+ // Activate the background so the splitter can be dragged again.
+ _elementGridExpanderBackground.IsHitTestVisible = true;
+ //_elementGridExpanderBackground.Opacity = 1;
+
+ // Raise the Expanded event.
+ OnExpanded(EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// The IsCollapsed property porperty changed handler.
+ ///
+ /// GridExpander that changed IsCollapsed.
+ /// An instance of DependencyPropertyChangedEventArgs.
+ private static void OnIsCollapsedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ GridExpander s = d as GridExpander;
+
+ bool value = (bool)e.NewValue;
+ s.OnIsCollapsedChanged(value);
+ }
+
+ ///
+ /// The DirectionProperty property changed handler.
+ ///
+ /// GridExpander that changed IsCollapsed.
+ /// An instance of DependencyPropertyChangedEventArgs.
+ private static void OnDirectionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ GridExpander s = d as GridExpander;
+
+ GridExpanderDirection value = (GridExpanderDirection)e.NewValue;
+ s.OnDirectionChanged(value);
+ }
+
+ private static void RowHeightAnimationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ (d as GridExpander).AnimatingRow.Height = new GridLength((double)e.NewValue);
+ }
+
+ private static void ColWidthAnimationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ (d as GridExpander).AnimatingColumn.Width = new GridLength((double)e.NewValue);
+ }
+
+ ///
+ /// Uses DoubleAnimation and a StoryBoard to animated the collapsing
+ /// of the specificed ColumnDefinition or RowDefinition.
+ ///
+ /// The RowDefinition or ColumnDefintition that will be collapsed.
+ private void AnimateCollapse(object definition)
+ {
+ double currentValue;
+
+ // Setup the animation and StoryBoard
+ DoubleAnimation gridLengthAnimation = new DoubleAnimation()
+ {
+ Duration = new Duration(TimeSpan.FromMilliseconds(_animationTimeMillis))
+ };
+ Storyboard sb = new Storyboard();
+
+ // Add the animation to the StoryBoard
+ sb.Children.Add(gridLengthAnimation);
+
+ if (_gridCollapseDirection == GridCollapseOrientation.Rows)
+ {
+ // Specify the target RowDefinition and property (Height) that will be altered by the animation.
+ this.AnimatingRow = (RowDefinition)definition;
+ Storyboard.SetTarget(gridLengthAnimation, this);
+ Storyboard.SetTargetProperty(gridLengthAnimation, new PropertyPath("RowHeightAnimation"));
+
+ currentValue = AnimatingRow.ActualHeight;
+ }
+ else
+ {
+ // Specify the target ColumnDefinition and property (Width) that will be altered by the animation.
+ this.AnimatingColumn = (ColumnDefinition)definition;
+ Storyboard.SetTarget(gridLengthAnimation, this);
+ Storyboard.SetTargetProperty(gridLengthAnimation, new PropertyPath("ColWidthAnimation"));
+
+ currentValue = AnimatingColumn.ActualWidth;
+ }
+
+ gridLengthAnimation.From = currentValue;
+ gridLengthAnimation.To = 0;
+
+ // Start the StoryBoard.
+ sb.Begin();
+ }
+
+ ///
+ /// Uses DoubleAnimation and a StoryBoard to animate the expansion
+ /// of the specificed ColumnDefinition or RowDefinition.
+ ///
+ /// The RowDefinition or ColumnDefintition that will be expanded.
+ private void AnimateExpand(object definition)
+ {
+ double currentValue;
+
+ // Setup the animation and StoryBoard
+ DoubleAnimation gridLengthAnimation = new DoubleAnimation()
+ {
+ Duration = new Duration(TimeSpan.FromMilliseconds(_animationTimeMillis))
+ };
+ Storyboard sb = new Storyboard();
+
+ // Add the animation to the StoryBoard
+ sb.Children.Add(gridLengthAnimation);
+
+ if (_gridCollapseDirection == GridCollapseOrientation.Rows)
+ {
+ // Specify the target RowDefinition and property (Height) that will be altered by the animation.
+ this.AnimatingRow = (RowDefinition)definition;
+ Storyboard.SetTarget(gridLengthAnimation, this);
+ Storyboard.SetTargetProperty(gridLengthAnimation, new PropertyPath("RowHeightAnimation"));
+
+ currentValue = AnimatingRow.ActualHeight;
+ }
+ else
+ {
+ // Specify the target ColumnDefinition and property (Width) that will be altered by the animation.
+ this.AnimatingColumn = (ColumnDefinition)definition;
+ Storyboard.SetTarget(gridLengthAnimation, this);
+ Storyboard.SetTargetProperty(gridLengthAnimation, new PropertyPath("ColWidthAnimation"));
+
+ currentValue = AnimatingColumn.ActualWidth;
+ }
+ gridLengthAnimation.From = currentValue;
+ gridLengthAnimation.To = _savedActualValue;
+
+ // Start the StoryBoard.
+ sb.Begin();
+ }
+
+ #endregion
+ }
+}
diff --git a/Samples/WpfTestSvgControl/Images/Copy.svg b/Samples/WpfTestSvgControl/Images/Copy.svg
new file mode 100644
index 000000000..3de60b008
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Copy.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Cut.svg b/Samples/WpfTestSvgControl/Images/Cut.svg
new file mode 100644
index 000000000..a28dbe156
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Cut.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Debug.svg b/Samples/WpfTestSvgControl/Images/Debug.svg
new file mode 100644
index 000000000..18934c5ea
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Debug.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Delete.svg b/Samples/WpfTestSvgControl/Images/Delete.svg
new file mode 100644
index 000000000..9b9f54b78
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Delete.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Find.svg b/Samples/WpfTestSvgControl/Images/Find.svg
new file mode 100644
index 000000000..1b80bf68c
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Find.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/FolderClose.svg b/Samples/WpfTestSvgControl/Images/FolderClose.svg
new file mode 100644
index 000000000..481bb01ac
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/FolderClose.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/FolderOpen.svg b/Samples/WpfTestSvgControl/Images/FolderOpen.svg
new file mode 100644
index 000000000..4c8a03f5b
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/FolderOpen.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Format.svg b/Samples/WpfTestSvgControl/Images/Format.svg
new file mode 100644
index 000000000..84ea7ccd0
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Format.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Information.svg b/Samples/WpfTestSvgControl/Images/Information.svg
new file mode 100644
index 000000000..76647f248
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Information.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Number.svg b/Samples/WpfTestSvgControl/Images/Number.svg
new file mode 100644
index 000000000..b2639d7da
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Number.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Open.svg b/Samples/WpfTestSvgControl/Images/Open.svg
new file mode 100644
index 000000000..9d8f471f8
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Open.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/OpenFolder.svg b/Samples/WpfTestSvgControl/Images/OpenFolder.svg
new file mode 100644
index 000000000..8284b8ba4
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/OpenFolder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Output.svg b/Samples/WpfTestSvgControl/Images/Output.svg
new file mode 100644
index 000000000..8cdeaf18e
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Output.svg
@@ -0,0 +1,22 @@
+
+
+
+
diff --git a/Samples/WpfTestSvgControl/Images/Panning.svg b/Samples/WpfTestSvgControl/Images/Panning.svg
new file mode 100644
index 000000000..313a82f10
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Panning.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Paste.svg b/Samples/WpfTestSvgControl/Images/Paste.svg
new file mode 100644
index 000000000..144e0ee5e
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Paste.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Print.svg b/Samples/WpfTestSvgControl/Images/Print.svg
new file mode 100644
index 000000000..d7299a3df
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Print.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/PrintPreview.svg b/Samples/WpfTestSvgControl/Images/PrintPreview.svg
new file mode 100644
index 000000000..2cb40960d
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/PrintPreview.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Redo.svg b/Samples/WpfTestSvgControl/Images/Redo.svg
new file mode 100644
index 000000000..f19995258
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Redo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Run.svg b/Samples/WpfTestSvgControl/Images/Run.svg
new file mode 100644
index 000000000..0713e1502
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Run.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Save.svg b/Samples/WpfTestSvgControl/Images/Save.svg
new file mode 100644
index 000000000..e2a1dec56
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Save.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Settings.svg b/Samples/WpfTestSvgControl/Images/Settings.svg
new file mode 100644
index 000000000..0a20698d6
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Settings.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Space.svg b/Samples/WpfTestSvgControl/Images/Space.svg
new file mode 100644
index 000000000..b05984d0c
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Space.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/SvgLogo.svg b/Samples/WpfTestSvgControl/Images/SvgLogo.svg
new file mode 100644
index 000000000..ec4145531
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/SvgLogo.svg
@@ -0,0 +1,64 @@
+
diff --git a/Samples/WpfTestSvgControl/Images/SvgLogoBasic.svg b/Samples/WpfTestSvgControl/Images/SvgLogoBasic.svg
new file mode 100644
index 000000000..fc6213b54
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/SvgLogoBasic.svg
@@ -0,0 +1,50 @@
+
diff --git a/Samples/WpfTestSvgControl/Images/Test.svg b/Samples/WpfTestSvgControl/Images/Test.svg
new file mode 100644
index 000000000..59dd2aa87
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Test.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/TestResultDetail.svg b/Samples/WpfTestSvgControl/Images/TestResultDetail.svg
new file mode 100644
index 000000000..0afc0c5a1
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/TestResultDetail.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/TestRunner.svg b/Samples/WpfTestSvgControl/Images/TestRunner.svg
new file mode 100644
index 000000000..915c81b46
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/TestRunner.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Undo.svg b/Samples/WpfTestSvgControl/Images/Undo.svg
new file mode 100644
index 000000000..b0fa32200
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Undo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/View.svg b/Samples/WpfTestSvgControl/Images/View.svg
new file mode 100644
index 000000000..0a9671948
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/View.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/Web.svg b/Samples/WpfTestSvgControl/Images/Web.svg
new file mode 100644
index 000000000..a888d5286
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/Web.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/WordWrap.svg b/Samples/WpfTestSvgControl/Images/WordWrap.svg
new file mode 100644
index 000000000..1d03f87ca
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/WordWrap.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/ZoomIn.svg b/Samples/WpfTestSvgControl/Images/ZoomIn.svg
new file mode 100644
index 000000000..4741411e7
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/ZoomIn.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/ZoomOut.svg b/Samples/WpfTestSvgControl/Images/ZoomOut.svg
new file mode 100644
index 000000000..de7a749f1
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/ZoomOut.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/ZoomReset.svg b/Samples/WpfTestSvgControl/Images/ZoomReset.svg
new file mode 100644
index 000000000..057870bd2
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/ZoomReset.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/Images/ZoomToFit.svg b/Samples/WpfTestSvgControl/Images/ZoomToFit.svg
new file mode 100644
index 000000000..9fac7c1ae
--- /dev/null
+++ b/Samples/WpfTestSvgControl/Images/ZoomToFit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Samples/WpfTestSvgControl/MainWindow.xaml b/Samples/WpfTestSvgControl/MainWindow.xaml
new file mode 100644
index 000000000..f315f69e0
--- /dev/null
+++ b/Samples/WpfTestSvgControl/MainWindow.xaml
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Samples/WpfTestSvgControl/MainWindow.xaml.cs b/Samples/WpfTestSvgControl/MainWindow.xaml.cs
new file mode 100644
index 000000000..d046fff52
--- /dev/null
+++ b/Samples/WpfTestSvgControl/MainWindow.xaml.cs
@@ -0,0 +1,1095 @@
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Diagnostics;
+using System.ComponentModel;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Input;
+using System.Windows.Shapes;
+using System.Windows.Resources;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+
+using Microsoft.Win32;
+
+using SharpVectors.Converters;
+using SharpVectors.Renderers.Wpf;
+
+using IoPath = System.IO.Path;
+
+namespace WpfTestSvgControl
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ #region Private Fields
+
+ private const int LeftPane = 350;
+ private const int LeftBottomPane = 300;
+
+ private const string AppTitle = "SharpVectors: WPF Testing SVG";
+ private const string AppErrorTitle = "SharpVectors: WPF Testing SVG - Error";
+ private const string SvgTestSettings = "SvgTestSettings.xml";
+
+ private const string SvgFilePattern = "*.svg*";
+
+ private delegate void FileChangedToUIThread(FileSystemEventArgs e);
+
+ private bool _leftSplitterChanging;
+ private bool _isBottomSplitterChanging;
+
+ private string _drawingDir;
+
+ private bool _isShown;
+ private bool _canDeleteXaml;
+
+ private string _testSettingsPath;
+ private string _svgFilePath;
+ private string _xamlFilePath;
+
+ private SvgPage _svgPage;
+ private XamlPage _xamlPage;
+ private DrawingPage _drawingPage;
+ private DebugPage _debugPage;
+ private SettingsPage _settingsPage;
+
+ private ImageSource _folderClose;
+ private ImageSource _folderOpen;
+ private ImageSource _fileThumbnail;
+
+ private OptionSettings _optionSettings;
+
+ #endregion
+
+ #region Constructors and Destructor
+
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ leftExpander.Expanded += OnLeftExpanderExpanded;
+ leftExpander.Collapsed += OnLeftExpanderCollapsed;
+ leftSplitter.MouseMove += OnLeftSplitterMove;
+
+ bottomExpander.Expanded += OnBottomExpanderExpanded;
+ bottomExpander.Collapsed += OnBottomExpanderCollapsed;
+ bottomSplitter.MouseMove += OnBottomSplitterMove;
+
+ this.Loaded += OnWindowLoaded;
+ this.Unloaded += OnWindowUnloaded;
+ this.Closing += OnWindowClosing;
+
+ _drawingDir = IoPath.Combine(IoPath.GetDirectoryName(
+ System.Reflection.Assembly.GetExecutingAssembly().Location), DrawingPage.TemporalDirName);
+
+ if (!Directory.Exists(_drawingDir))
+ {
+ Directory.CreateDirectory(_drawingDir);
+ }
+
+ _optionSettings = new OptionSettings();
+ _testSettingsPath = IoPath.GetFullPath(SvgTestSettings);
+ if (!string.IsNullOrWhiteSpace(_testSettingsPath) && File.Exists(_testSettingsPath))
+ {
+ _optionSettings.Load(_testSettingsPath);
+ // Override any saved local directory, default to sample files.
+ _optionSettings.CurrentSvgPath = _optionSettings.DefaultSvgPath;
+ }
+
+ _optionSettings.PropertyChanged += OnSettingsPropertyChanged;
+
+ try
+ {
+ _folderClose = this.GetImage(new Uri("Images/FolderClose.svg", UriKind.Relative));
+ _folderOpen = this.GetImage(new Uri("Images/FolderOpen.svg", UriKind.Relative));
+ _fileThumbnail = this.GetImage(new Uri("Images/SvgLogoBasic.svg", UriKind.Relative));
+ }
+ catch (Exception ex)
+ {
+ _folderClose = null;
+ _folderOpen = null;
+
+ MessageBox.Show(ex.ToString(), AppErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ public OptionSettings OptionSettings
+ {
+ get {
+ return _optionSettings;
+ }
+ set {
+ if (value != null)
+ {
+ _optionSettings = value;
+ if (_drawingPage != null)
+ {
+ _drawingPage.ConversionSettings = value.ConversionSettings;
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ public async Task BrowseForFile()
+ {
+ OpenFileDialog dlg = new OpenFileDialog();
+ dlg.Multiselect = false;
+ dlg.Title = "Select An SVG File";
+ dlg.DefaultExt = "*.svg";
+ dlg.Filter = "All SVG Files (*.svg,*.svgz)|*.svg;*.svgz"
+ + "|Svg Uncompressed Files (*.svg)|*.svg"
+ + "|SVG Compressed Files (*.svgz)|*.svgz";
+
+ bool? isSelected = dlg.ShowDialog();
+
+ if (isSelected != null && isSelected.Value)
+ {
+ this.CloseFile();
+
+ await this.LoadFile(dlg.FileName);
+
+ TreeViewItem selItem = treeView.SelectedItem as TreeViewItem;
+ if (selItem == null || selItem.Tag == null)
+ {
+ return;
+ }
+ selItem.IsSelected = false;
+ }
+ }
+
+ #endregion
+
+ #region Protected Methods
+
+ protected override void OnInitialized(EventArgs e)
+ {
+ base.OnInitialized(e);
+
+ double width = SystemParameters.PrimaryScreenWidth;
+ double height = SystemParameters.PrimaryScreenHeight;
+
+ this.Width = Math.Min(1600, width) * 0.85;
+ this.Height = height * 0.85;
+
+ this.Left = (width - this.Width) / 2.0;
+ this.Top = (height - this.Height) / 2.0;
+
+ this.WindowStartupLocation = WindowStartupLocation.Manual;
+
+ ColumnDefinition colExpander = mainGrid.ColumnDefinitions[0];
+ colExpander.Width = new GridLength(LeftPane, GridUnitType.Pixel);
+
+ RowDefinition rowExpander = bottomGrid.RowDefinitions[2];
+ rowExpander.Height = new GridLength(LeftBottomPane, GridUnitType.Pixel);
+ }
+
+ protected override void OnContentRendered(EventArgs e)
+ {
+ base.OnContentRendered(e);
+
+ if (_isShown)
+ return;
+
+ _isShown = true;
+ }
+
+ #endregion
+
+ #region Private Event Handlers
+
+ private void OnWindowLoaded(object sender, RoutedEventArgs e)
+ {
+ bottomExpander.IsExpanded = true;
+ leftExpander.IsExpanded = true;
+
+ // Retrieve the display pages...
+ _svgPage = frameSvgInput.Content as SvgPage;
+ _xamlPage = frameXamlOutput.Content as XamlPage;
+ _drawingPage = frameDrawing.Content as DrawingPage;
+ _debugPage = frameDebugging.Content as DebugPage;
+ _settingsPage = frameSettings.Content as SettingsPage;
+
+ if (_svgPage != null)
+ {
+ _svgPage.MainWindow = this;
+ }
+ if (_xamlPage != null)
+ {
+ _xamlPage.MainWindow = this;
+ }
+ if (_drawingPage != null)
+ {
+ _drawingPage.WorkingDrawingDir = _drawingDir;
+ _drawingPage.MainWindow = this;
+ }
+ if (_debugPage != null)
+ {
+ _debugPage.MainWindow = this;
+ _debugPage.Startup();
+ }
+ if (_settingsPage != null)
+ {
+ _settingsPage.MainWindow = this;
+ }
+
+ tabSvgInput.Visibility = _optionSettings.ShowInputFile ? Visibility.Visible : Visibility.Collapsed;
+ tabXamlOutput.Visibility = _optionSettings.ShowOutputFile ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ private void OnWindowUnloaded(object sender, RoutedEventArgs e)
+ {
+ }
+
+ private void OnWindowClosing(object sender, CancelEventArgs e)
+ {
+ string backupFile = null;
+ if (File.Exists(_testSettingsPath))
+ {
+ backupFile = IoPath.ChangeExtension(_testSettingsPath, SvgConverter.BackupExt);
+ try
+ {
+ if (File.Exists(backupFile))
+ {
+ File.Delete(backupFile);
+ }
+ File.Move(_testSettingsPath, backupFile);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.ToString(), AppErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error);
+
+ return;
+ }
+ }
+ try
+ {
+ _optionSettings.Save(_testSettingsPath);
+ }
+ catch (Exception ex)
+ {
+ if (File.Exists(backupFile))
+ {
+ File.Move(backupFile, _testSettingsPath);
+ }
+
+ MessageBox.Show(ex.ToString(), AppErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ if (!string.IsNullOrWhiteSpace(backupFile) && File.Exists(backupFile))
+ {
+ File.Delete(backupFile);
+ }
+
+ try
+ {
+ if (_canDeleteXaml && !string.IsNullOrWhiteSpace(_xamlFilePath) && File.Exists(_xamlFilePath))
+ {
+ File.Delete(_xamlFilePath);
+ }
+ if (!string.IsNullOrWhiteSpace(_drawingDir) && Directory.Exists(_drawingDir))
+ {
+ string[] imageFiles = Directory.GetFiles(_drawingDir, "*.png");
+ if (imageFiles != null && imageFiles.Length != 0)
+ {
+ foreach (var imageFile in imageFiles)
+ {
+ File.Delete(imageFile);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Trace.TraceError(ex.ToString());
+ }
+
+ if (_debugPage != null)
+ {
+ _debugPage.Shutdown();
+ }
+ }
+
+ private async void OnBrowseForSvgFile(object sender, RoutedEventArgs e)
+ {
+ await this.BrowseForFile();
+ }
+
+ private void OnSettingsPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (!this.IsLoaded)
+ {
+ return;
+ }
+
+ var changedProp = e.PropertyName;
+ if (string.IsNullOrWhiteSpace(changedProp))
+ {
+ return;
+ }
+
+ if (string.Equals(changedProp, "ShowInputFile", StringComparison.OrdinalIgnoreCase))
+ {
+ this.OnFillSvgInputChecked();
+ }
+ else if (string.Equals(changedProp, "ShowOutputFile", StringComparison.OrdinalIgnoreCase))
+ {
+ this.OnFillXamlOutputChecked();
+ }
+ }
+
+ private void OnFillSvgInputChecked()
+ {
+ if (_svgPage == null)
+ {
+ tabSvgInput.Visibility = _optionSettings.ShowInputFile ? Visibility.Visible : Visibility.Collapsed;
+ return;
+ }
+
+ Cursor saveCursor = this.Cursor;
+
+ try
+ {
+ if (_optionSettings.ShowInputFile)
+ {
+ this.Cursor = Cursors.Wait;
+ this.ForceCursor = true;
+
+ if (File.Exists(_svgFilePath))
+ {
+ _svgPage.LoadDocument(_svgFilePath);
+ }
+ else
+ {
+ _svgPage.UnloadDocument();
+ }
+ }
+ else
+ {
+ _svgPage.UnloadDocument();
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.ToString(), AppErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ finally
+ {
+ this.Cursor = saveCursor;
+ this.ForceCursor = false;
+
+ tabSvgInput.Visibility = _optionSettings.ShowInputFile ? Visibility.Visible : Visibility.Collapsed;
+ }
+ }
+
+ private void OnFillXamlOutputChecked()
+ {
+ if (_xamlPage == null || string.IsNullOrWhiteSpace(_xamlFilePath))
+ {
+ tabXamlOutput.Visibility = _optionSettings.ShowOutputFile ? Visibility.Visible : Visibility.Collapsed;
+ return;
+ }
+
+ Cursor saveCursor = this.Cursor;
+
+ try
+ {
+ if (_optionSettings.ShowOutputFile)
+ {
+ this.Cursor = Cursors.Wait;
+ this.ForceCursor = true;
+
+ if (!File.Exists(_xamlFilePath))
+ {
+ if (!_drawingPage.SaveDocument(_xamlFilePath))
+ {
+ return;
+ }
+ }
+
+ if (File.Exists(_xamlFilePath))
+ {
+ _xamlPage.LoadDocument(_xamlFilePath);
+ }
+ else
+ {
+ _xamlPage.UnloadDocument();
+ }
+ }
+ else
+ {
+ _xamlPage.UnloadDocument();
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.ToString(), AppErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ finally
+ {
+ this.Cursor = saveCursor;
+ this.ForceCursor = false;
+
+ tabXamlOutput.Visibility = _optionSettings.ShowOutputFile ? Visibility.Visible : Visibility.Collapsed;
+ }
+ }
+
+ private void OnTabItemGotFocus(object sender, RoutedEventArgs e)
+ {
+ if (sender == tabDrawing)
+ {
+ if (_drawingPage != null)
+ {
+ _drawingPage.PageSelected(true);
+ }
+ }
+ else if (sender == tabXamlOutput)
+ {
+ if (_xamlPage != null)
+ {
+ _xamlPage.PageSelected(true);
+ }
+ }
+ else if (sender == tabSvgInput)
+ {
+ if (_svgPage != null)
+ {
+ _svgPage.PageSelected(true);
+ }
+ }
+ else if (sender == tabSettings)
+ {
+ if (_settingsPage != null)
+ {
+ _settingsPage.PageSelected(true);
+ }
+ }
+ else if (sender == tabDebugging)
+ {
+ if (_debugPage != null)
+ {
+ _debugPage.PageSelected(true);
+ }
+ }
+ }
+
+ #endregion
+
+ #region TreeView Event Handlers
+
+ private void OnTreeViewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs