From b1ca5f17946af7e8202e6770b6c9c2e21e195b67 Mon Sep 17 00:00:00 2001 From: paulushub Date: Sat, 28 Sep 2019 22:55:27 +0900 Subject: [PATCH] More work on WpfDrawingDocument - Improved the WpfDrawingDrawing object implementation - Added support for hit-testing by point and rectangle - Added a sample, WpfTestSvgControl, for testing this feature - Modified the codes to make the SetId value same as the SVG ID - This is the first step in support for interactivities and resolve the issue #106 --- Output/SharpVectors.Core.dll | Bin 118784 -> 110592 bytes Output/SharpVectors.Dom.dll | Bin 49152 -> 45056 bytes Output/SharpVectors.Runtime.Wpf.dll | Bin 52224 -> 47616 bytes Samples/WpfTestSvgControl/App.ico | Bin 0 -> 176188 bytes Samples/WpfTestSvgControl/App.xaml | 562 +++++++ Samples/WpfTestSvgControl/App.xaml.cs | 12 + Samples/WpfTestSvgControl/DebugPage.xaml | 14 + Samples/WpfTestSvgControl/DebugPage.xaml.cs | 53 + .../WpfTestSvgControl/DrawingHelpWindow.xaml | 25 + .../DrawingHelpWindow.xaml.cs | 27 + Samples/WpfTestSvgControl/DrawingPage.xaml | 169 ++ Samples/WpfTestSvgControl/DrawingPage.xaml.cs | 1403 +++++++++++++++++ .../WpfTestSvgControl/DrawingPageHelp.xaml | 78 + Samples/WpfTestSvgControl/GridExpander.cs | 625 ++++++++ Samples/WpfTestSvgControl/Images/Copy.svg | 1 + Samples/WpfTestSvgControl/Images/Cut.svg | 1 + Samples/WpfTestSvgControl/Images/Debug.svg | 1 + Samples/WpfTestSvgControl/Images/Delete.svg | 1 + Samples/WpfTestSvgControl/Images/Find.svg | 1 + .../WpfTestSvgControl/Images/FolderClose.svg | 1 + .../WpfTestSvgControl/Images/FolderOpen.svg | 1 + Samples/WpfTestSvgControl/Images/Format.svg | 1 + .../WpfTestSvgControl/Images/Information.svg | 1 + Samples/WpfTestSvgControl/Images/Number.svg | 1 + Samples/WpfTestSvgControl/Images/Open.svg | 1 + .../WpfTestSvgControl/Images/OpenFolder.svg | 1 + Samples/WpfTestSvgControl/Images/Output.svg | 22 + Samples/WpfTestSvgControl/Images/Panning.svg | 1 + Samples/WpfTestSvgControl/Images/Paste.svg | 1 + Samples/WpfTestSvgControl/Images/Print.svg | 1 + .../WpfTestSvgControl/Images/PrintPreview.svg | 1 + Samples/WpfTestSvgControl/Images/Redo.svg | 1 + Samples/WpfTestSvgControl/Images/Run.svg | 1 + Samples/WpfTestSvgControl/Images/Save.svg | 1 + Samples/WpfTestSvgControl/Images/Settings.svg | 1 + Samples/WpfTestSvgControl/Images/Space.svg | 1 + Samples/WpfTestSvgControl/Images/SvgLogo.svg | 64 + .../WpfTestSvgControl/Images/SvgLogoBasic.svg | 50 + Samples/WpfTestSvgControl/Images/Test.svg | 1 + .../Images/TestResultDetail.svg | 1 + .../WpfTestSvgControl/Images/TestRunner.svg | 1 + Samples/WpfTestSvgControl/Images/Undo.svg | 1 + Samples/WpfTestSvgControl/Images/View.svg | 1 + Samples/WpfTestSvgControl/Images/Web.svg | 1 + Samples/WpfTestSvgControl/Images/WordWrap.svg | 1 + Samples/WpfTestSvgControl/Images/ZoomIn.svg | 1 + Samples/WpfTestSvgControl/Images/ZoomOut.svg | 1 + .../WpfTestSvgControl/Images/ZoomReset.svg | 1 + .../WpfTestSvgControl/Images/ZoomToFit.svg | 1 + Samples/WpfTestSvgControl/MainWindow.xaml | 149 ++ Samples/WpfTestSvgControl/MainWindow.xaml.cs | 1095 +++++++++++++ .../WpfTestSvgControl/MainWindowSettings.cs | 304 ++++ Samples/WpfTestSvgControl/OptionSettings.cs | 624 ++++++++ .../WpfTestSvgControl/PrintPreviewWindow.xaml | 8 + .../PrintPreviewWindow.xaml.cs | 165 ++ .../Properties/AssemblyInfo.cs | 55 + .../Properties/Resources.Designer.cs | 83 + .../Properties/Resources.resx | 127 ++ .../Properties/Settings.Designer.cs | 26 + .../Properties/Settings.settings | 7 + .../WpfTestSvgControl/Resources/PanTool.cur | Bin 0 -> 326 bytes .../Resources/PanToolDown.cur | Bin 0 -> 326 bytes Samples/WpfTestSvgControl/SettingsPage.xaml | 140 ++ .../WpfTestSvgControl/SettingsPage.xaml.cs | 261 +++ Samples/WpfTestSvgControl/SvgPage.xaml | 78 + Samples/WpfTestSvgControl/SvgPage.xaml.cs | 411 +++++ Samples/WpfTestSvgControl/TraceDocument.xaml | 26 + .../WpfTestSvgControl/TraceDocument.xaml.cs | 109 ++ Samples/WpfTestSvgControl/TraceTextSource.cs | 56 + .../WpfTestSvgControl.csproj | 333 ++++ Samples/WpfTestSvgControl/XamlPage.xaml | 76 + Samples/WpfTestSvgControl/XamlPage.xaml.cs | 419 +++++ .../ZoomPanDimensionConverter.cs | 41 + .../ZoomPanMouseHandlingMode.cs | 32 + .../WpfTestSvgControl/ZoomPanOverview.xaml | 9 + .../WpfTestSvgControl/ZoomPanOverview.xaml.cs | 339 ++++ .../ZoomPanScaleConverter.cs | 39 + Samples/WpfTestSvgControl/app.config | 3 + Samples/WpfTestSvgSample/DrawingPage.xaml.cs | 360 +++-- Samples/WpfTestSvgSample/MainWindow.xaml.cs | 15 +- .../ZoomPanMouseHandlingMode.cs | 4 + SharpVectorsAll.sln | 9 + .../EmbeddedImageSerializerVisitor.cs | 145 +- .../SharpVectorConvertersWpf/FileSvgReader.cs | 9 +- .../SharpVectorConvertersWpf/SvgConverter.cs | 10 + .../SharpVectorRenderingGdi/SvgAlertArgs.cs | 2 +- .../SharpVectorRenderingGdi/SvgErrorArgs.cs | 4 +- .../SharpVectors.Rendering.Wpf.csproj | 1 + .../Wpf/WpfDrawingContext.cs | 13 + .../Wpf/WpfDrawingDocument.cs | 128 +- .../Wpf/WpfHitTestResult.cs | 83 + .../Wpf/WpfImageRendering.cs | 10 +- .../Wpf/WpfRenderingBase.cs | 6 +- .../Wpf/WpfSvgRendering.cs | 10 +- .../EmbeddedBitmapSource.cs | 27 + .../SharpVectorRuntimeWpf/SvgDrawingCanvas.cs | 2 +- Source/SharpVectorRuntimeWpf/SvgObject.cs | 14 + 97 files changed, 8835 insertions(+), 168 deletions(-) create mode 100644 Samples/WpfTestSvgControl/App.ico create mode 100644 Samples/WpfTestSvgControl/App.xaml create mode 100644 Samples/WpfTestSvgControl/App.xaml.cs create mode 100644 Samples/WpfTestSvgControl/DebugPage.xaml create mode 100644 Samples/WpfTestSvgControl/DebugPage.xaml.cs create mode 100644 Samples/WpfTestSvgControl/DrawingHelpWindow.xaml create mode 100644 Samples/WpfTestSvgControl/DrawingHelpWindow.xaml.cs create mode 100644 Samples/WpfTestSvgControl/DrawingPage.xaml create mode 100644 Samples/WpfTestSvgControl/DrawingPage.xaml.cs create mode 100644 Samples/WpfTestSvgControl/DrawingPageHelp.xaml create mode 100644 Samples/WpfTestSvgControl/GridExpander.cs create mode 100644 Samples/WpfTestSvgControl/Images/Copy.svg create mode 100644 Samples/WpfTestSvgControl/Images/Cut.svg create mode 100644 Samples/WpfTestSvgControl/Images/Debug.svg create mode 100644 Samples/WpfTestSvgControl/Images/Delete.svg create mode 100644 Samples/WpfTestSvgControl/Images/Find.svg create mode 100644 Samples/WpfTestSvgControl/Images/FolderClose.svg create mode 100644 Samples/WpfTestSvgControl/Images/FolderOpen.svg create mode 100644 Samples/WpfTestSvgControl/Images/Format.svg create mode 100644 Samples/WpfTestSvgControl/Images/Information.svg create mode 100644 Samples/WpfTestSvgControl/Images/Number.svg create mode 100644 Samples/WpfTestSvgControl/Images/Open.svg create mode 100644 Samples/WpfTestSvgControl/Images/OpenFolder.svg create mode 100644 Samples/WpfTestSvgControl/Images/Output.svg create mode 100644 Samples/WpfTestSvgControl/Images/Panning.svg create mode 100644 Samples/WpfTestSvgControl/Images/Paste.svg create mode 100644 Samples/WpfTestSvgControl/Images/Print.svg create mode 100644 Samples/WpfTestSvgControl/Images/PrintPreview.svg create mode 100644 Samples/WpfTestSvgControl/Images/Redo.svg create mode 100644 Samples/WpfTestSvgControl/Images/Run.svg create mode 100644 Samples/WpfTestSvgControl/Images/Save.svg create mode 100644 Samples/WpfTestSvgControl/Images/Settings.svg create mode 100644 Samples/WpfTestSvgControl/Images/Space.svg create mode 100644 Samples/WpfTestSvgControl/Images/SvgLogo.svg create mode 100644 Samples/WpfTestSvgControl/Images/SvgLogoBasic.svg create mode 100644 Samples/WpfTestSvgControl/Images/Test.svg create mode 100644 Samples/WpfTestSvgControl/Images/TestResultDetail.svg create mode 100644 Samples/WpfTestSvgControl/Images/TestRunner.svg create mode 100644 Samples/WpfTestSvgControl/Images/Undo.svg create mode 100644 Samples/WpfTestSvgControl/Images/View.svg create mode 100644 Samples/WpfTestSvgControl/Images/Web.svg create mode 100644 Samples/WpfTestSvgControl/Images/WordWrap.svg create mode 100644 Samples/WpfTestSvgControl/Images/ZoomIn.svg create mode 100644 Samples/WpfTestSvgControl/Images/ZoomOut.svg create mode 100644 Samples/WpfTestSvgControl/Images/ZoomReset.svg create mode 100644 Samples/WpfTestSvgControl/Images/ZoomToFit.svg create mode 100644 Samples/WpfTestSvgControl/MainWindow.xaml create mode 100644 Samples/WpfTestSvgControl/MainWindow.xaml.cs create mode 100644 Samples/WpfTestSvgControl/MainWindowSettings.cs create mode 100644 Samples/WpfTestSvgControl/OptionSettings.cs create mode 100644 Samples/WpfTestSvgControl/PrintPreviewWindow.xaml create mode 100644 Samples/WpfTestSvgControl/PrintPreviewWindow.xaml.cs create mode 100644 Samples/WpfTestSvgControl/Properties/AssemblyInfo.cs create mode 100644 Samples/WpfTestSvgControl/Properties/Resources.Designer.cs create mode 100644 Samples/WpfTestSvgControl/Properties/Resources.resx create mode 100644 Samples/WpfTestSvgControl/Properties/Settings.Designer.cs create mode 100644 Samples/WpfTestSvgControl/Properties/Settings.settings create mode 100644 Samples/WpfTestSvgControl/Resources/PanTool.cur create mode 100644 Samples/WpfTestSvgControl/Resources/PanToolDown.cur create mode 100644 Samples/WpfTestSvgControl/SettingsPage.xaml create mode 100644 Samples/WpfTestSvgControl/SettingsPage.xaml.cs create mode 100644 Samples/WpfTestSvgControl/SvgPage.xaml create mode 100644 Samples/WpfTestSvgControl/SvgPage.xaml.cs create mode 100644 Samples/WpfTestSvgControl/TraceDocument.xaml create mode 100644 Samples/WpfTestSvgControl/TraceDocument.xaml.cs create mode 100644 Samples/WpfTestSvgControl/TraceTextSource.cs create mode 100644 Samples/WpfTestSvgControl/WpfTestSvgControl.csproj create mode 100644 Samples/WpfTestSvgControl/XamlPage.xaml create mode 100644 Samples/WpfTestSvgControl/XamlPage.xaml.cs create mode 100644 Samples/WpfTestSvgControl/ZoomPanDimensionConverter.cs create mode 100644 Samples/WpfTestSvgControl/ZoomPanMouseHandlingMode.cs create mode 100644 Samples/WpfTestSvgControl/ZoomPanOverview.xaml create mode 100644 Samples/WpfTestSvgControl/ZoomPanOverview.xaml.cs create mode 100644 Samples/WpfTestSvgControl/ZoomPanScaleConverter.cs create mode 100644 Samples/WpfTestSvgControl/app.config create mode 100644 Source/SharpVectorRenderingWpf/Wpf/WpfHitTestResult.cs diff --git a/Output/SharpVectors.Core.dll b/Output/SharpVectors.Core.dll index 3f7754ab47858363b8de2c607f7a00598e9b0475..6913e4781874325ee3a2f3e72f6b80078ce44b39 100644 GIT binary patch literal 110592 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̈~;z+8Rp)w5BRc51W2xY5{|_Ng3P=ONdT=XG&Vz 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%0AJkBKW|-1%8!5eV5T!qkr?jb((jz#X)RnHEXKAaK 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

(rh9%lp*M8E`}Jh*&&xW3Em!tV~kn(;# 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 diff --git a/Output/SharpVectors.Dom.dll b/Output/SharpVectors.Dom.dll index f8e5d215409ae2d8085a0867c7e478db0d17aa58..eee72c822f4b91cb4c3c9e9c4e84f75e5f7021c7 100644 GIT binary patch delta 12811 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 NBmyH5_=iN`{{YRMJY)a> diff --git a/Output/SharpVectors.Runtime.Wpf.dll b/Output/SharpVectors.Runtime.Wpf.dll index 21b904896a355beade395045f9091da21e6f3253..e330df52b7fd27d992926f37b0f3755d25c00ccd 100644 GIT binary patch literal 47616 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 literal 52224 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 lew0gI + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + SVG Logo + Designed for the SVG Logo Contest in 2006 by Harvey Rayner, and adopted by W3C in 2009. It is available under the Creative Commons license for those who have an SVG product or who are using SVG on their site. + + + + + SVG Logo + 14-08-2009 + + W3C + Harvey Rayner, designer + + See document description + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + SVG Logo + Designed for the SVG Logo Contest in 2006 by Harvey Rayner, and adopted by W3C in 2009. It is available under the Creative Commons license for those who have an SVG product or who are using SVG on their site. + + + + + SVG Logo + 14-08-2009 + + W3C + Harvey Rayner, designer + + See document description + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + 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 e) + { + } + + private void OnTreeViewItemSelected(object sender, RoutedEventArgs e) + { + TreeViewItem selItem = treeView.SelectedItem as TreeViewItem; + if (selItem == null || selItem.Tag == null) + { + return; + } + + string selectedName = selItem.Tag as string; + if (string.IsNullOrWhiteSpace(selectedName)) + { + return; + } + + e.Handled = true; + + treeView.IsEnabled = false; + + try + { + this.Cursor = Cursors.Wait; + this.ForceCursor = true; + + _drawingPage.SelectElement(selectedName); + } + catch (Exception ex) + { + MessageBox.Show(ex.ToString(), AppErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); + } + finally + { + this.Cursor = Cursors.Arrow; + this.ForceCursor = false; + + treeView.IsEnabled = true; + treeView.Focus(); + } + } + + private void OnTreeViewItemUnselected(object sender, RoutedEventArgs e) + { + } + + private void OnTreeViewItemCollapsed(object sender, RoutedEventArgs e) + { + if (_folderClose == null) + { + return; + } + + TreeViewItem treeItem = e.OriginalSource as TreeViewItem; + if (treeItem == null) + { + return; + } + + BulletDecorator decorator = treeItem.Header as BulletDecorator; + if (decorator == null) + { + return; + } + Image headerImage = decorator.Bullet as Image; + if (headerImage == null) + { + return; + } + headerImage.Source = _folderClose; + + e.Handled = true; + } + + private void OnTreeViewItemExpanded(object sender, RoutedEventArgs e) + { + if (_folderOpen == null) + { + return; + } + + TreeViewItem treeItem = e.OriginalSource as TreeViewItem; + if (treeItem == null) + { + return; + } + + BulletDecorator decorator = treeItem.Header as BulletDecorator; + if (decorator == null) + { + return; + } + Image headerImage = decorator.Bullet as Image; + if (headerImage == null) + { + return; + } + headerImage.Source = _folderOpen; + + e.Handled = true; + } + + #endregion + + #region Drag/Drop Methods + + private void OnDragEnter(object sender, DragEventArgs de) + { + if (de.Data.GetDataPresent(DataFormats.Text) || + de.Data.GetDataPresent(DataFormats.FileDrop)) + { + de.Effects = DragDropEffects.Copy; + } + else + { + de.Effects = DragDropEffects.None; + } + } + + private void OnDragLeave(object sender, DragEventArgs e) + { + + } + + private async void OnDragDrop(object sender, DragEventArgs de) + { + string fileName = ""; + if (de.Data.GetDataPresent(DataFormats.Text)) + { + fileName = (string)de.Data.GetData(DataFormats.Text); + } + else if (de.Data.GetDataPresent(DataFormats.FileDrop)) + { + string[] fileNames; + fileNames = (string[])de.Data.GetData(DataFormats.FileDrop); + fileName = fileNames[0]; + } + + if (!string.IsNullOrWhiteSpace(fileName)) + { + } + if (string.IsNullOrWhiteSpace(fileName) || !File.Exists(fileName)) + { + return; + } + + this.CloseFile(); + + try + { + this.Cursor = Cursors.Wait; + this.ForceCursor = true; + + await this.LoadFile(fileName); + } + catch (Exception ex) + { + MessageBox.Show(ex.ToString(), AppErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); + } + finally + { + this.Cursor = Cursors.Arrow; + this.ForceCursor = false; + } + } + + #endregion + + #region LeftExpander/Splitter Event Handlers + + private void OnLeftExpanderCollapsed(object sender, RoutedEventArgs e) + { + if (_leftSplitterChanging || _isBottomSplitterChanging) + { + return; + } + // Prevent WPF silly event routing... + if (e.Source != leftExpander) + { + return; + } + + e.Handled = true; + + ColumnDefinition columnDef = mainGrid.ColumnDefinitions[0]; + columnDef.Width = new GridLength(24, GridUnitType.Pixel); + } + + private void OnLeftExpanderExpanded(object sender, RoutedEventArgs e) + { + if (_leftSplitterChanging || _isBottomSplitterChanging) + { + return; + } + // Prevent WPF silly event routing... + if (e.Source != leftExpander) + { + return; + } + + e.Handled = true; + + ColumnDefinition columnDef = mainGrid.ColumnDefinitions[0]; + columnDef.Width = new GridLength(LeftPane, GridUnitType.Pixel); + } + + private void OnLeftSplitterMove(object sender, MouseEventArgs e) + { + _leftSplitterChanging = true; + + ColumnDefinition columnDef = mainGrid.ColumnDefinitions[0]; + + leftExpander.IsExpanded = columnDef.ActualWidth > 30; + + _leftSplitterChanging = false; + + e.Handled = true; + } + + #endregion + + #region BottomExpander/Splitter Event Handlers + + private void OnBottomExpanderCollapsed(object sender, RoutedEventArgs e) + { + if (_isBottomSplitterChanging) + { + return; + } + // Prevent WPF silly event routing... + if (e.Source != bottomExpander) + { + return; + } + + RowDefinition rowDef = bottomGrid.RowDefinitions[2]; + rowDef.Height = new GridLength(24, GridUnitType.Pixel); + } + + private void OnBottomExpanderExpanded(object sender, RoutedEventArgs e) + { + if (_isBottomSplitterChanging) + { + return; + } + // Prevent WPF silly event routing... + if (e.Source != bottomExpander) + { + return; + } + + RowDefinition rowDef = bottomGrid.RowDefinitions[2]; + rowDef.Height = new GridLength(LeftBottomPane, GridUnitType.Pixel); + } + + private void OnBottomSplitterMove(object sender, MouseEventArgs e) + { + _isBottomSplitterChanging = true; + + RowDefinition rowDef = bottomGrid.RowDefinitions[2]; + + bottomExpander.IsExpanded = rowDef.ActualHeight > 30; + + _isBottomSplitterChanging = false; + } + + #endregion + + #region Private Methods + + /// + /// This converts the SVG resource specified by the Uri to . + /// + /// A specifying the source of the SVG resource. + /// A of the converted SVG resource. + private DrawingGroup GetDrawing(Uri svgSource) + { + WpfDrawingSettings settings = new WpfDrawingSettings(); + settings.IncludeRuntime = false; + settings.TextAsGeometry = true; + settings.OptimizePath = true; + + StreamResourceInfo svgStreamInfo = null; + if (svgSource.ToString().IndexOf("siteoforigin", StringComparison.OrdinalIgnoreCase) >= 0) + { + svgStreamInfo = Application.GetRemoteStream(svgSource); + } + else + { + svgStreamInfo = Application.GetResourceStream(svgSource); + } + + Stream svgStream = (svgStreamInfo != null) ? svgStreamInfo.Stream : null; + + if (svgStream != null) + { + string fileExt = IoPath.GetExtension(svgSource.ToString()); + bool isCompressed = !string.IsNullOrWhiteSpace(fileExt) && string.Equals( + fileExt, SvgConverter.CompressedSvgExt, StringComparison.OrdinalIgnoreCase); + + if (isCompressed) + { + using (svgStream) + { + using (var zipStream = new GZipStream(svgStream, CompressionMode.Decompress)) + { + using (FileSvgReader reader = new FileSvgReader(settings)) + { + DrawingGroup drawGroup = reader.Read(zipStream); + + if (drawGroup != null) + { + return drawGroup; + } + } + } + } + } + else + { + using (svgStream) + { + using (FileSvgReader reader = new FileSvgReader(settings)) + { + DrawingGroup drawGroup = reader.Read(svgStream); + + if (drawGroup != null) + { + return drawGroup; + } + } + } + } + } + + return null; + } + + /// + /// This converts the SVG resource specified by the Uri to . + /// + /// A specifying the source of the SVG resource. + /// A of the converted SVG resource. + /// + /// This uses the method to convert the SVG resource to , + /// which is then wrapped in . + /// + private DrawingImage GetImage(Uri svgSource) + { + DrawingGroup drawGroup = this.GetDrawing(svgSource); + if (drawGroup != null) + { + return new DrawingImage(drawGroup); + } + return null; + } + + private async Task LoadFile(string fileName) + { + string fileExt = IoPath.GetExtension(fileName); + if (string.IsNullOrWhiteSpace(fileExt)) + { + return; + } + + bool generateXaml = _optionSettings.ShowOutputFile; + + if (string.Equals(fileExt, SvgConverter.SvgExt, StringComparison.OrdinalIgnoreCase) || + string.Equals(fileExt, SvgConverter.CompressedSvgExt, StringComparison.OrdinalIgnoreCase)) + { + _svgFilePath = fileName; + + if (_svgPage != null && _optionSettings.ShowInputFile) + { + _svgPage.LoadDocument(fileName); + } + + if (_drawingPage == null) + { + return; + } + _drawingPage.SaveXaml = generateXaml; + + try + { + if (await _drawingPage.LoadDocumentAsync(fileName)) + { + this.Title = AppTitle + " - " + IoPath.GetFileName(fileName); + + if (_xamlPage != null && !string.IsNullOrWhiteSpace(_drawingDir)) + { + string xamlFilePath = IoPath.Combine(_drawingDir, + IoPath.GetFileNameWithoutExtension(fileName) + SvgConverter.XamlExt); + + _xamlFilePath = xamlFilePath; + _canDeleteXaml = true; + + if (File.Exists(xamlFilePath) && _optionSettings.ShowOutputFile) + { + _xamlPage.LoadDocument(xamlFilePath); + } + } + + var drawingDocument = _drawingPage.DrawingDocument; + + this.FillTreeView(drawingDocument); + } + } + catch + { + // Try loading the XAML, if generated but the rendering failed... + if (_xamlPage != null && !string.IsNullOrWhiteSpace(_drawingDir)) + { + string xamlFilePath = IoPath.Combine(_drawingDir, + IoPath.GetFileNameWithoutExtension(fileName) + SvgConverter.XamlExt); + + _xamlFilePath = xamlFilePath; + _canDeleteXaml = true; + + if (File.Exists(xamlFilePath) && _optionSettings.ShowOutputFile) + { + _xamlPage.LoadDocument(xamlFilePath); + } + } + throw; + } + } + } + + private void CloseFile() + { + try + { + if (_svgPage != null) + { + _svgPage.UnloadDocument(); + } + if (_xamlPage != null) + { + _xamlPage.UnloadDocument(); + } + if (_drawingPage != null) + { + _drawingPage.UnloadDocument(); + } + + 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) + { + try + { + foreach (var imageFile in imageFiles) + { + if (File.Exists(imageFile)) + { + File.Delete(imageFile); + } + } + } + catch (IOException ex) + { + Trace.TraceError(ex.ToString()); + // Image this, WPF will typically cache and/or lock loaded images + } + } + } + + _svgFilePath = null; + _xamlFilePath = null; + _canDeleteXaml = false; + } + catch (Exception ex) + { + Trace.TraceError(ex.ToString()); + } + } + + #region FillTreeView Methods + + private void FillTreeView(WpfDrawingDocument drawingDocument) + { + if (drawingDocument == null) + { + return; + } + + treeView.BeginInit(); + treeView.Items.Clear(); + + for (int i = 0; i < 1; i++) + { + TextBlock headerText = new TextBlock(); + headerText.Text = i == 0 ? "SVG Element Names" : "SVG Element Unique Names"; + headerText.Margin = new Thickness(3, 0, 0, 0); + + BulletDecorator decorator = new BulletDecorator(); + if (_folderClose != null) + { + Image image = new Image(); + image.Source = _folderClose; + + decorator.Bullet = image; + } + else + { + Ellipse bullet = new Ellipse(); + bullet.Height = 16; + bullet.Width = 16; + bullet.Fill = Brushes.Goldenrod; + bullet.Stroke = Brushes.DarkGray; + bullet.StrokeThickness = 1; + + decorator.Bullet = bullet; + } + decorator.Margin = new Thickness(0, 0, 10, 0); + decorator.Child = headerText; + + TreeViewItem categoryItem = new TreeViewItem(); + categoryItem.Tag = string.Empty; + categoryItem.Header = decorator; + categoryItem.Margin = new Thickness(0); + categoryItem.Padding = new Thickness(3); + categoryItem.FontSize = 14; + categoryItem.FontWeight = FontWeights.Bold; + + treeView.Items.Add(categoryItem); + + // FillTreeView(i == 0 ? drawingDocument.ElementNames : drawingDocument.ElementUniqueNames, categoryItem); + FillTreeView(i == 0 ? drawingDocument.DrawingNames : drawingDocument.DrawingUniqueNames, categoryItem); + + categoryItem.IsExpanded = (i == 0); + } + + treeView.EndInit(); + + leftExpander.IsExpanded = true; + bottomExpander.IsExpanded = true; + } + + private void FillTreeView(ICollection idItems, TreeViewItem treeItem) + { + if (idItems == null || idItems.Count == 0) + { + return; + } + + int itemCount = 0; + + foreach (var idItem in idItems) + { + TextBlock itemText = new TextBlock(); + itemText.Text = string.Format("({0:D3}) - {1}", itemCount, idItem); + itemText.Margin = new Thickness(3, 0, 0, 0); + + BulletDecorator fileItem = new BulletDecorator(); + if (_fileThumbnail != null) + { + Image image = new Image(); + image.Source = _fileThumbnail; + image.Height = 16; + image.Width = 16; + + fileItem.Bullet = image; + } + else + { + Ellipse bullet = new Ellipse(); + bullet.Height = 16; + bullet.Width = 16; + bullet.Fill = Brushes.Goldenrod; + bullet.Stroke = Brushes.DarkGray; + bullet.StrokeThickness = 1; + + fileItem.Bullet = bullet; + } + fileItem.Margin = new Thickness(0, 0, 10, 0); + fileItem.Child = itemText; + + TreeViewItem item = new TreeViewItem(); + item.Tag = idItem; + item.Header = fileItem; + item.Margin = new Thickness(0); + item.Padding = new Thickness(2); + item.FontSize = 12; + item.FontWeight = FontWeights.Normal; + + treeItem.Items.Add(item); + + itemCount++; + } + } + + #endregion + + #endregion + } +} diff --git a/Samples/WpfTestSvgControl/MainWindowSettings.cs b/Samples/WpfTestSvgControl/MainWindowSettings.cs new file mode 100644 index 000000000..27a4f6e97 --- /dev/null +++ b/Samples/WpfTestSvgControl/MainWindowSettings.cs @@ -0,0 +1,304 @@ +// The codes by Jake Ginnivan and licensed under MIT. +// Web Link: http://jake.ginnivan.net/remembering-wpf-window-positions +// + +using System; +using System.Diagnostics; +using System.Configuration; +using System.ComponentModel; +using System.Runtime.InteropServices; + +using System.Windows; +using System.Windows.Interop; + +namespace WpfTestSvgControl +{ + /// + /// Persists a Window's Size, Location and WindowState to UserScopeSettings + /// + public sealed class MainWindowSettings + { + [DllImport("user32.dll")] + private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl); + + [DllImport("user32.dll")] + private static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl); + + // ReSharper disable InconsistentNaming + private const int SW_SHOWNORMAL = 1; + private const int SW_SHOWMINIMIZED = 2; + // ReSharper restore InconsistentNaming + + private Window _window; + + private WindowApplicationSettings _windowApplicationSettings; + + public MainWindowSettings(Window window) + { + _window = window; + } + + /// + /// Register the "Save" attached property and the "OnSaveInvalidated" callback + /// + public static readonly DependencyProperty SaveProperty + = DependencyProperty.RegisterAttached("Save", typeof(bool), typeof(MainWindowSettings), + new FrameworkPropertyMetadata(new PropertyChangedCallback(OnSaveInvalidated))); + + + public static void SetSave(DependencyObject dependencyObject, bool enabled) + { + dependencyObject.SetValue(SaveProperty, enabled); + } + + /// + /// Called when Save is changed on an object. + /// + private static void OnSaveInvalidated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) + { + var window = dependencyObject as Window; + if (window == null || !((bool)e.NewValue)) + return; + var settings = new MainWindowSettings(window); + settings.Attach(); + } + + /// + /// Load the Window Size Location and State from the settings object + /// + private void LoadWindowState() + { + Settings.Reload(); + + if (Settings.Placement == null) + return; + try + { + // Load window placement details for previous application session from application settings + // if window was closed on a monitor that is now disconnected from the computer, + // SetWindowPlacement will place the window onto a visible monitor. + var wp = Settings.Placement.Value; + + wp.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT)); + wp.flags = 0; + wp.showCmd = (wp.showCmd == SW_SHOWMINIMIZED ? SW_SHOWNORMAL : wp.showCmd); + var hwnd = new WindowInteropHelper(_window).Handle; + SetWindowPlacement(hwnd, ref wp); + } + catch (Exception ex) + { + Trace.TraceError(ex.ToString()); + } + } + + /// + /// Save the Window Size, Location and State to the settings object + /// + private void SaveWindowState() + { + WINDOWPLACEMENT wp; + var hwnd = new WindowInteropHelper(_window).Handle; + GetWindowPlacement(hwnd, out wp); + Settings.Placement = wp; + Settings.Save(); + } + + private void Attach() + { + if (_window == null) + return; + _window.Closing += WindowClosing; + _window.SourceInitialized += WindowSourceInitialized; + } + + private void WindowSourceInitialized(object sender, EventArgs e) + { + LoadWindowState(); + } + + private void WindowClosing(object sender, CancelEventArgs e) + { + SaveWindowState(); + _window.Closing -= WindowClosing; + _window.SourceInitialized -= WindowSourceInitialized; + _window = null; + } + + internal WindowApplicationSettings CreateWindowApplicationSettingsInstance() + { + return new WindowApplicationSettings(this); + } + + [Browsable(false)] + internal WindowApplicationSettings Settings + { + get { + if (_windowApplicationSettings == null) + { + _windowApplicationSettings = CreateWindowApplicationSettingsInstance(); + } + return _windowApplicationSettings; + } + } + + internal class WindowApplicationSettings : ApplicationSettingsBase + { + public WindowApplicationSettings(MainWindowSettings windowSettings) + : base(windowSettings._window.GetType().FullName) + { + } + + [UserScopedSetting] + public WINDOWPLACEMENT? Placement + { + get { + if (this["Placement"] != null) + { + return ((WINDOWPLACEMENT)this["Placement"]); + } + return null; + } + set { + this["Placement"] = value; + } + } + } + } + + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + private int _left; + private int _top; + private int _right; + private int _bottom; + + public RECT(int left, int top, int right, int bottom) + { + _left = left; + _top = top; + _right = right; + _bottom = bottom; + } + + public override bool Equals(object obj) + { + if (obj is RECT) + { + var rect = (RECT)obj; + + return rect._bottom == _bottom && + rect._left == _left && + rect._right == _right && + rect._top == _top; + } + return base.Equals(obj); + } + + public override int GetHashCode() + { + return this.Bottom.GetHashCode() ^ this.Left.GetHashCode() ^ this.Right.GetHashCode() ^ this.Top.GetHashCode(); + } + + public static bool operator ==(RECT a, RECT b) + { + return a._bottom == b._bottom && + a._left == b._left && + a._right == b._right && + a._top == b._top; + } + + public static bool operator !=(RECT a, RECT b) + { + return !(a == b); + } + + public int Left + { + get { return _left; } + set { _left = value; } + } + + public int Top + { + get { return _top; } + set { _top = value; } + } + + public int Right + { + get { return _right; } + set { _right = value; } + } + + public int Bottom + { + get { return _bottom; } + set { _bottom = value; } + } + } + + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct POINT + { + private int _x; + private int _y; + + public POINT(int x, int y) + { + _x = x; + _y = y; + } + + public int X + { + get { return _x; } + set { _x = value; } + } + + public int Y + { + get { return _y; } + set { _y = value; } + } + + public override bool Equals(object obj) + { + if (obj is POINT) + { + var point = (POINT)obj; + + return point._x == _x && point._y == _y; + } + return base.Equals(obj); + } + public override int GetHashCode() + { + return this.X.GetHashCode() ^ this.Y.GetHashCode(); + } + + public static bool operator ==(POINT a, POINT b) + { + return a._x == b._x && a._y == b._y; + } + + public static bool operator !=(POINT a, POINT b) + { + return !(a == b); + } + } + + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct WINDOWPLACEMENT + { + public int length; + public int flags; + public int showCmd; + public POINT minPosition; + public POINT maxPosition; + public RECT normalPosition; + } +} diff --git a/Samples/WpfTestSvgControl/OptionSettings.cs b/Samples/WpfTestSvgControl/OptionSettings.cs new file mode 100644 index 000000000..22dc13cab --- /dev/null +++ b/Samples/WpfTestSvgControl/OptionSettings.cs @@ -0,0 +1,624 @@ +using System; +using System.IO; +using System.Xml; +using System.Text; +using System.Linq; +using System.ComponentModel; +using System.Runtime.InteropServices; + +using SharpVectors.Renderers.Wpf; + +namespace WpfTestSvgControl +{ + [Serializable] + public sealed class OptionSettings : INotifyPropertyChanged, ICloneable + { + #region Private Interop Methods + + [DllImport("shell32.dll", SetLastError = true)] + private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, + uint cidl, [In, MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, uint dwFlags); + + [DllImport("shell32.dll", SetLastError = true)] + private static extern void SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name, + IntPtr bindingContext, [Out] out IntPtr pidl, uint sfgaoIn, [Out] out uint psfgaoOut); + + #endregion + + #region Public Events + + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + #region Private Fields + + private const string ParentSymbol = "..\\"; + private const string SharpVectors = "SharpVectors"; + + [DllImport("Shlwapi.dll", EntryPoint = "PathIsDirectoryEmpty")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool IsDirectoryEmpty([MarshalAs(UnmanagedType.LPStr)]string directory); + + private bool _hidePathsRoot; + private bool _showInputFile; + private bool _showOutputFile; + private bool _recursiveSearch; + + private string _defaultSvgPath; + private string _currentSvgPath; + + private string _selectedValuePath; + + private WpfDrawingSettings _wpfSettings; + + #endregion + + #region Constructors and Destructor + + public OptionSettings() + { + _wpfSettings = new WpfDrawingSettings(); + string currentDir = Path.GetFullPath(@".\Samples"); + if (!Directory.Exists(currentDir)) + { + Directory.CreateDirectory(currentDir); + } + _currentSvgPath = currentDir; + _defaultSvgPath = currentDir; + + _showInputFile = false; + _showOutputFile = false; + _recursiveSearch = true; + } + + public OptionSettings(WpfDrawingSettings wpfSettings, string testPath) + { + _wpfSettings = wpfSettings; + _currentSvgPath = testPath; + + _showInputFile = false; + _showOutputFile = false; + _recursiveSearch = true; + + if (wpfSettings == null) + { + _wpfSettings = new WpfDrawingSettings(); + } + if (string.IsNullOrWhiteSpace(testPath)) + { + string currentDir = Path.GetFullPath(@".\Samples"); + _currentSvgPath = currentDir; + } + if (!Directory.Exists(_currentSvgPath)) + { + Directory.CreateDirectory(_currentSvgPath); + } + _defaultSvgPath = _currentSvgPath; + } + + public OptionSettings(OptionSettings source) + { + if (source == null) + { + return; + } + _hidePathsRoot = source._hidePathsRoot; + _defaultSvgPath = source._defaultSvgPath; + _currentSvgPath = source._currentSvgPath; + _showInputFile = source._showInputFile; + _showOutputFile = source._showOutputFile; + _recursiveSearch = source._recursiveSearch; + _wpfSettings = source._wpfSettings; + } + + #endregion + + #region Public Properties + + public bool HidePathsRoot + { + get { + return _hidePathsRoot; + } + set { + bool isChanged = (_hidePathsRoot != value); + _hidePathsRoot = value; + + if (isChanged) + { + this.RaisePropertyChanged("HidePathsRoot"); + } + } + } + + public bool ShowInputFile + { + get { + return _showInputFile; + } + set { + bool isChanged = (_showInputFile != value); + _showInputFile = value; + + if (isChanged) + { + this.RaisePropertyChanged("ShowInputFile"); + } + } + } + + public bool ShowOutputFile + { + get { + return _showOutputFile; + } + set { + bool isChanged = (_showOutputFile != value); + _showOutputFile = value; + + if (isChanged) + { + this.RaisePropertyChanged("ShowOutputFile"); + } + } + } + + public bool RecursiveSearch + { + get { + return _recursiveSearch; + } + set { + bool isChanged = (_recursiveSearch != value); + _recursiveSearch = value; + + if (isChanged) + { + this.RaisePropertyChanged("RecursiveSearch"); + } + } + } + + public string DefaultSvgPath + { + get { + return _defaultSvgPath; + } + set { + bool isChanged = !string.Equals(_defaultSvgPath, value, StringComparison.OrdinalIgnoreCase); + _defaultSvgPath = value; + + if (isChanged) + { + this.RaisePropertyChanged("DefaultSvgPath"); + } + } + } + + public string CurrentSvgPath + { + get { + return _currentSvgPath; + } + set { + bool isChanged = !string.Equals(_currentSvgPath, value, StringComparison.OrdinalIgnoreCase); + _currentSvgPath = value; + + if (isChanged) + { + this.RaisePropertyChanged("CurrentSvgPath"); + } + } + } + + public string SelectedValuePath + { + get { + return _selectedValuePath; + } + set { + bool isChanged = !string.Equals(_defaultSvgPath, value, StringComparison.OrdinalIgnoreCase); + _selectedValuePath = value; + + if (isChanged) + { + this.RaisePropertyChanged("SelectedValuePath"); + } + } + } + + public WpfDrawingSettings ConversionSettings + { + get { + return _wpfSettings; + } + set { + if (value != null) + { + bool isChanged = (_wpfSettings != value); + + _wpfSettings = value; + + if (isChanged) + { + this.RaisePropertyChanged("ConversionSettings"); + } + } + } + } + + #endregion + + #region Public Methods + + public string GetPath(string inputPath) + { + if (string.IsNullOrWhiteSpace(inputPath)) + { + return inputPath; + } + if (_hidePathsRoot) + { + Uri fullPath = new Uri(inputPath, UriKind.Absolute); + + // Make relative path to the SharpVectors folder... + int indexOf = inputPath.IndexOf(SharpVectors, StringComparison.OrdinalIgnoreCase); + if (indexOf > 0) + { + Uri relRoot = new Uri(inputPath.Substring(0, indexOf), UriKind.Absolute); + + string relPath = relRoot.MakeRelativeUri(fullPath).ToString(); + relPath = relPath.Replace('/', '\\'); + + relPath = Uri.UnescapeDataString(relPath); + if (!relPath.StartsWith(ParentSymbol, StringComparison.OrdinalIgnoreCase)) + { + relPath = ParentSymbol + relPath; + } + + return relPath; + } + } + return inputPath; + } + + public void Load(string settingsPath) + { + if (string.IsNullOrWhiteSpace(settingsPath) || + File.Exists(settingsPath) == false) + { + return; + } + + XmlReaderSettings settings = new XmlReaderSettings(); + settings.IgnoreWhitespace = false; + settings.IgnoreComments = true; + settings.IgnoreProcessingInstructions = true; + + using (XmlReader reader = XmlReader.Create(settingsPath, settings)) + { + this.Load(reader); + } + } + + public void Save(string settingsPath) + { + if (string.IsNullOrWhiteSpace(settingsPath)) + { + return; + } + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + settings.IndentChars = " "; + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(settingsPath, settings)) + { + this.Save(writer); + } + } + + public bool IsCurrentSvgPathChanged(string svgPath) + { + if (string.IsNullOrWhiteSpace(svgPath) || + string.IsNullOrWhiteSpace(_currentSvgPath)) + { + return true; + } + string currentPath = string.Copy(svgPath); + if (!currentPath.EndsWith("\\", StringComparison.OrdinalIgnoreCase)) + { + currentPath = currentPath + "\\"; + } + + string currentSvgPath = string.Copy(_currentSvgPath); + if (!_currentSvgPath.EndsWith("\\", StringComparison.OrdinalIgnoreCase)) + { + currentSvgPath = _currentSvgPath + "\\"; + } + + return !(string.Equals(currentPath, currentSvgPath, StringComparison.OrdinalIgnoreCase)); + } + + public static void OpenFolderAndSelectItem(string folderPath, string file) + { + if (string.IsNullOrEmpty(folderPath) || Directory.Exists(folderPath) == false) + { + return; + } + + if (string.IsNullOrWhiteSpace(file)) + { + var selectedIsFile = false; + var selectedName = string.Empty; + var dirInfo = new DirectoryInfo(folderPath); + + var firstFileName = dirInfo.EnumerateFiles() + .Select(f => f.Name) + .FirstOrDefault(name => !string.Equals(name, "Thumbs.db", StringComparison.OrdinalIgnoreCase)); + if (!string.IsNullOrWhiteSpace(firstFileName)) + { + selectedIsFile = true; + selectedName = firstFileName; + } + else + { + if (!IsDirectoryEmpty(dirInfo.FullName)) + { + var firstDirName = dirInfo.EnumerateDirectories() + .Select(f => f.Name) + .FirstOrDefault(); + if (!string.IsNullOrWhiteSpace(firstDirName)) + { + selectedIsFile = false; + selectedName = firstDirName; + } + } + } + if (!string.IsNullOrWhiteSpace(selectedName)) + { + if (selectedIsFile) + { + file = selectedName; + } + else + { + folderPath = Path.Combine(folderPath, selectedName); + } + } + } + + IntPtr nativeFolder; + uint psfgaoOut; + SHParseDisplayName(folderPath, IntPtr.Zero, out nativeFolder, 0, out psfgaoOut); + + if (nativeFolder == IntPtr.Zero) + { + // Log error, can't find folder + return; + } + + IntPtr nativeFile = IntPtr.Zero; + if (!string.IsNullOrWhiteSpace(file)) + { + SHParseDisplayName(Path.Combine(folderPath, file), + IntPtr.Zero, out nativeFile, 0, out psfgaoOut); + } + + IntPtr[] fileArray; + if (nativeFile == IntPtr.Zero) + { + // Open the folder without the file selected if we can't find the file + fileArray = new IntPtr[0]; + } + else + { + fileArray = new IntPtr[] { nativeFile }; + } + + SHOpenFolderAndSelectItems(nativeFolder, (uint)fileArray.Length, fileArray, 0); + + Marshal.FreeCoTaskMem(nativeFolder); + if (nativeFile != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(nativeFile); + } + } + + #endregion + + #region Private Methods + + private void RaisePropertyChanged(string propertyName) + { + this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + private void Load(XmlReader reader) + { + var comparer = StringComparison.OrdinalIgnoreCase; + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element && + string.Equals(reader.Name, "option", comparer)) + { + string optionName = reader.GetAttribute("name"); + string optionType = reader.GetAttribute("type"); + if (string.Equals(optionType, "String", comparer)) + { + string optionValue = reader.ReadElementContentAsString(); + + switch (optionName) + { + case "DefaultSvgPath": + if (optionValue.StartsWith(ParentSymbol, comparer)) + { + var inputPath = string.Copy(_defaultSvgPath); + int indexOf = inputPath.IndexOf(SharpVectors, comparer); + + if (indexOf > 0) + { + var basePath = inputPath.Substring(0, indexOf); + _defaultSvgPath = Path.Combine(basePath, optionValue.Replace(ParentSymbol, "")); + } + else + { + _defaultSvgPath = optionValue; + } + } + else + { + _defaultSvgPath = optionValue; + } + break; + case "CurrentSvgPath": + if (optionValue.StartsWith(ParentSymbol, comparer)) + { + var inputPath = string.Copy(_currentSvgPath); + int indexOf = inputPath.IndexOf(SharpVectors, comparer); + + if (indexOf > 0) + { + var basePath = inputPath.Substring(0, indexOf); + _currentSvgPath = Path.Combine(basePath, optionValue.Replace(ParentSymbol, "")); + } + else + { + _currentSvgPath = optionValue; + } + } + else + { + _currentSvgPath = optionValue; + } + break; + case "SelectedValuePath": + _selectedValuePath = optionValue; + break; + } + } + else if (string.Equals(optionType, "Boolean", comparer)) + { + bool optionValue = reader.ReadElementContentAsBoolean(); + switch (optionName) + { + case "HidePathsRoot": + _hidePathsRoot = optionValue; + break; + case "ShowInputFile": + _showInputFile = optionValue; + break; + case "ShowOutputFile": + _showOutputFile = optionValue; + break; + case "RecursiveSearch": + _recursiveSearch = optionValue; + break; + + case "TextAsGeometry": + _wpfSettings.TextAsGeometry = optionValue; + break; + case "IncludeRuntime": + _wpfSettings.IncludeRuntime = optionValue; + break; + + case "IgnoreRootViewbox": + _wpfSettings.IgnoreRootViewbox = optionValue; + break; + case "EnsureViewboxSize": + _wpfSettings.EnsureViewboxSize = optionValue; + break; + case "EnsureViewboxPosition": + _wpfSettings.EnsureViewboxPosition = optionValue; + break; + } + } + + } + } + } + + private void Save(XmlWriter writer) + { + writer.WriteStartDocument(); + writer.WriteStartElement("options"); + + this.SaveOption(writer, "HidePathsRoot", _hidePathsRoot); + this.SaveOption(writer, "ShowInputFile", _showInputFile); + this.SaveOption(writer, "ShowOutputFile", _showOutputFile); + this.SaveOption(writer, "RecursiveSearch", _recursiveSearch); + this.SaveOption(writer, "DefaultSvgPath", this.GetPath(_defaultSvgPath)); + this.SaveOption(writer, "CurrentSvgPath", this.GetPath(_currentSvgPath)); + this.SaveOption(writer, "SelectedValuePath", _selectedValuePath); + + if (_wpfSettings != null) + { + this.SaveOption(writer, "TextAsGeometry", _wpfSettings.TextAsGeometry); + this.SaveOption(writer, "IncludeRuntime", _wpfSettings.IncludeRuntime); + + this.SaveOption(writer, "IgnoreRootViewbox", _wpfSettings.IgnoreRootViewbox); + this.SaveOption(writer, "EnsureViewboxSize", _wpfSettings.EnsureViewboxSize); + this.SaveOption(writer, "EnsureViewboxPosition", _wpfSettings.EnsureViewboxPosition); + } + + writer.WriteEndElement(); + writer.WriteEndDocument(); + } + + private void SaveOption(XmlWriter writer, string name, string value) + { + if (value == null) + { + value = string.Empty; + } + + writer.WriteStartElement("option"); + writer.WriteAttributeString("name", name); + writer.WriteAttributeString("type", "String"); + writer.WriteString(value); + writer.WriteEndElement(); + } + private void SaveOption(XmlWriter writer, string name, bool value) + { + writer.WriteStartElement("option"); + writer.WriteAttributeString("name", name); + writer.WriteAttributeString("type", "Boolean"); + writer.WriteString(value ? "true" : "false"); + writer.WriteEndElement(); + } + + #endregion + + #region ICloneable Members + + public OptionSettings Clone() + { + OptionSettings optSettings = new OptionSettings(this); + + if (_wpfSettings != null) + { + optSettings._wpfSettings = _wpfSettings.Clone(); + } + if (_defaultSvgPath != null) + { + optSettings._defaultSvgPath = string.Copy(_defaultSvgPath); + } + if (_currentSvgPath != null) + { + optSettings._currentSvgPath = string.Copy(_currentSvgPath); + } + + return optSettings; + } + + object ICloneable.Clone() + { + return this.Clone(); + } + + #endregion + } +} diff --git a/Samples/WpfTestSvgControl/PrintPreviewWindow.xaml b/Samples/WpfTestSvgControl/PrintPreviewWindow.xaml new file mode 100644 index 000000000..908642dc9 --- /dev/null +++ b/Samples/WpfTestSvgControl/PrintPreviewWindow.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/Samples/WpfTestSvgControl/PrintPreviewWindow.xaml.cs b/Samples/WpfTestSvgControl/PrintPreviewWindow.xaml.cs new file mode 100644 index 000000000..1909469ba --- /dev/null +++ b/Samples/WpfTestSvgControl/PrintPreviewWindow.xaml.cs @@ -0,0 +1,165 @@ +using System; +using System.IO; +using System.IO.Packaging; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Collections.Generic; + +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; +using System.Windows.Xps.Packaging; + +namespace WpfTestSvgControl +{ + /// + /// Interaction logic for PrintPreviewWindow.xaml + /// + public partial class PrintPreviewWindow : Window + { + #region Private Fields + + private string _fileName; + private Package _xpsDocPackage; + private XpsDocument _xpsDocument; + + #endregion + + #region Constructors and Destructor + + public PrintPreviewWindow() + { + InitializeComponent(); + + this.Loaded += new RoutedEventHandler(OnWindowLoaded); + this.Unloaded += new RoutedEventHandler(OnWindowUnloaded); + + this.Closed += new EventHandler(OnWindowClosed); + this.Closing += new CancelEventHandler(OnWindowClosing); + } + + #endregion + + #region Public Methods + + public void LoadDocument(XpsDocument document, Package package, string sourceFileName) + { + if (document == null) + { + return; + } + + try + { + if (_xpsDocument != null) + { + _xpsDocument.Close(); + _xpsDocument = null; + } + + if (_xpsDocPackage != null) + { + _xpsDocPackage.Close(); + _xpsDocPackage = null; + } + + if (!string.IsNullOrWhiteSpace(_fileName)) + { + PackageStore.RemovePackage(new Uri(_fileName)); + } + } + catch + { + } + + _xpsDocument = document; + _xpsDocPackage = package; + _fileName = sourceFileName; + + docViewer.Document = _xpsDocument.GetFixedDocumentSequence(); + } + + #endregion + + #region Private Methods + + private void OnWindowLoaded(object sender, RoutedEventArgs e) + { + ContentControl findToolbar = docViewer.Template.FindName( + "PART_FindToolBarHost", docViewer) as ContentControl; + if (findToolbar != null) + { + findToolbar.Visibility = Visibility.Collapsed; + } + } + + private void OnWindowUnloaded(object sender, RoutedEventArgs e) + { + try + { + docViewer.Document = null; + + if (_xpsDocument != null) + { + _xpsDocument.Close(); + _xpsDocument = null; + } + + if (_xpsDocPackage != null) + { + _xpsDocPackage.Close(); + _xpsDocPackage = null; + } + + if (!string.IsNullOrWhiteSpace(_fileName)) + { + PackageStore.RemovePackage(new Uri(_fileName)); + } + } + catch + { + } + } + + private void OnWindowClosing(object sender, CancelEventArgs e) + { + try + { + docViewer.Document = null; + + if (_xpsDocument != null) + { + _xpsDocument.Close(); + _xpsDocument = null; + } + + if (_xpsDocPackage != null) + { + _xpsDocPackage.Close(); + _xpsDocPackage = null; + } + + if (!string.IsNullOrWhiteSpace(_fileName)) + { + PackageStore.RemovePackage(new Uri(_fileName)); + } + } + catch + { + } + } + + private void OnWindowClosed(object sender, EventArgs e) + { + } + + #endregion + } +} diff --git a/Samples/WpfTestSvgControl/Properties/AssemblyInfo.cs b/Samples/WpfTestSvgControl/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..1019508dc --- /dev/null +++ b/Samples/WpfTestSvgControl/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WpfTestSvgControl")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WpfTestSvgControl")] +[assembly: AssemblyCopyright("Copyright © 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.3.0.0")] +[assembly: AssemblyFileVersion("1.3.0.0")] diff --git a/Samples/WpfTestSvgControl/Properties/Resources.Designer.cs b/Samples/WpfTestSvgControl/Properties/Resources.Designer.cs new file mode 100644 index 000000000..778c4e9cb --- /dev/null +++ b/Samples/WpfTestSvgControl/Properties/Resources.Designer.cs @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WpfTestSvgControl.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WpfTestSvgControl.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] PanTool { + get { + object obj = ResourceManager.GetObject("PanTool", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] PanToolDown { + get { + object obj = ResourceManager.GetObject("PanToolDown", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/Samples/WpfTestSvgControl/Properties/Resources.resx b/Samples/WpfTestSvgControl/Properties/Resources.resx new file mode 100644 index 000000000..9568416da --- /dev/null +++ b/Samples/WpfTestSvgControl/Properties/Resources.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\PanTool.cur;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\PanToolDown.cur;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Samples/WpfTestSvgControl/Properties/Settings.Designer.cs b/Samples/WpfTestSvgControl/Properties/Settings.Designer.cs new file mode 100644 index 000000000..8eaca5753 --- /dev/null +++ b/Samples/WpfTestSvgControl/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WpfTestSvgControl.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/Samples/WpfTestSvgControl/Properties/Settings.settings b/Samples/WpfTestSvgControl/Properties/Settings.settings new file mode 100644 index 000000000..033d7a5e9 --- /dev/null +++ b/Samples/WpfTestSvgControl/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Samples/WpfTestSvgControl/Resources/PanTool.cur b/Samples/WpfTestSvgControl/Resources/PanTool.cur new file mode 100644 index 0000000000000000000000000000000000000000..6d10c848cdc8d0d7a52bd81ee2f5e8b47d1ef9a2 GIT binary patch literal 326 zcmaLSu?@m75QX92iBKx!4vC7Clx#q@VT6=;1V%v=)EOlO1EfMJP>y#vqV%1wvZ(il?Nc7Gzh@zp nrv7_+LpkQg%uO=ZW`hf6NpSkcO~%!*QD1jo(QLVvXRG@KcZz>G literal 0 HcmV?d00001 diff --git a/Samples/WpfTestSvgControl/Resources/PanToolDown.cur b/Samples/WpfTestSvgControl/Resources/PanToolDown.cur new file mode 100644 index 0000000000000000000000000000000000000000..5f3c236bac7fdaf45d323e9e800e4f0ccedae28c GIT binary patch literal 326 zcmaLSF%E+;429u8Ffdiw$_Qh}-T;oqkvebyj=~W#AqG_UnWzgwar8NnEjbBP1o|GL z);quxc!^4k60|&~Gv`!ka~@`v52aiuaE-(rIvI|v{i-f;gU753Kcz{xJ-wkEdt>I3 XthL!9Q^*#5Vf|ArsdvQkyi4~7fDwUA literal 0 HcmV?d00001 diff --git a/Samples/WpfTestSvgControl/SettingsPage.xaml b/Samples/WpfTestSvgControl/SettingsPage.xaml new file mode 100644 index 000000000..0b3e3aa46 --- /dev/null +++ b/Samples/WpfTestSvgControl/SettingsPage.xaml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + Security feature, which is only useful in screenshot to be posted on the web. + + + + + + Indicates whether to search the selected directory recursively. + + + + + + Indicates whether to show the input SVG file tab. + + + + + + Indicates whether to show the output XAML file tab. + + + Default SVG Directory: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/WpfTestSvgControl/SvgPage.xaml.cs b/Samples/WpfTestSvgControl/SvgPage.xaml.cs new file mode 100644 index 000000000..88c2f6a88 --- /dev/null +++ b/Samples/WpfTestSvgControl/SvgPage.xaml.cs @@ -0,0 +1,411 @@ +using System; +using System.IO; +using System.IO.Packaging; +using System.IO.Compression; + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Xps; +using System.Windows.Xps.Packaging; + +using ICSharpCode.AvalonEdit; +using ICSharpCode.AvalonEdit.Utils; +using ICSharpCode.AvalonEdit.Search; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Folding; +using ICSharpCode.AvalonEdit.Highlighting; + +using Microsoft.Win32; + +namespace WpfTestSvgControl +{ + /// + /// Interaction logic for SvgPage.xaml + /// + public partial class SvgPage : Page + { + #region Private Fields + + private string _currentFileName; + + private MainWindow _mainWindow; + + private FoldingManager _foldingManager; + private XmlFoldingStrategy _foldingStrategy; + + private readonly SearchPanel _searchPanel; + + #endregion + + #region Constructors and Destructor + + public SvgPage() + { + InitializeComponent(); + + 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; + + _foldingManager = FoldingManager.Install(textEditor.TextArea); + _foldingStrategy = new XmlFoldingStrategy(); + + textEditor.CommandBindings.Add(new CommandBinding( + ApplicationCommands.Print, OnPrint, OnCanExecuteTextEditorCommand)); + textEditor.CommandBindings.Add(new CommandBinding( + ApplicationCommands.PrintPreview, OnPrintPreview, OnCanExecuteTextEditorCommand)); + + _searchPanel = SearchPanel.Install(textEditor); + } + + #endregion + + #region Public Properties + + public MainWindow MainWindow + { + get { + return _mainWindow; + } + set { + _mainWindow = value; + } + } + + #endregion + + #region Public Methods + + public void LoadDocument(string documentFileName) + { + if (textEditor == null || string.IsNullOrWhiteSpace(documentFileName)) + { + return; + } + + if (!string.IsNullOrWhiteSpace(_currentFileName) && File.Exists(_currentFileName)) + { + // Prevent reloading the same file, just in case we are editing... + if (string.Equals(documentFileName, _currentFileName, StringComparison.OrdinalIgnoreCase)) + { + return; + } + } + + string fileExt = Path.GetExtension(documentFileName); + if (string.Equals(fileExt, ".svgz", StringComparison.OrdinalIgnoreCase)) + { + using (FileStream fileStream = File.OpenRead(documentFileName)) + { + using (GZipStream zipStream = + new GZipStream(fileStream, CompressionMode.Decompress)) + { + // Text Editor does not work with this stream, so we read the data to memory stream... + MemoryStream memoryStream = new MemoryStream(); + // Use this method is used to read all bytes from a stream. + int totalCount = 0; + int bufferSize = 512; + byte[] buffer = new byte[bufferSize]; + while (true) + { + int bytesRead = zipStream.Read(buffer, 0, bufferSize); + if (bytesRead == 0) + { + break; + } + else + { + memoryStream.Write(buffer, 0, bytesRead); + } + totalCount += bytesRead; + } + + if (totalCount > 0) + { + memoryStream.Position = 0; + } + + textEditor.Load(memoryStream); + + memoryStream.Close(); + } + } + } + else + { + textEditor.Load(documentFileName); + } + + if (_foldingManager == null || _foldingStrategy == null) + { + _foldingManager = FoldingManager.Install(textEditor.TextArea); + _foldingStrategy = new XmlFoldingStrategy(); + } + _foldingStrategy.UpdateFoldings(_foldingManager, textEditor.Document); + + _currentFileName = documentFileName; + } + + public void UnloadDocument() + { + if (textEditor != null) + { + textEditor.Document.Text = string.Empty; + } + _currentFileName = null; + } + + public void PageSelected(bool isSelected) + { + if (isSelected) + { + if (textEditor.TextArea.IsKeyboardFocusWithin) + { + Keyboard.Focus(textEditor.TextArea); + } + } + } + + #endregion + + #region Private Methods + + private void OnOpenFileClick(object sender, RoutedEventArgs e) + { + OpenFileDialog dlg = new OpenFileDialog(); + dlg.CheckFileExists = true; + if (dlg.ShowDialog() ?? false) + { + _currentFileName = dlg.FileName; + textEditor.Load(_currentFileName); + textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension( + Path.GetExtension(_currentFileName)); + } + } + + private void OnSaveFileClick(object sender, EventArgs e) + { + if (_currentFileName == null) + { + SaveFileDialog dlg = new SaveFileDialog(); + dlg.Title = "Save As"; + dlg.Filter = "SVG Files|*.svg;*.svgz"; + dlg.DefaultExt = ".svg"; + if (dlg.ShowDialog() ?? false) + { + _currentFileName = dlg.FileName; + } + else + { + return; + } + } + + string fileExt = Path.GetExtension(_currentFileName); + if (string.Equals(fileExt, ".svg", StringComparison.OrdinalIgnoreCase)) + { + textEditor.Save(_currentFileName); + } + else if (string.Equals(fileExt, ".svgz", StringComparison.OrdinalIgnoreCase)) + { + using (FileStream svgzDestFile = File.Create(_currentFileName)) + { + using (GZipStream zipStream = new GZipStream(svgzDestFile, + CompressionMode.Compress, true)) + { + textEditor.Save(zipStream); + } + } + } + } + + private void OnSearchTextClick(object sender, RoutedEventArgs e) + { + if (_searchPanel == null) + { + return; + } + + string searchText = searchTextBox.Text; + + if (!string.IsNullOrWhiteSpace(searchText)) + { + _searchPanel.SearchPattern = searchText; + } + + _searchPanel.Open(); + _searchPanel.Reactivate(); + } + + private void OnSearchTextBoxKeyUp(object sender, KeyEventArgs e) + { + if (e.Key != Key.Enter) + return; + + // your event handler here + e.Handled = true; + + this.OnSearchTextClick(sender, e); + } + + private void OnHighlightingSelectionChanged(object sender, SelectionChangedEventArgs e) + { + } + + #region Printing Methods + + private Block ConvertTextDocumentToBlock() + { + TextDocument document = textEditor.Document; + IHighlighter highlighter = + textEditor.TextArea.GetService(typeof(IHighlighter)) as IHighlighter; + + return DocumentPrinter.ConvertTextDocumentToBlock(document, highlighter); + } + + private FlowDocument CreateFlowDocumentForEditor() + { + FlowDocument doc = new FlowDocument(ConvertTextDocumentToBlock()); + doc.FontFamily = textEditor.FontFamily; + doc.FontSize = textEditor.FontSize; + return doc; + } + + // CanExecuteRoutedEventHandler that only returns true if + // the source is a control. + private void OnCanExecuteTextEditorCommand(object sender, CanExecuteRoutedEventArgs e) + { + var target = e.Source as TextEditor; + + if (target != null) + { + e.CanExecute = true; + } + else + { + e.CanExecute = false; + } + } + + private void OnPrint(object sender, ExecutedRoutedEventArgs e) + { + PrintDialog printDialog = new PrintDialog(); + printDialog.PageRangeSelection = PageRangeSelection.AllPages; + printDialog.UserPageRangeEnabled = true; + bool? dialogResult = printDialog.ShowDialog(); + if (dialogResult != null && dialogResult.Value == false) + { + return; + } + + FlowDocument printSource = this.CreateFlowDocumentForEditor(); + + // Save all the existing settings. + double pageHeight = printSource.PageHeight; + double pageWidth = printSource.PageWidth; + Thickness pagePadding = printSource.PagePadding; + double columnGap = printSource.ColumnGap; + double columnWidth = printSource.ColumnWidth; + + // Make the FlowDocument page match the printed page. + printSource.PageHeight = printDialog.PrintableAreaHeight; + printSource.PageWidth = printDialog.PrintableAreaWidth; + printSource.PagePadding = new Thickness(20); + printSource.ColumnGap = Double.NaN; + printSource.ColumnWidth = printDialog.PrintableAreaWidth; + + Size pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight); + + DocumentPaginator paginator = ((IDocumentPaginatorSource)printSource).DocumentPaginator; + paginator.PageSize = pageSize; + paginator.ComputePageCount(); + + printDialog.PrintDocument(paginator, "Svg Contents"); + + // Reapply the old settings. + printSource.PageHeight = pageHeight; + printSource.PageWidth = pageWidth; + printSource.PagePadding = pagePadding; + printSource.ColumnGap = columnGap; + printSource.ColumnWidth = columnWidth; + } + + private void OnPrintPreview(object sender, ExecutedRoutedEventArgs e) + { + PrintDialog printDialog = new PrintDialog(); + printDialog.PageRangeSelection = PageRangeSelection.AllPages; + printDialog.UserPageRangeEnabled = true; + bool? dialogResult = printDialog.ShowDialog(); + if (dialogResult != null && dialogResult.Value == false) + { + return; + } + + FlowDocument printSource = this.CreateFlowDocumentForEditor(); + + // Save all the existing settings. + double pageHeight = printSource.PageHeight; + double pageWidth = printSource.PageWidth; + Thickness pagePadding = printSource.PagePadding; + double columnGap = printSource.ColumnGap; + double columnWidth = printSource.ColumnWidth; + + // Make the FlowDocument page match the printed page. + printSource.PageHeight = printDialog.PrintableAreaHeight; + printSource.PageWidth = printDialog.PrintableAreaWidth; + printSource.PagePadding = new Thickness(20); + printSource.ColumnGap = Double.NaN; + printSource.ColumnWidth = printDialog.PrintableAreaWidth; + + Size pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight); + + MemoryStream xpsStream = new MemoryStream(); + Package package = Package.Open(xpsStream, FileMode.Create, FileAccess.ReadWrite); + string packageUriString = "memorystream://data.xps"; + PackageStore.AddPackage(new Uri(packageUriString), package); + + XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.Normal, packageUriString); + XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument); + + DocumentPaginator paginator = ((IDocumentPaginatorSource)printSource).DocumentPaginator; + paginator.PageSize = pageSize; + paginator.ComputePageCount(); + + writer.Write(paginator); + + // Reapply the old settings. + printSource.PageHeight = pageHeight; + printSource.PageWidth = pageWidth; + printSource.PagePadding = pagePadding; + printSource.ColumnGap = columnGap; + printSource.ColumnWidth = columnWidth; + + PrintPreviewWindow printPreview = new PrintPreviewWindow(); + printPreview.Width = this.ActualWidth; + printPreview.Height = this.ActualHeight; + printPreview.Owner = Application.Current.MainWindow; + + printPreview.LoadDocument(xpsDocument, package, packageUriString); + + printPreview.Show(); + } + + #endregion + + #endregion + } +} diff --git a/Samples/WpfTestSvgControl/TraceDocument.xaml b/Samples/WpfTestSvgControl/TraceDocument.xaml new file mode 100644 index 000000000..a2a9b7614 --- /dev/null +++ b/Samples/WpfTestSvgControl/TraceDocument.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + diff --git a/Samples/WpfTestSvgControl/TraceDocument.xaml.cs b/Samples/WpfTestSvgControl/TraceDocument.xaml.cs new file mode 100644 index 000000000..7c64af364 --- /dev/null +++ b/Samples/WpfTestSvgControl/TraceDocument.xaml.cs @@ -0,0 +1,109 @@ +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Controls.Primitives; + +namespace WpfTestSvgControl +{ + public partial class TraceDocument : FlowDocument, ITraceTextSink + { + private delegate void AppendTextDelegate(string msg, string style); + + private TraceListener _listener; + + public TraceDocument() + { + AutoAttach = false; + InitializeComponent(); + } + + public bool AutoAttach { get; set; } + + public void Event(string msg, TraceEventType eventType) + { + Append(msg, eventType.ToString()); + } + + public void Fail(string msg) + { + Append(msg, "Fail"); + } + + public void Startup() + { + if (_listener == null) + { + _listener = new TraceTextSource(this); + Trace.Listeners.Add(_listener); + } + } + + public void Shutdown() + { + if (_listener != null) + { + Trace.Listeners.Remove(_listener); + _listener.Dispose(); + _listener = null; + } + } + + private void Append(string msg, string style) + { + if (Dispatcher.CheckAccess()) + { + Debug.Assert(Blocks.LastBlock != null); + Debug.Assert(Blocks.LastBlock is Paragraph); + var run = new Run(msg); + + run.Style = (Style)(Resources[style]); + if (run.Style == null) + run.Style = (Style)(Resources["Information"]); + + ((Paragraph)Blocks.LastBlock).Inlines.Add(run); + + ScrollParent(this); + } + else + { + Dispatcher.Invoke(new AppendTextDelegate(Append), msg, style); + } + } + + private static void ScrollParent(FrameworkContentElement element) + { + if (element != null) + { + if (element.Parent is TextBoxBase) + ((TextBoxBase)element.Parent).ScrollToEnd(); + + else if (element.Parent is ScrollViewer) + ((ScrollViewer)element.Parent).ScrollToEnd(); + + else + ScrollParent(element.Parent as FrameworkContentElement); + } + } + + private void Document_Loaded(object sender, RoutedEventArgs e) + { + if (AutoAttach && _listener == null) + { + _listener = new TraceTextSource(this); + Trace.Listeners.Add(_listener); + } + } + + private void Document_Unloaded(object sender, RoutedEventArgs e) + { + if (AutoAttach && _listener != null) + { + Trace.Listeners.Remove(_listener); + _listener.Dispose(); + _listener = null; + } + } + } +} diff --git a/Samples/WpfTestSvgControl/TraceTextSource.cs b/Samples/WpfTestSvgControl/TraceTextSource.cs new file mode 100644 index 000000000..c0d21e1a1 --- /dev/null +++ b/Samples/WpfTestSvgControl/TraceTextSource.cs @@ -0,0 +1,56 @@ +using System; +using System.Diagnostics; + +namespace WpfTestSvgControl +{ + interface ITraceTextSink + { + void Fail(string msg); + void Event(string msg, TraceEventType eventType); + } + + class TraceTextSource : TraceListener + { + public ITraceTextSink Sink { get; private set; } + private bool _fail; + private TraceEventType _eventType = TraceEventType.Information; + + public TraceTextSource(ITraceTextSink sink) + { + Debug.Assert(sink != null); + Sink = sink; + } + + public override void Fail(string message) + { + _fail = true; + base.Fail(message); + } + + public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message) + { + _eventType = eventType; + base.TraceEvent(eventCache, source, eventType, id, message); + } + + public override void Write(string message) + { + if (IndentLevel > 0) + message = message.PadLeft(IndentLevel + message.Length, '\t'); + + if (_fail) + Sink.Fail(message); + + else + Sink.Event(message, _eventType); + + _fail = false; + _eventType = TraceEventType.Information; + } + + public override void WriteLine(string message) + { + Write(message + "\n"); + } + } +} diff --git a/Samples/WpfTestSvgControl/WpfTestSvgControl.csproj b/Samples/WpfTestSvgControl/WpfTestSvgControl.csproj new file mode 100644 index 000000000..e3e0f9852 --- /dev/null +++ b/Samples/WpfTestSvgControl/WpfTestSvgControl.csproj @@ -0,0 +1,333 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {28586359-004C-45B5-823E-38F714784B31} + WinExe + Properties + WpfTestSvgControl + WpfTestSvgControl + v4.5.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + App.ico + + + + true + full + false + Output\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + false + + + pdbonly + true + Output\ + TRACE + prompt + 4 + AllRules.ruleset + false + + + WpfTestSvgControl.App + + + + + ..\..\Libraries\ICSharpCode.AvalonEdit.dll + + + + ..\..\Libraries\SharpVectors.ShellFileDialogs.dll + + + + + + + + + + + + + + + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + DrawingHelpWindow.xaml + + + + + + ZoomPanOverview.xaml + + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + App.xaml + Code + + + DebugPage.xaml + + + MainWindow.xaml + Code + + + Designer + MSBuild:Compile + + + + + DrawingPage.xaml + + + + + PrintPreviewWindow.xaml + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + SettingsPage.xaml + + + SvgPage.xaml + + + TraceDocument.xaml + + + + XamlPage.xaml + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + {E8056611-E49C-4BC3-A682-A629D5CEC11C} + SharpVectors.Converters.Wpf + + + {D6BB65FC-240E-4241-B2ED-A7FB3F13E978} + SharpVectors.Core + + + {351B0A6E-2F6B-497A-844B-DCB5A502FB0D} + SharpVectors.Css + + + {FE34CBC0-D23C-4A95-BA64-83A031814010} + SharpVectors.Dom + + + {5D336F48-3FB9-4382-B4B9-06974C764007} + SharpVectors.Model + + + {A2576CE0-E492-490F-97E9-C0E7ABAFAF27} + SharpVectors.Rendering.Wpf + + + {2CD52982-A1C2-4A14-9D69-D64719357216} + SharpVectors.Runtime.Wpf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/WpfTestSvgControl/XamlPage.xaml b/Samples/WpfTestSvgControl/XamlPage.xaml new file mode 100644 index 000000000..7a4367348 --- /dev/null +++ b/Samples/WpfTestSvgControl/XamlPage.xaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/WpfTestSvgControl/XamlPage.xaml.cs b/Samples/WpfTestSvgControl/XamlPage.xaml.cs new file mode 100644 index 000000000..b654a1265 --- /dev/null +++ b/Samples/WpfTestSvgControl/XamlPage.xaml.cs @@ -0,0 +1,419 @@ +using System; +using System.IO; +using System.IO.Packaging; +using System.IO.Compression; + +using System.Windows; +using System.Windows.Input; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Xps; +using System.Windows.Xps.Packaging; + +using ICSharpCode.AvalonEdit; +using ICSharpCode.AvalonEdit.Utils; +using ICSharpCode.AvalonEdit.Search; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Folding; +using ICSharpCode.AvalonEdit.Highlighting; + +using Microsoft.Win32; + +namespace WpfTestSvgControl +{ + /// + /// Interaction logic for XamlPage.xaml + /// + public partial class XamlPage : Page + { + #region Private Fields + + private string _currentFileName; + + private MainWindow _mainWindow; + + private FoldingManager _foldingManager; + private XmlFoldingStrategy _foldingStrategy; + + private readonly SearchPanel _searchPanel; + + #endregion + + #region Constructors and Destructor + + public XamlPage() + { + InitializeComponent(); + + 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; + + _foldingManager = FoldingManager.Install(textEditor.TextArea); + _foldingStrategy = new XmlFoldingStrategy(); + + textEditor.CommandBindings.Add(new CommandBinding( + ApplicationCommands.Print, OnPrint, OnCanExecuteTextEditorCommand)); + textEditor.CommandBindings.Add(new CommandBinding( + ApplicationCommands.PrintPreview, OnPrintPreview, OnCanExecuteTextEditorCommand)); + + _searchPanel = SearchPanel.Install(textEditor); + } + + #endregion + + #region Public Properties + + public MainWindow MainWindow + { + get { + return _mainWindow; + } + set { + _mainWindow = value; + } + } + + #endregion + + #region Public Methods + + public void LoadDocument(string documentFileName) + { + if (textEditor == null || string.IsNullOrWhiteSpace(documentFileName)) + { + return; + } + + string fileExt = Path.GetExtension(documentFileName); + if (string.Equals(fileExt, ".zaml", StringComparison.OrdinalIgnoreCase)) + { + using (FileStream fileStream = File.OpenRead(documentFileName)) + { + using (GZipStream zipStream = new GZipStream(fileStream, CompressionMode.Decompress)) + { + // Text Editor does not work with this stream, so we read the data to memory stream... + MemoryStream memoryStream = new MemoryStream(); + // Use this method is used to read all bytes from a stream. + int totalCount = 0; + int bufferSize = 512; + byte[] buffer = new byte[bufferSize]; + while (true) + { + int bytesRead = zipStream.Read(buffer, 0, bufferSize); + if (bytesRead == 0) + { + break; + } + else + { + memoryStream.Write(buffer, 0, bytesRead); + } + totalCount += bytesRead; + } + + if (totalCount > 0) + { + memoryStream.Position = 0; + } + + textEditor.Load(memoryStream); + + memoryStream.Close(); + } + } + } + else + { + textEditor.Load(documentFileName); + } + + if (_foldingManager == null || _foldingStrategy == null) + { + _foldingManager = FoldingManager.Install(textEditor.TextArea); + _foldingStrategy = new XmlFoldingStrategy(); + } + + _foldingStrategy.UpdateFoldings(_foldingManager, textEditor.Document); + } + + public void UnloadDocument() + { + if (textEditor != null) + { + textEditor.Document.Text = string.Empty; + } + } + + public void PageSelected(bool isSelected) + { + if (isSelected) + { + if (textEditor.TextArea.IsKeyboardFocusWithin) + { + Keyboard.Focus(textEditor.TextArea); + } + } + } + + #endregion + + #region Private Methods + + private void OnOpenFileClick(object sender, RoutedEventArgs e) + { + OpenFileDialog dlg = new OpenFileDialog(); + dlg.CheckFileExists = true; + if (dlg.ShowDialog() ?? false) + { + _currentFileName = dlg.FileName; + textEditor.Load(_currentFileName); + textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension( + Path.GetExtension(_currentFileName)); + } + } + + private void OnSaveFileClick(object sender, EventArgs e) + { + if (_currentFileName == null) + { + SaveFileDialog dlg = new SaveFileDialog(); + dlg.Title = "Save As (.zaml is GZip compressed file used by SharpVectors)"; + dlg.Filter = "SVG Files|*.xaml;*.zaml"; + dlg.DefaultExt = ".xaml"; + if (dlg.ShowDialog() ?? false) + { + _currentFileName = dlg.FileName; + } + else + { + return; + } + } + + string fileExt = Path.GetExtension(_currentFileName); + if (string.Equals(fileExt, ".xaml", StringComparison.OrdinalIgnoreCase)) + { + textEditor.Save(_currentFileName); + } + else if (string.Equals(fileExt, ".zaml", StringComparison.OrdinalIgnoreCase)) + { + using (FileStream zamlDestFile = File.Create(_currentFileName)) + { + using (GZipStream zipStream = new GZipStream(zamlDestFile, + CompressionMode.Compress, true)) + { + textEditor.Save(zipStream); + } + } + } + } + + private void OnSearchTextClick(object sender, RoutedEventArgs e) + { + if (_searchPanel == null) + { + return; + } + + string searchText = searchTextBox.Text; + + if (!string.IsNullOrWhiteSpace(searchText)) + { + _searchPanel.SearchPattern = searchText; + } + + _searchPanel.Open(); + _searchPanel.Reactivate(); + } + + private void OnSearchTextBoxKeyUp(object sender, KeyEventArgs e) + { + if (e.Key != Key.Enter) + return; + + // your event handler here + e.Handled = true; + + textEditor.Select(textEditor.SelectionStart, 1); + searchTextBox.Focus(); + + this.OnSearchTextClick(sender, e); + } + + private void OnHighlightingSelectionChanged(object sender, SelectionChangedEventArgs e) + { + } + + #endregion + + #region Printing Methods + + private Block ConvertTextDocumentToBlock() + { + TextDocument document = textEditor.Document; + IHighlighter highlighter = + textEditor.TextArea.GetService(typeof(IHighlighter)) as IHighlighter; + + return DocumentPrinter.ConvertTextDocumentToBlock(document, highlighter); + + //Paragraph p = new Paragraph(); + //foreach (DocumentLine line in document.Lines) + //{ + // int lineNumber = line.LineNumber; + // HighlightedInlineBuilder inlineBuilder = new HighlightedInlineBuilder(document.GetText(line)); + // if (highlighter != null) + // { + // HighlightedLine highlightedLine = highlighter.HighlightLine(lineNumber); + // int lineStartOffset = line.Offset; + // foreach (HighlightedSection section in highlightedLine.Sections) + // inlineBuilder.SetHighlighting(section.Offset - lineStartOffset, section.Length, section.Color); + // } + // p.Inlines.AddRange(inlineBuilder.CreateRuns()); + // p.Inlines.Add(new LineBreak()); + //} + + //return p; + } + + private FlowDocument CreateFlowDocumentForEditor() + { + FlowDocument doc = new FlowDocument(ConvertTextDocumentToBlock()); + doc.FontFamily = textEditor.FontFamily; + doc.FontSize = textEditor.FontSize; + return doc; + } + + // CanExecuteRoutedEventHandler that only returns true if + // the source is a control. + private void OnCanExecuteTextEditorCommand(object sender, CanExecuteRoutedEventArgs e) + { + var target = e.Source as TextEditor; + + if (target != null) + { + e.CanExecute = true; + } + else + { + e.CanExecute = false; + } + } + + private void OnPrint(object sender, ExecutedRoutedEventArgs e) + { + PrintDialog printDialog = new PrintDialog(); + printDialog.PageRangeSelection = PageRangeSelection.AllPages; + printDialog.UserPageRangeEnabled = true; + bool? dialogResult = printDialog.ShowDialog(); + if (dialogResult != null && dialogResult.Value == false) + { + return; + } + + FlowDocument printSource = this.CreateFlowDocumentForEditor(); + + // Save all the existing settings. + double pageHeight = printSource.PageHeight; + double pageWidth = printSource.PageWidth; + Thickness pagePadding = printSource.PagePadding; + double columnGap = printSource.ColumnGap; + double columnWidth = printSource.ColumnWidth; + + // Make the FlowDocument page match the printed page. + printSource.PageHeight = printDialog.PrintableAreaHeight; + printSource.PageWidth = printDialog.PrintableAreaWidth; + printSource.PagePadding = new Thickness(20); + printSource.ColumnGap = Double.NaN; + printSource.ColumnWidth = printDialog.PrintableAreaWidth; + + Size pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight); + + DocumentPaginator paginator = ((IDocumentPaginatorSource)printSource).DocumentPaginator; + paginator.PageSize = pageSize; + paginator.ComputePageCount(); + + printDialog.PrintDocument(paginator, "Svg Contents"); + + // Reapply the old settings. + printSource.PageHeight = pageHeight; + printSource.PageWidth = pageWidth; + printSource.PagePadding = pagePadding; + printSource.ColumnGap = columnGap; + printSource.ColumnWidth = columnWidth; + } + + private void OnPrintPreview(object sender, ExecutedRoutedEventArgs e) + { + PrintDialog printDialog = new PrintDialog(); + printDialog.PageRangeSelection = PageRangeSelection.AllPages; + printDialog.UserPageRangeEnabled = true; + bool? dialogResult = printDialog.ShowDialog(); + if (dialogResult != null && dialogResult.Value == false) + { + return; + } + + FlowDocument printSource = this.CreateFlowDocumentForEditor(); + + // Save all the existing settings. + double pageHeight = printSource.PageHeight; + double pageWidth = printSource.PageWidth; + Thickness pagePadding = printSource.PagePadding; + double columnGap = printSource.ColumnGap; + double columnWidth = printSource.ColumnWidth; + + // Make the FlowDocument page match the printed page. + printSource.PageHeight = printDialog.PrintableAreaHeight; + printSource.PageWidth = printDialog.PrintableAreaWidth; + printSource.PagePadding = new Thickness(20); + printSource.ColumnGap = Double.NaN; + printSource.ColumnWidth = printDialog.PrintableAreaWidth; + + Size pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight); + + MemoryStream xpsStream = new MemoryStream(); + Package package = Package.Open(xpsStream, FileMode.Create, FileAccess.ReadWrite); + string packageUriString = "memorystream://data.xps"; + PackageStore.AddPackage(new Uri(packageUriString), package); + + XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.Normal, packageUriString); + XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument); + + DocumentPaginator paginator = ((IDocumentPaginatorSource)printSource).DocumentPaginator; + paginator.PageSize = pageSize; + paginator.ComputePageCount(); + + writer.Write(paginator); + + // Reapply the old settings. + printSource.PageHeight = pageHeight; + printSource.PageWidth = pageWidth; + printSource.PagePadding = pagePadding; + printSource.ColumnGap = columnGap; + printSource.ColumnWidth = columnWidth; + + PrintPreviewWindow printPreview = new PrintPreviewWindow(); + printPreview.Width = this.ActualWidth; + printPreview.Height = this.ActualHeight; + printPreview.Owner = Application.Current.MainWindow; + + printPreview.LoadDocument(xpsDocument, package, packageUriString); + + printPreview.Show(); + } + + #endregion + } +} diff --git a/Samples/WpfTestSvgControl/ZoomPanDimensionConverter.cs b/Samples/WpfTestSvgControl/ZoomPanDimensionConverter.cs new file mode 100644 index 000000000..4f4654a32 --- /dev/null +++ b/Samples/WpfTestSvgControl/ZoomPanDimensionConverter.cs @@ -0,0 +1,41 @@ +using System; +using System.Globalization; + +using System.Windows; +using System.Windows.Data; +using System.Windows.Markup; + +using SharpVectors.Runtime; + +namespace WpfTestSvgControl +{ + [MarkupExtensionReturnType(typeof(double))] + public sealed class ZoomPanDimensionConverter : MarkupExtension, IMultiValueConverter + { + public override object ProvideValue(IServiceProvider serviceProvider) + { + return this; + } + + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + //NOTE: Cannot pass ExtentWidth or ExtentHeight as one of the values because it does not seem to update + var zoomPanControl = values[3] as ZoomPanControl; + if (values[0] == null || zoomPanControl == null) + return DependencyProperty.UnsetValue; + var size = (double)values[0]; + var offset = (double)values[1]; + var zoom = (double)values[2]; + + var isWidth = string.Equals(parameter?.ToString(), "width", StringComparison.OrdinalIgnoreCase); + return Math.Max(isWidth ? Math.Min(zoomPanControl.ExtentWidth / zoom - offset, size) + : Math.Min(zoomPanControl.ExtentHeight / zoom - offset, size), 0); + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } + +} diff --git a/Samples/WpfTestSvgControl/ZoomPanMouseHandlingMode.cs b/Samples/WpfTestSvgControl/ZoomPanMouseHandlingMode.cs new file mode 100644 index 000000000..98fb32454 --- /dev/null +++ b/Samples/WpfTestSvgControl/ZoomPanMouseHandlingMode.cs @@ -0,0 +1,32 @@ +namespace WpfTestSvgControl +{ + /// + /// Defines the current state of the mouse handling logic. + /// + public enum ZoomPanMouseHandlingMode + { + /// + /// Not in any special mode. + /// + None, + + /// + /// The user is left-mouse-button-dragging to pan the viewport. + /// + Panning, + + /// + /// The user is holding down shift and left-clicking or right-clicking to zoom in or out. + /// + Zooming, + + /// + /// The user is holding down shift and left-mouse-button-dragging to select a region to zoom to. + /// + DragZooming, + + SelectPoint, + + SelectRectangle + } +} diff --git a/Samples/WpfTestSvgControl/ZoomPanOverview.xaml b/Samples/WpfTestSvgControl/ZoomPanOverview.xaml new file mode 100644 index 000000000..817ba4aff --- /dev/null +++ b/Samples/WpfTestSvgControl/ZoomPanOverview.xaml @@ -0,0 +1,9 @@ + + diff --git a/Samples/WpfTestSvgControl/ZoomPanOverview.xaml.cs b/Samples/WpfTestSvgControl/ZoomPanOverview.xaml.cs new file mode 100644 index 000000000..b8f64cb34 --- /dev/null +++ b/Samples/WpfTestSvgControl/ZoomPanOverview.xaml.cs @@ -0,0 +1,339 @@ +using System; +using System.Diagnostics; + +using System.Windows; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Controls; + +using SharpVectors.Runtime; + +namespace WpfTestSvgControl +{ + /// + /// Interaction logic for ZoomPanOverview.xaml + /// + public partial class ZoomPanOverview : UserControl + { + #region Private Fields + + /// + /// The control for creating a drag border + /// + private Border _dragBorder; + + /// + /// The control for creating a drag border + /// + private Border _sizingBorder; + + private bool _sizingEvent; + + /// + /// The control for containing a zoom border + /// + private Canvas _viewportCanvas; + + private Viewbox _viewportBox; + + /// + /// Specifies the current state of the mouse handling logic. + /// + private ZoomPanMouseHandlingMode _mouseHandlingMode = ZoomPanMouseHandlingMode.None; + + /// + /// The point that was clicked relative to the content that is contained within the ZoomPanControl. + /// + private Point _origContentMouseDownPoint; + + #endregion + + #region Constructors and Destructor + + public ZoomPanOverview() + { + InitializeComponent(); + + this.HorizontalContentAlignment = HorizontalAlignment.Center; + this.VerticalContentAlignment = VerticalAlignment.Center; + } + + /// + /// Static constructor to define metadata for the control (and link it to the style in Generic.xaml). + /// + static ZoomPanOverview() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(ZoomPanOverview), + new FrameworkPropertyMetadata(typeof(ZoomPanOverview))); + } + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _dragBorder = this.Template.FindName("PART_DraggingBorder", this) as Border; + _sizingBorder = this.Template.FindName("PART_SizingBorder", this) as Border; + _viewportCanvas = this.Template.FindName("PART_Content", this) as Canvas; + _viewportBox = this.Template.FindName("PART_Viewbox", this) as Viewbox; + } + + #endregion + + #region Mouse Event Handlers + + /// + /// Event raised on mouse down in the ZoomPanControl. + /// + protected override void OnMouseDown(MouseButtonEventArgs e) + { + base.OnMouseLeftButtonDown(e); + + var drawingPage = this.GetDrawingPage(); + if (drawingPage != null) + { + drawingPage.SaveZoom(); + } + _mouseHandlingMode = ZoomPanMouseHandlingMode.Panning; + _origContentMouseDownPoint = e.GetPosition(_viewportCanvas); + + if ((Keyboard.Modifiers & ModifierKeys.Shift) != 0) + { + // Shift + left- or right-down initiates zooming mode. + _mouseHandlingMode = ZoomPanMouseHandlingMode.DragZooming; + _dragBorder.Visibility = Visibility.Hidden; + _sizingBorder.Visibility = Visibility.Visible; + Canvas.SetLeft(_sizingBorder, _origContentMouseDownPoint.X); + Canvas.SetTop(_sizingBorder, _origContentMouseDownPoint.Y); + _sizingBorder.Width = 0; + _sizingBorder.Height = 0; + } + else + { + // 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. + _viewportCanvas.CaptureMouse(); + e.Handled = true; + } + } + + /// + /// Event raised on mouse up in the ZoomPanControl. + /// + protected override void OnMouseUp(MouseButtonEventArgs e) + { + base.OnMouseLeftButtonUp(e); + + if (_mouseHandlingMode == ZoomPanMouseHandlingMode.DragZooming) + { + var zoomAndPanControl = GetZoomPanControl(); + var curContentPoint = e.GetPosition(_viewportCanvas); + var rect = GetClip(curContentPoint, _origContentMouseDownPoint, new Point(0, 0), + new Point(_viewportCanvas.Width, _viewportCanvas.Height)); + zoomAndPanControl.AnimatedZoomTo(rect); + _dragBorder.Visibility = Visibility.Visible; + _sizingBorder.Visibility = Visibility.Hidden; + } + _mouseHandlingMode = ZoomPanMouseHandlingMode.None; + _viewportCanvas.ReleaseMouseCapture(); + e.Handled = true; + } + + /// + /// Event raised on mouse move in the ZoomPanControl. + /// + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + if (_mouseHandlingMode == ZoomPanMouseHandlingMode.Panning) + { + var curContentPoint = e.GetPosition(_viewportCanvas); + var rectangleDragVector = curContentPoint - _origContentMouseDownPoint; + // + // When in 'dragging rectangles' mode update the position of the rectangle as the user drags it. + // + _origContentMouseDownPoint = Clamp(e.GetPosition(_viewportCanvas)); + Canvas.SetLeft(_dragBorder, Canvas.GetLeft(_dragBorder) + rectangleDragVector.X); + Canvas.SetTop(_dragBorder, Canvas.GetTop(_dragBorder) + rectangleDragVector.Y); + } + else if (_mouseHandlingMode == ZoomPanMouseHandlingMode.DragZooming) + { + var curContentPoint = e.GetPosition(_viewportCanvas); + var rect = GetClip(curContentPoint, _origContentMouseDownPoint, + new Point(0, 0), new Point(_viewportCanvas.Width, _viewportCanvas.Height)); + + PositionBorderOnCanvas(_sizingBorder, rect); + } + + e.Handled = true; + } + + /// + /// Event raised with the double click command + /// + protected override void OnMouseDoubleClick(MouseButtonEventArgs e) + { + base.OnMouseDoubleClick(e); + + if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0) + { + var drawingPage = this.GetDrawingPage(); + if (drawingPage != null) + { + drawingPage.SaveZoom(); + } + var zoomAndPanControl = GetZoomPanControl(); + zoomAndPanControl.AnimatedSnapTo(e.GetPosition(_viewportCanvas)); + } + } + + #endregion + + #region Background--Visual Brush + + /// + /// The X coordinate of the content focus, this is the point that we are focusing on when zooming. + /// + public FrameworkElement Visual + { + get { return (FrameworkElement)GetValue(VisualProperty); } + set { SetValue(VisualProperty, value); } + } + + public static readonly DependencyProperty VisualProperty = DependencyProperty.Register("Visual", + typeof(FrameworkElement), typeof(ZoomPanOverview), new FrameworkPropertyMetadata(null, OnVisualChanged)); + + private static void OnVisualChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var c = (ZoomPanOverview)d; + + c.SetBackground(e.NewValue as FrameworkElement); + } + + private void SetBackground(FrameworkElement frameworkElement) + { + try + { + frameworkElement = frameworkElement ?? (DataContext as ContentControl)?.Content as FrameworkElement; + if (frameworkElement == null) + { + return; + } + var visualBrush = new VisualBrush + { + Visual = frameworkElement, + ViewboxUnits = BrushMappingMode.RelativeToBoundingBox, + ViewportUnits = BrushMappingMode.RelativeToBoundingBox, + AlignmentX = AlignmentX.Center, + AlignmentY = AlignmentY.Center, + TileMode = TileMode.None, + Stretch = Stretch.Uniform + }; + + if (!_sizingEvent) + { + frameworkElement.SizeChanged += (s, e) => + { + _viewportCanvas.Height = frameworkElement.ActualHeight; + _viewportCanvas.Width = frameworkElement.ActualWidth; + _viewportCanvas.Background = visualBrush; + }; + + _viewportCanvas.Height = frameworkElement.ActualHeight; + _viewportCanvas.Width = frameworkElement.ActualWidth; + _viewportCanvas.Background = visualBrush; + + _sizingEvent = true; + } + else + { + _viewportCanvas.Height = frameworkElement.ActualHeight; + _viewportCanvas.Width = frameworkElement.ActualWidth; + _viewportCanvas.Background = visualBrush; + } + } + catch (Exception ex) + { + Trace.TraceError(ex.ToString()); + } + } + + #endregion + + #region Private Methods + + private DrawingPage GetDrawingPage() + { + return this.DataContext as DrawingPage; + } + + private ZoomPanControl GetZoomPanControl() + { + var zoomAndPanControl = (this.DataContext as DrawingPage)?.ZoomPanContent; + if (zoomAndPanControl == null) + throw new NullReferenceException("DataContext is not of type ZoomPanControl"); + return zoomAndPanControl; + } + + /// + /// Moves and sized a border on a Canvas according to a Rect + /// + /// Border to be moved and sized + /// Rect that specifies the size and postion of the Border on the Canvas + private static void PositionBorderOnCanvas(Border border, Rect rect) + { + Canvas.SetLeft(border, rect.Left); + Canvas.SetTop(border, rect.Top); + border.Width = rect.Width; + border.Height = rect.Height; + } + + /// + /// Limits the extent of a Point to the area where X and Y are at least 0 + /// + /// Point to be clamped + /// + private static Point Clamp(Point value) + { + return new Point(Math.Max(value.X, 0), Math.Max(value.Y, 0)); + } + + /// + /// Limits the extent of a Point to the area between two points + /// + /// + /// Point specifiying the Top Left corner + /// Point specifiying the Bottom Right corner + /// The Point clamped by the Top Left and Bottom Right points + private static Point Clamp(Point value, Point topLeft, Point bottomRight) + { + return new Point(Math.Max(Math.Min(value.X, bottomRight.X), topLeft.X), + Math.Max(Math.Min(value.Y, bottomRight.Y), topLeft.Y)); + } + + /// + /// Return a Rect that specificed by two points and clipped by a rectangle specified + /// by two other points + /// + /// First Point specifing the rectangle to be clipped + /// Second Point specifing the rectangle to be clipped + /// Point specifiying the Top Left corner of the clipping rectangle + /// Point specifiying the Bottom Right corner of the clipping rectangle + /// Rectangle specified by two points clipped by the other two points + private static Rect GetClip(Point value1, Point value2, Point topLeft, Point bottomRight) + { + var point1 = Clamp(value1, topLeft, bottomRight); + var point2 = Clamp(value2, topLeft, bottomRight); + var newTopLeft = new Point(Math.Min(point1.X, point2.X), Math.Min(point1.Y, point2.Y)); + var size = new Size(Math.Abs(point1.X - point2.X), Math.Abs(point1.Y - point2.Y)); + return new Rect(newTopLeft, size); + } + + #endregion + } +} diff --git a/Samples/WpfTestSvgControl/ZoomPanScaleConverter.cs b/Samples/WpfTestSvgControl/ZoomPanScaleConverter.cs new file mode 100644 index 000000000..4588b7913 --- /dev/null +++ b/Samples/WpfTestSvgControl/ZoomPanScaleConverter.cs @@ -0,0 +1,39 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Markup; + +namespace WpfTestSvgControl +{ + /// + /// Used in MainWindow.xaml to converts a scale value to a percentage. + /// It is used to display the 50%, 100%, etc that appears underneath the zoom and pan control. + /// + [MarkupExtensionReturnType(typeof(double))] + public sealed class ZoomPanScaleConverter : MarkupExtension, IValueConverter + { + public override object ProvideValue(IServiceProvider serviceProvider) + { + return this; + } + + /// + /// Convert a fraction to a percentage. + /// + /// + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + // Round to an integer value whilst converting. + return (double)(int)((double)value * 100.0); + } + + /// + /// Convert a percentage back to a fraction. + /// + /// + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return (double)value / 100.0; + } + } +} diff --git a/Samples/WpfTestSvgControl/app.config b/Samples/WpfTestSvgControl/app.config new file mode 100644 index 000000000..de8289320 --- /dev/null +++ b/Samples/WpfTestSvgControl/app.config @@ -0,0 +1,3 @@ + + + diff --git a/Samples/WpfTestSvgSample/DrawingPage.xaml.cs b/Samples/WpfTestSvgSample/DrawingPage.xaml.cs index 63f260853..ee35c9447 100644 --- a/Samples/WpfTestSvgSample/DrawingPage.xaml.cs +++ b/Samples/WpfTestSvgSample/DrawingPage.xaml.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Globalization; using System.Threading.Tasks; +using System.Collections.Generic; using System.Windows; using System.Windows.Input; @@ -104,6 +105,11 @@ public partial class DrawingPage : Page private DispatcherTimer _dispatcherTimer; + private WpfDrawingDocument _drawingDocument; + + private EmbeddedImageSerializerVisitor _embeddedImageVisitor; + private IList _embeddedImages; + #endregion #region Constructors and Destructor @@ -127,11 +133,31 @@ public DrawingPage() _workingDir = new DirectoryInfo(workDir); + _embeddedImages = new List(); + + _embeddedImageVisitor = new EmbeddedImageSerializerVisitor(true); + _wpfSettings.Visitors.ImageVisitor = _embeddedImageVisitor; + + _embeddedImageVisitor.ImageCreated += OnEmbeddedImageCreated; + 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 @@ -159,11 +185,6 @@ public string WorkingDrawingDir { _fileReader.SaveXaml = Directory.Exists(_drawingDir); } - - if (_wpfSettings != null) - { - _wpfSettings.Visitors.ImageVisitor = new EmbeddedImageSerializerVisitor(true, _drawingDir); - } } } } @@ -202,8 +223,6 @@ public WpfDrawingSettings ConversionSettings Directory.Exists(_drawingDir)) { _fileReader.SaveXaml = Directory.Exists(_drawingDir); - - _wpfSettings.Visitors.ImageVisitor = new EmbeddedImageSerializerVisitor(true, _drawingDir); } } } @@ -244,12 +263,21 @@ public MainWindow MainWindow /// /// This allows the same property name to be used for direct and indirect access to the ZoomPanelControl control. /// - public ZoomPanControl ZoomPanContent => zoomPanControl; + 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 => svgViewer; + public SvgDrawingCanvas Viewer + { + get { + return svgViewer; + } + } #endregion @@ -271,6 +299,7 @@ public bool LoadDocument(string svgFilePath) this.UnloadDocument(true); _svgFilePath = svgFilePath; + _saveXaml = _optionSettings.ShowOutputFile; string fileExt = Path.GetExtension(svgFilePath); @@ -282,7 +311,12 @@ public bool LoadDocument(string svgFilePath) _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(); @@ -324,12 +358,6 @@ public Task LoadDocumentAsync(string svgFilePath) return Task.FromResult(false); } - DirectoryInfo workingDir = _workingDir; - if (_directoryInfo != null) - { - workingDir = _directoryInfo; - } - string fileExt = Path.GetExtension(svgFilePath); if (!(string.Equals(fileExt, SvgConverter.SvgExt, StringComparison.OrdinalIgnoreCase) || @@ -339,6 +367,23 @@ public Task LoadDocumentAsync(string svgFilePath) 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); @@ -346,23 +391,18 @@ public Task LoadDocumentAsync(string svgFilePath) _fileReader.SaveZaml = false; } - _isLoadingDrawing = true; - - this.UnloadDocument(true); - - _svgFilePath = svgFilePath; - - MemoryStream drawingStream = new MemoryStream(); + 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... +// 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; +// _fileReader.SaveXaml = saveXaml; + _drawingDocument = _fileReader.DrawingDocument; if (drawing != null) { XamlWriter.Save(drawing, drawingStream); @@ -397,6 +437,9 @@ public Task LoadDocumentAsync(string svgFilePath) zoomPanControl.AnimatedZoomTo(bounds); CommandManager.InvalidateRequerySuggested(); + + // The drawing changed, update the source... + _fileReader.Drawing = drawing; } _isLoadingDrawing = false; @@ -413,35 +456,71 @@ public Task LoadDocumentAsync(string svgFilePath) public void UnloadDocument(bool displayMessage = false) { - _svgFilePath = null; - - if (svgViewer != null) + try { - svgViewer.UnloadDiagrams(); + _svgFilePath = null; + _drawingDocument = null; - if (displayMessage) + if (svgViewer != null) { - var drawing = this.DrawText("Loading..."); + svgViewer.UnloadDiagrams(); + + if (displayMessage) + { + var drawing = this.DrawText("Loading..."); + + svgViewer.RenderDiagrams(drawing); + + Rect bounds = svgViewer.Bounds; + if (bounds.IsEmpty) + { + bounds = drawing.Bounds; + } - svgViewer.RenderDiagrams(drawing); + zoomPanControl.ZoomTo(bounds); + return; + } + } - Rect bounds = svgViewer.Bounds; - if (bounds.IsEmpty) + 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) { - bounds = drawing.Bounds; + 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 + } } - zoomPanControl.ZoomTo(bounds); - return; + _embeddedImages.Clear(); } } - - var drawRect = this.DrawRect(); - svgViewer.RenderDiagrams(drawRect); - - zoomPanControl.ZoomTo(drawRect.Bounds); - ClearPrevZoomRect(); - ClearNextZoomRect(); } public bool SaveDocument(string fileName) @@ -455,7 +534,6 @@ public bool SaveDocument(string fileName) { return false; } - return _fileReader.Save(fileName, true, false); } @@ -645,29 +723,37 @@ private void OnZoomPanMouseDown(object sender, MouseButtonEventArgs e) _origZoomAndPanControlMouseDownPoint = e.GetPosition(zoomPanControl); _origContentMouseDownPoint = e.GetPosition(svgViewer); - if ((Keyboard.Modifiers & ModifierKeys.Shift) != 0 && - (e.ChangedButton == MouseButton.Left || e.ChangedButton == MouseButton.Right)) + if (_mouseHandlingMode == ZoomPanMouseHandlingMode.SelectPoint || + _mouseHandlingMode == ZoomPanMouseHandlingMode.SelectRectangle) + { + } + else { - // Shift + left- or right-down initiates zooming mode. - _mouseHandlingMode = ZoomPanMouseHandlingMode.Zooming; + 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) + if (zoomPanControl != null && _canvasCursor != null) + { + zoomPanControl.Cursor = _canvasCursor; + } + } + else if (_mouseButtonDown == MouseButton.Left) { - zoomPanControl.Cursor = _canvasCursor; + // Just a plain old left-down initiates panning mode. + _mouseHandlingMode = ZoomPanMouseHandlingMode.Panning; } - } - 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; + if (_mouseHandlingMode != ZoomPanMouseHandlingMode.None) + { + // Capture the mouse so that we eventually receive the mouse up event. + zoomPanControl.CaptureMouse(); + e.Handled = true; + } } + } /// @@ -675,35 +761,42 @@ private void OnZoomPanMouseDown(object sender, MouseButtonEventArgs e) /// private void OnZoomPanMouseUp(object sender, MouseButtonEventArgs e) { - if (_mouseHandlingMode != ZoomPanMouseHandlingMode.None) + if (_mouseHandlingMode == ZoomPanMouseHandlingMode.SelectPoint || + _mouseHandlingMode == ZoomPanMouseHandlingMode.SelectRectangle) { - if (_mouseHandlingMode == ZoomPanMouseHandlingMode.Zooming) + } + else + { + if (_mouseHandlingMode != ZoomPanMouseHandlingMode.None) { - if (_mouseButtonDown == MouseButton.Left) + if (_mouseHandlingMode == ZoomPanMouseHandlingMode.Zooming) { - // Shift + left-click zooms in on the content. - ZoomIn(_origContentMouseDownPoint); + 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 (_mouseButtonDown == MouseButton.Right) + else if (_mouseHandlingMode == ZoomPanMouseHandlingMode.DragZooming) { - // Shift + left-click zooms out from the content. - ZoomOut(_origContentMouseDownPoint); + // 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; } - else if (_mouseHandlingMode == ZoomPanMouseHandlingMode.DragZooming) + + if (zoomPanControl != null && _canvasCursor != null) { - // When drag-zooming has finished we zoom in on the rectangle that was highlighted by the user. - ApplyDragZoomRect(); + zoomPanControl.Cursor = _canvasCursor; } - - zoomPanControl.ReleaseMouseCapture(); - _mouseHandlingMode = ZoomPanMouseHandlingMode.None; - e.Handled = true; - } - - if (zoomPanControl != null && _canvasCursor != null) - { - zoomPanControl.Cursor = _canvasCursor; } } @@ -712,67 +805,74 @@ private void OnZoomPanMouseUp(object sender, MouseButtonEventArgs e) /// private void OnZoomPanMouseMove(object sender, MouseEventArgs e) { - if (_mouseHandlingMode == ZoomPanMouseHandlingMode.Panning) + if (_mouseHandlingMode == ZoomPanMouseHandlingMode.SelectPoint || + _mouseHandlingMode == ZoomPanMouseHandlingMode.SelectRectangle) { - 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) + else { - if (zoomPanControl != null && _canvasCursor != null) + if (_mouseHandlingMode == ZoomPanMouseHandlingMode.Panning) { - zoomPanControl.Cursor = _canvasCursor; - } + if (zoomPanControl != null) + { + zoomPanControl.Cursor = _panToolCursor; + } - 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. + // The user is left-dragging the mouse. + // Pan the viewport by the appropriate amount. // - _mouseHandlingMode = ZoomPanMouseHandlingMode.DragZooming; - Point curContentMousePoint = e.GetPosition(svgViewer); - InitDragZoomRect(_origContentMouseDownPoint, curContentMousePoint); - } + Vector dragOffset = curContentMousePoint - _origContentMouseDownPoint; - e.Handled = true; - } - else if (_mouseHandlingMode == ZoomPanMouseHandlingMode.DragZooming) - { - if (zoomPanControl != null && _canvasCursor != null) + zoomPanControl.ContentOffsetX -= dragOffset.X; + zoomPanControl.ContentOffsetY -= dragOffset.Y; + + e.Handled = true; + } + else if (_mouseHandlingMode == ZoomPanMouseHandlingMode.Zooming) { - zoomPanControl.Cursor = _canvasCursor; + 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); + // + // 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; + e.Handled = true; + } } } diff --git a/Samples/WpfTestSvgSample/MainWindow.xaml.cs b/Samples/WpfTestSvgSample/MainWindow.xaml.cs index 1d3b5589a..434982334 100644 --- a/Samples/WpfTestSvgSample/MainWindow.xaml.cs +++ b/Samples/WpfTestSvgSample/MainWindow.xaml.cs @@ -1076,9 +1076,20 @@ private void CloseFile() string[] imageFiles = Directory.GetFiles(_drawingDir, "*.png"); if (imageFiles != null && imageFiles.Length != 0) { - foreach (var imageFile in imageFiles) + try { - File.Delete(imageFile); + foreach (var imageFile in imageFiles) + { + if (File.Exists(imageFile)) + { + File.Delete(imageFile); + } + } + } + catch (IOException ex) + { + Trace.TraceError(ex.ToString()); + // Image this, WPF will typically cache and/or lock loaded images } } } diff --git a/Samples/WpfTestSvgSample/ZoomPanMouseHandlingMode.cs b/Samples/WpfTestSvgSample/ZoomPanMouseHandlingMode.cs index adcf850bf..066c5d0fc 100644 --- a/Samples/WpfTestSvgSample/ZoomPanMouseHandlingMode.cs +++ b/Samples/WpfTestSvgSample/ZoomPanMouseHandlingMode.cs @@ -24,5 +24,9 @@ public enum ZoomPanMouseHandlingMode /// The user is holding down shift and left-mouse-button-dragging to select a region to zoom to. /// DragZooming, + + SelectPoint, + + SelectRectangle } } diff --git a/SharpVectorsAll.sln b/SharpVectorsAll.sln index d4e7b87d8..75706db8c 100644 --- a/SharpVectorsAll.sln +++ b/SharpVectorsAll.sln @@ -38,6 +38,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GdiW3cSvgTestSuite", "Sampl EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GdiSvgTestBox", "Samples\GdiSvgTestBox\GdiSvgTestBox.csproj", "{5A71211F-9EDA-4C43-8DDC-E3EB54843113}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfTestSvgControl", "Samples\WpfTestSvgControl\WpfTestSvgControl.csproj", "{28586359-004C-45B5-823E-38F714784B31}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -141,6 +143,12 @@ Global {5A71211F-9EDA-4C43-8DDC-E3EB54843113}.Documentation|Any CPU.Build.0 = Debug|Any CPU {5A71211F-9EDA-4C43-8DDC-E3EB54843113}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A71211F-9EDA-4C43-8DDC-E3EB54843113}.Release|Any CPU.Build.0 = Release|Any CPU + {28586359-004C-45B5-823E-38F714784B31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {28586359-004C-45B5-823E-38F714784B31}.Debug|Any CPU.Build.0 = Debug|Any CPU + {28586359-004C-45B5-823E-38F714784B31}.Documentation|Any CPU.ActiveCfg = Debug|Any CPU + {28586359-004C-45B5-823E-38F714784B31}.Documentation|Any CPU.Build.0 = Debug|Any CPU + {28586359-004C-45B5-823E-38F714784B31}.Release|Any CPU.ActiveCfg = Release|Any CPU + {28586359-004C-45B5-823E-38F714784B31}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -154,6 +162,7 @@ Global {D3B055DA-D97B-4B97-BFB6-18837FFA7C2D} = {A1967865-8F3B-4976-A7EC-8F91EAE4C964} {406733B7-B9C5-4100-8D10-F3CCE9C58B2C} = {A1967865-8F3B-4976-A7EC-8F91EAE4C964} {5A71211F-9EDA-4C43-8DDC-E3EB54843113} = {A1967865-8F3B-4976-A7EC-8F91EAE4C964} + {28586359-004C-45B5-823E-38F714784B31} = {A1967865-8F3B-4976-A7EC-8F91EAE4C964} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {ECBBFB65-D6D1-4727-BE82-54D52CE8FCF3} diff --git a/Source/SharpVectorConvertersWpf/EmbeddedImageSerializerVisitor.cs b/Source/SharpVectorConvertersWpf/EmbeddedImageSerializerVisitor.cs index 13d47afae..7a91cc780 100644 --- a/Source/SharpVectorConvertersWpf/EmbeddedImageSerializerVisitor.cs +++ b/Source/SharpVectorConvertersWpf/EmbeddedImageSerializerVisitor.cs @@ -14,22 +14,45 @@ namespace SharpVectors.Converters { + public sealed class EmbeddedImageSerializerArgs : EventArgs + { + public EmbeddedImageSerializerArgs(string imagePath, BitmapImage image) + { + this.ImagePath = imagePath; + this.Image = image; + } + + public string ImagePath { get; private set; } + public BitmapImage Image { get; private set; } + } + public sealed class EmbeddedImageSerializerVisitor : WpfEmbeddedImageVisitor { #region Public Private Fields public const string ImageExt = ".png"; + private int _imageCount; + private bool _saveImages; + private bool _converterFallback; private string _namePrefix; private string _saveDirectory; private IDictionary _imageCache; + private event EventHandler _imageCreated; + #endregion #region Constructors and Destructor + public EmbeddedImageSerializerVisitor(bool converterFallback) + : this(false, null) + { + _converterFallback = converterFallback; + } + public EmbeddedImageSerializerVisitor(string saveDirectory) : this(true, saveDirectory) { @@ -55,11 +78,22 @@ public EmbeddedImageSerializerVisitor(bool saveImages, string saveDirectory) Trace.TraceError(ex.ToString()); } + _imageCount = 1; _imageCache = new Dictionary(StringComparer.Ordinal); } #endregion + #region Public Events + + public event EventHandler ImageCreated + { + add { _imageCreated += value; } + remove { _imageCreated -= value; } + } + + #endregion + #region Public Properties public bool SaveImages @@ -67,13 +101,30 @@ public bool SaveImages get { return _saveImages; } + set { + _saveImages = value; + } + } + + public bool ConverterFallback + { + get { + return _converterFallback; + } + set { + _converterFallback = value; + } } + public string SaveDirectory { get { return _saveDirectory; } + set { + _saveDirectory = value; + } } public string ImageNamePrefix @@ -123,7 +174,35 @@ public override ImageSource Visit(SvgImageElement element, WpfDrawingContext con { if (!string.IsNullOrWhiteSpace(imageId) && _imageCache.ContainsKey(imageId)) { - return _imageCache[imageId]; + var cachedSource = _imageCache[imageId]; + if (cachedSource != null) + { + var cachedImage = cachedSource as BitmapImage; + if (cachedImage != null) + { + var imageUri = cachedImage.UriSource; + if (imageUri != null) + { + if (imageUri.IsFile && File.Exists(imageUri.LocalPath)) + { + return cachedImage; + } + _imageCache.Remove(imageId); + } + else if (cachedImage.StreamSource != null) + { + return cachedImage; + } + } + else + { + return cachedSource; + } + } + else + { + _imageCache.Remove(imageId); + } } } @@ -183,7 +262,9 @@ private ImageSource GetImage(SvgImageElement element, WpfDrawingContext context) using (GZipStream zipStream = new GZipStream(stream, CompressionMode.Decompress)) { using (var reader = new FileSvgReader(context.Settings)) + { return new DrawingImage(reader.Read(zipStream)); + } } } } @@ -192,23 +273,27 @@ private ImageSource GetImage(SvgImageElement element, WpfDrawingContext context) using (var stream = new MemoryStream(imageBytes)) { using (var reader = new FileSvgReader(context.Settings)) + { return new DrawingImage(reader.Read(stream)); + } } } } + var memStream = new MemoryStream(imageBytes); + BitmapImage imageSource = new BitmapImage(); imageSource.BeginInit(); + imageSource.StreamSource = memStream; + imageSource.CacheOption = BitmapCacheOption.OnLoad; imageSource.CreateOptions = BitmapCreateOptions.PreservePixelFormat; - imageSource.StreamSource = new MemoryStream(imageBytes); imageSource.EndInit(); - imageSource.Freeze(); + string imagePath = null; - if (isSavingImages && !string.IsNullOrWhiteSpace(imagesDir) - && Directory.Exists(imagesDir)) + if (isSavingImages && !string.IsNullOrWhiteSpace(imagesDir) && Directory.Exists(imagesDir)) { - var imagePath = this.GetImagePath(imagesDir); + imagePath = this.GetImagePath(imagesDir); BitmapEncoder encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(imageSource)); @@ -218,19 +303,47 @@ private ImageSource GetImage(SvgImageElement element, WpfDrawingContext context) encoder.Save(fileStream); } - BitmapImage savedSource = new BitmapImage(); + imageSource.CreateOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreImageCache; + imageSource.UriSource = new Uri(imagePath); + + //imageSource.StreamSource.Dispose(); + //imageSource = null; - savedSource.BeginInit(); - savedSource.CacheOption = BitmapCacheOption.OnLoad; - savedSource.CreateOptions = BitmapCreateOptions.PreservePixelFormat; - savedSource.UriSource = new Uri(imagePath); - savedSource.EndInit(); + //BitmapImage savedSource = new BitmapImage(); - savedSource.Freeze(); + //savedSource.BeginInit(); + //savedSource.CacheOption = BitmapCacheOption.None; + //savedSource.CreateOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreImageCache; + //savedSource.UriSource = new Uri(imagePath); + //savedSource.EndInit(); - return savedSource; + //savedSource.Freeze(); + + //if (_imageCreated != null) + //{ + // var eventArgs = new EmbeddedImageSerializerArgs(imagePath, savedSource); + // _imageCreated.Invoke(this, eventArgs); + //} + + //return savedSource; + } + else if (_converterFallback) + { + //if (_imageCreated != null) + //{ + // var eventArgs = new EmbeddedImageSerializerArgs(imagePath, imageSource); + // _imageCreated.Invoke(this, eventArgs); + //} + return new EmbeddedBitmapSource(memStream, imageSource); + } + if (_imageCreated != null) + { + var eventArgs = new EmbeddedImageSerializerArgs(imagePath, imageSource); + _imageCreated.Invoke(this, eventArgs); } + imageSource.Freeze(); + return imageSource; } @@ -241,7 +354,7 @@ private string GetImagePath(string savedDir) savedDir = _saveDirectory; } - int imageCount = 1; + int imageCount = _imageCount; var nextPath = Path.Combine(savedDir, string.Format("{0}{1}{2}", _namePrefix, imageCount, ImageExt)); @@ -252,6 +365,8 @@ private string GetImagePath(string savedDir) _namePrefix, imageCount, ImageExt)); } + _imageCount = imageCount + 1; + return nextPath; } diff --git a/Source/SharpVectorConvertersWpf/FileSvgReader.cs b/Source/SharpVectorConvertersWpf/FileSvgReader.cs index 5b6545d45..792ba952d 100644 --- a/Source/SharpVectorConvertersWpf/FileSvgReader.cs +++ b/Source/SharpVectorConvertersWpf/FileSvgReader.cs @@ -187,7 +187,7 @@ public string ZamlFile } /// - /// Gets the last created drawing. + /// Gets or sets the last created drawing. /// /// /// A specifying the last converted drawing. @@ -197,6 +197,13 @@ public DrawingGroup Drawing get { return _drawing; } + set { + _drawing = value; + if (_drawingDocument != null) + { + _drawingDocument.EnumerateDrawing(_drawing); + } + } } public WpfDrawingDocument DrawingDocument diff --git a/Source/SharpVectorConvertersWpf/SvgConverter.cs b/Source/SharpVectorConvertersWpf/SvgConverter.cs index 1dc91b487..45471eaf7 100644 --- a/Source/SharpVectorConvertersWpf/SvgConverter.cs +++ b/Source/SharpVectorConvertersWpf/SvgConverter.cs @@ -287,6 +287,16 @@ protected virtual void EndProcessing() { _wpfRenderer.EndRender(); } + + if (_wpfSettings != null) + { + var visitors = _wpfSettings.Visitors; + if (visitors != null) + { + visitors.Uninitialize(); + } + } + //TODO: Currently, experimental GC.Collect(); } diff --git a/Source/SharpVectorRenderingGdi/SvgAlertArgs.cs b/Source/SharpVectorRenderingGdi/SvgAlertArgs.cs index 78e418d7d..684d7c3f1 100644 --- a/Source/SharpVectorRenderingGdi/SvgAlertArgs.cs +++ b/Source/SharpVectorRenderingGdi/SvgAlertArgs.cs @@ -9,7 +9,7 @@ public SvgAlertArgs(string message) this.Message = message; } - public string Message { get; } + public string Message { get; private set; } public bool Handled { get; set; } } } diff --git a/Source/SharpVectorRenderingGdi/SvgErrorArgs.cs b/Source/SharpVectorRenderingGdi/SvgErrorArgs.cs index 44270fc9c..e547bb639 100644 --- a/Source/SharpVectorRenderingGdi/SvgErrorArgs.cs +++ b/Source/SharpVectorRenderingGdi/SvgErrorArgs.cs @@ -17,8 +17,8 @@ public SvgErrorArgs(string message, Exception exception) public bool Handled { get; set; } - public string Message { get; } + public string Message { get; private set; } - public Exception Exception { get; } + public Exception Exception { get; private set; } } } diff --git a/Source/SharpVectorRenderingWpf/SharpVectors.Rendering.Wpf.csproj b/Source/SharpVectorRenderingWpf/SharpVectors.Rendering.Wpf.csproj index c669a04c0..845171133 100644 --- a/Source/SharpVectorRenderingWpf/SharpVectors.Rendering.Wpf.csproj +++ b/Source/SharpVectorRenderingWpf/SharpVectors.Rendering.Wpf.csproj @@ -105,6 +105,7 @@ + diff --git a/Source/SharpVectorRenderingWpf/Wpf/WpfDrawingContext.cs b/Source/SharpVectorRenderingWpf/Wpf/WpfDrawingContext.cs index 4e86bc095..1c1e11ce0 100644 --- a/Source/SharpVectorRenderingWpf/Wpf/WpfDrawingContext.cs +++ b/Source/SharpVectorRenderingWpf/Wpf/WpfDrawingContext.cs @@ -5,6 +5,8 @@ using System.Windows; using System.Windows.Media; +using SharpVectors.Runtime; + namespace SharpVectors.Renderers.Wpf { public sealed class WpfDrawingContext : DependencyObject, IEnumerable @@ -589,6 +591,17 @@ public void RegisterDrawing(string elementId, string uniqueId, Drawing drawing) { if (_drawingDocument != null) { + if (_settings != null && _settings.IncludeRuntime) + { + if (!string.IsNullOrWhiteSpace(elementId)) + { + SvgObject.SetId(drawing, elementId); + } + if (!string.IsNullOrWhiteSpace(uniqueId)) + { + SvgObject.SetUniqueId(drawing, uniqueId); + } + } _drawingDocument.Add(elementId, uniqueId, drawing); } } diff --git a/Source/SharpVectorRenderingWpf/Wpf/WpfDrawingDocument.cs b/Source/SharpVectorRenderingWpf/Wpf/WpfDrawingDocument.cs index f82a0aff7..8f7452ef9 100644 --- a/Source/SharpVectorRenderingWpf/Wpf/WpfDrawingDocument.cs +++ b/Source/SharpVectorRenderingWpf/Wpf/WpfDrawingDocument.cs @@ -1,7 +1,6 @@ using System; +using System.Threading.Tasks; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Windows; using System.Windows.Media; @@ -13,6 +12,8 @@ namespace SharpVectors.Renderers.Wpf { public sealed class WpfDrawingDocument : DependencyObject { + #region Private Fields + private Transform _displayTransform; private bool _isEnumerated; @@ -21,6 +22,10 @@ public sealed class WpfDrawingDocument : DependencyObject private IDictionary _idMap; private IDictionary _guidMap; + #endregion + + #region Constructors and Destructor + public WpfDrawingDocument() { _idMap = new Dictionary(StringComparer.Ordinal); @@ -28,6 +33,10 @@ public WpfDrawingDocument() _displayTransform = Transform.Identity; } + #endregion + + #region Public Properties + public Transform DisplayTransform { get { @@ -111,6 +120,10 @@ public ICollection ElementUniqueNames } } + #endregion + + #region Public Methods + public void Initialize(SvgDocument svgDocument, DrawingGroup svgDrawing) { _svgDocument = svgDocument; @@ -191,7 +204,7 @@ public Drawing GetById(string elementId) var svgElement = _svgDocument.GetSvgById(elementId); if (svgElement != null) { - this.EnumerateDrawing(); + // this.EnumerateDrawing(); } if (_idMap.Count != 0 && _idMap.ContainsKey(elementId)) { @@ -254,11 +267,71 @@ public SvgElement GetSvgByUniqueId(string uniqueId) return _svgDocument.GetSvgByUniqueId(uniqueId); } + public void EnumerateDrawing(DrawingGroup drawing) + { + if (drawing == null) + { + return; + } + + _svgDrawing = drawing; + + //TODO: Trying a background run... + Task.Factory.StartNew(() => + { + this.EnumerateDrawing(); + }); + } + + public WpfHitTestResult HitTest(Point point) + { + var svgDrawing = this.PerformHitTest(point); + if (svgDrawing == null) + { + return WpfHitTestResult.Empty; + } + string uniqueId = SvgObject.GetUniqueId(svgDrawing); + if (string.IsNullOrWhiteSpace(uniqueId)) + { + return WpfHitTestResult.Empty; + } + var svgElement = this.GetSvgByUniqueId(uniqueId); + if (svgElement == null) + { + return WpfHitTestResult.Empty; + } + + return new WpfHitTestResult(point, svgElement, svgDrawing); + } + + public WpfHitTestResult HitTest(Rect rect, IntersectionDetail detail) + { + var svgDrawing = this.PerformHitTest(rect, detail); + if (svgDrawing == null) + { + return WpfHitTestResult.Empty; + } + string uniqueId = SvgObject.GetUniqueId(svgDrawing); + if (string.IsNullOrWhiteSpace(uniqueId)) + { + return WpfHitTestResult.Empty; + } + var svgElement = this.GetSvgByUniqueId(uniqueId); + if (svgElement == null) + { + return WpfHitTestResult.Empty; + } + + return new WpfHitTestResult(rect, svgElement, svgDrawing); + } + + #endregion + #region Private Methods #region HitTest Point - private Drawing HitTest(Point pt) + private Drawing PerformHitTest(Point pt) { if (_svgDrawing == null) { @@ -292,7 +365,7 @@ private Drawing HitTest(Point pt) foundDrawing = drawing; break; } - else if (SvgObject.GetType(groupDrawing) == SvgObjectType.Text && + if (SvgObject.GetType(groupDrawing) == SvgObjectType.Text && groupDrawing.Bounds.Contains(ptDisplay)) { foundDrawing = drawing; @@ -448,7 +521,7 @@ private bool HitTestDrawing(DrawingGroup group, Point pt) #region HitTest Geometry - private Drawing HitTest(Rect rect, IntersectionDetail detail) + private Drawing PerformHitTest(Rect rect, IntersectionDetail detail) { if (_svgDrawing == null) { @@ -657,9 +730,52 @@ private void EnumerateDrawing() { return; } + _idMap = new Dictionary(StringComparer.Ordinal); + _guidMap = new Dictionary(StringComparer.Ordinal); + + this.EnumDrawingGroup(_svgDrawing); + _isEnumerated = true; } + // Enumerate the drawings in the DrawingGroup. + private void EnumDrawingGroup(DrawingGroup drawingGroup) + { + DrawingCollection dc = drawingGroup.Children; + + DrawingGroup groupDrawing = null; + + // Enumerate the drawings in the DrawingCollection. + foreach (Drawing drawing in dc) + { + string objectId = SvgObject.GetId(drawing); + if (!string.IsNullOrWhiteSpace(objectId)) + { + _idMap.Add(objectId, drawing); + } + string objectName = (string)drawing.GetValue(FrameworkElement.NameProperty); + if (!string.IsNullOrWhiteSpace(objectName)) + { + _idMap[objectName] = drawing; + } + string uniqueId = SvgObject.GetUniqueId(drawing); + if (!string.IsNullOrWhiteSpace(uniqueId)) + { + _guidMap.Add(uniqueId, drawing); + } + + // If the drawing is a DrawingGroup, call the function recursively. + if (TryCast.Cast(drawing, out groupDrawing)) + { + SvgObjectType objectType = SvgObject.GetType(groupDrawing); + if (objectType != SvgObjectType.Text) + { + EnumDrawingGroup(groupDrawing); + } + } + } + } + #endregion } } diff --git a/Source/SharpVectorRenderingWpf/Wpf/WpfHitTestResult.cs b/Source/SharpVectorRenderingWpf/Wpf/WpfHitTestResult.cs new file mode 100644 index 000000000..6c0470c3e --- /dev/null +++ b/Source/SharpVectorRenderingWpf/Wpf/WpfHitTestResult.cs @@ -0,0 +1,83 @@ +using System; + +using System.Windows; +using System.Windows.Media; + +using SharpVectors.Dom.Svg; + +namespace SharpVectors.Renderers.Wpf +{ + public class WpfHitTestResult + { + public static readonly WpfHitTestResult Empty = new WpfHitTestResult(); + + private Point? _point; + private Rect? _rect; + private Drawing _drawing; + private SvgElement _element; + + private WpfHitTestResult() + { + } + + public WpfHitTestResult(int pointX, int pointY, SvgElement element, Drawing drawing) + : this(new Point(pointX, pointY), element, drawing) + { + } + + public WpfHitTestResult(Point point, SvgElement element, Drawing drawing) + { + _point = point; + _element = element; + _drawing = drawing; + } + + public WpfHitTestResult(Rect rect, SvgElement element, Drawing drawing) + { + _rect = rect; + _element = element; + _drawing = drawing; + } + + public bool IsHit + { + get { + return (_element != null && _drawing != null); + } + } + + /// + /// The point value to hit test against. + /// + public Point? Point + { + get { + return _point; + } + } + + public Rect? Rect + { + get { + return _rect; + } + } + + /// + /// Gets the SVG object that was hit. + /// + public SvgElement Element + { + get { + return _element; + } + } + + public Drawing Drawing + { + get { + return _drawing; + } + } + } +} diff --git a/Source/SharpVectorRenderingWpf/Wpf/WpfImageRendering.cs b/Source/SharpVectorRenderingWpf/Wpf/WpfImageRendering.cs index 8486ac89f..625d68744 100644 --- a/Source/SharpVectorRenderingWpf/Wpf/WpfImageRendering.cs +++ b/Source/SharpVectorRenderingWpf/Wpf/WpfImageRendering.cs @@ -346,12 +346,12 @@ public override void Render(WpfDrawingRenderer renderer) if (!_idAssigned && !string.IsNullOrWhiteSpace(elementId) && !context.IsRegisteredId(elementId)) { - SvgObject.SetName(drawGroup, elementId); - context.RegisterId(elementId); if (context.IncludeRuntime) { + SvgObject.SetName(drawGroup, elementId); + SvgObject.SetId(drawGroup, elementId); } } @@ -363,12 +363,12 @@ public override void Render(WpfDrawingRenderer renderer) { if (!_idAssigned && !string.IsNullOrWhiteSpace(elementId) && !context.IsRegisteredId(elementId)) { - SvgObject.SetName(imageSource, elementId); - context.RegisterId(elementId); if (context.IncludeRuntime) { + SvgObject.SetName(imageSource, elementId); + SvgObject.SetId(imageSource, elementId); } } @@ -501,7 +501,7 @@ private ImageSource GetBitmap(SvgImageElement element, WpfDrawingContext context imageSource.CacheOption = BitmapCacheOption.OnLoad; imageSource.CreateOptions = BitmapCreateOptions.IgnoreImageCache | BitmapCreateOptions.PreservePixelFormat; - imageSource.UriSource = imageUri; + imageSource.UriSource = imageUri; imageSource.EndInit(); // imageSource.Freeze(); diff --git a/Source/SharpVectorRenderingWpf/Wpf/WpfRenderingBase.cs b/Source/SharpVectorRenderingWpf/Wpf/WpfRenderingBase.cs index e55946240..9b31c9b29 100644 --- a/Source/SharpVectorRenderingWpf/Wpf/WpfRenderingBase.cs +++ b/Source/SharpVectorRenderingWpf/Wpf/WpfRenderingBase.cs @@ -165,9 +165,11 @@ protected void Rendered(Drawing drawing) { if (drawing != null && _context != null) { - if (string.IsNullOrWhiteSpace(_elementId)) + if (string.IsNullOrWhiteSpace(_elementId) + || !string.Equals(_elementId, _svgElement.Id)) { - _elementId = this.GetElementName(); + //_elementId = this.GetElementName(); + _elementId = _svgElement.Id; } if (string.IsNullOrWhiteSpace(_uniqueId)) { diff --git a/Source/SharpVectorRenderingWpf/Wpf/WpfSvgRendering.cs b/Source/SharpVectorRenderingWpf/Wpf/WpfSvgRendering.cs index 6f97dd92d..257bd0dd0 100644 --- a/Source/SharpVectorRenderingWpf/Wpf/WpfSvgRendering.cs +++ b/Source/SharpVectorRenderingWpf/Wpf/WpfSvgRendering.cs @@ -156,8 +156,8 @@ public override void BeforeRender(WpfDrawingRenderer renderer) // We have already applied the transform, which will translate to the start point... if (transform is TranslateTransform) { - _drawGroup.ClipGeometry = new RectangleGeometry( - new Rect(0, 0, elmRect.Width, elmRect.Height)); + //_drawGroup.ClipGeometry = new RectangleGeometry( + // new Rect(0, 0, elmRect.Width, elmRect.Height)); } else { @@ -198,7 +198,11 @@ public override void Render(WpfDrawingRenderer renderer) } // Register this drawing with the Drawing-Document... - this.Rendered(_drawGroup); + // ...but not the root SVG object, since there is not point for that + if (!_isRoot) + { + this.Rendered(_drawGroup); + } } public override void AfterRender(WpfDrawingRenderer renderer) diff --git a/Source/SharpVectorRuntimeWpf/EmbeddedBitmapSource.cs b/Source/SharpVectorRuntimeWpf/EmbeddedBitmapSource.cs index ed4b37175..1a84981e8 100644 --- a/Source/SharpVectorRuntimeWpf/EmbeddedBitmapSource.cs +++ b/Source/SharpVectorRuntimeWpf/EmbeddedBitmapSource.cs @@ -54,6 +54,33 @@ public EmbeddedBitmapSource(MemoryStream stream) this.EndInit(); } + public EmbeddedBitmapSource(MemoryStream stream, BitmapImage image) + : this() + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + _stream = stream; + // Associated this class with source. + this.BeginInit(); + + if (image == null) + { + _bitmap = new BitmapImage(); + + _bitmap.BeginInit(); + _bitmap.StreamSource = _stream; + _bitmap.EndInit(); + } + else + { + _bitmap = image; + } + + this.InitWicInfo(_bitmap); + this.EndInit(); + } + #endregion Constructors #region Public Properties diff --git a/Source/SharpVectorRuntimeWpf/SvgDrawingCanvas.cs b/Source/SharpVectorRuntimeWpf/SvgDrawingCanvas.cs index 250c90672..c307805c8 100644 --- a/Source/SharpVectorRuntimeWpf/SvgDrawingCanvas.cs +++ b/Source/SharpVectorRuntimeWpf/SvgDrawingCanvas.cs @@ -25,7 +25,7 @@ public class SvgDrawingCanvas : Canvas { #region Private Fields - private static readonly Action EmptyDelegate = delegate { }; + //private static readonly Action EmptyDelegate = delegate { }; private bool _drawForInteractivity; diff --git a/Source/SharpVectorRuntimeWpf/SvgObject.cs b/Source/SharpVectorRuntimeWpf/SvgObject.cs index 165ed9927..df410a8da 100644 --- a/Source/SharpVectorRuntimeWpf/SvgObject.cs +++ b/Source/SharpVectorRuntimeWpf/SvgObject.cs @@ -16,6 +16,10 @@ public static class SvgObject DependencyProperty.RegisterAttached("Id", typeof(string), typeof(SvgObject), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.None)); + public static readonly DependencyProperty UniqueIdProperty = + DependencyProperty.RegisterAttached("UniqueId", typeof(string), typeof(SvgObject), + new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.None)); + public static readonly DependencyProperty ClassProperty = DependencyProperty.RegisterAttached("Class", typeof(string), typeof(SvgObject), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.None)); @@ -61,6 +65,16 @@ public static string GetId(DependencyObject element) return (string)element.GetValue(IdProperty); } + public static void SetUniqueId(DependencyObject element, string value) + { + element.SetValue(UniqueIdProperty, value); + } + + public static string GetUniqueId(DependencyObject element) + { + return (string)element.GetValue(UniqueIdProperty); + } + public static void SetClass(DependencyObject element, string value) { element.SetValue(ClassProperty, value);