From 47d7ba0834d8dd989f84245f22ee502ea3b1f0e4 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Mon, 29 Dec 2014 11:33:18 +0100 Subject: [PATCH] Support for drawing a Polyline --- samples/XamlDesigner/XamlDesigner.sln | 8 +- .../Extensions/CanvasDrawLineBehavior.cs | 82 +++++----- .../Extensions/CanvasDrawPolyLineBehavior.cs | 148 ++++++++++++++++++ .../Project/Services/CreateComponentTool.cs | 4 +- .../Project/Services/MouseGestureBase.cs | 71 ++++++++- .../Project/WpfDesign.Designer.csproj | 1 + ...awItemBehavior.cs => DrawItemExtension.cs} | 2 +- .../WpfDesign/Project/WpfDesign.csproj | 2 +- 8 files changed, 266 insertions(+), 52 deletions(-) create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasDrawPolyLineBehavior.cs rename src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/{DrawItemBehavior.cs => DrawItemExtension.cs} (97%) diff --git a/samples/XamlDesigner/XamlDesigner.sln b/samples/XamlDesigner/XamlDesigner.sln index e4a9c002ca..08bd78a71d 100644 --- a/samples/XamlDesigner/XamlDesigner.sln +++ b/samples/XamlDesigner/XamlDesigner.sln @@ -1,7 +1,9 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -# SharpDevelop 5.0 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +# SharpDevelop 5.1 +VisualStudioVersion = 12.0.20827.3 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlDesigner", "XamlDesigner.csproj", "{27DA2B5C-2AAA-4478-AB00-3E184273C241}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfDesign", "..\..\src\AddIns\DisplayBindings\WpfDesign\WpfDesign\Project\WpfDesign.csproj", "{66A378A1-E9F4-4AD5-8946-D0EC06C2902F}" diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasDrawLineBehavior.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasDrawLineBehavior.cs index 7d16163d2c..4b48228c7d 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasDrawLineBehavior.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasDrawLineBehavior.cs @@ -29,7 +29,7 @@ using ICSharpCode.WpfDesign.Designer.Services; namespace ICSharpCode.WpfDesign.Designer.Extensions { [ExtensionFor(typeof(Canvas))] - public class CanvasDrawLineBehavior : BehaviorExtension, IDrawItemBehavior + public class CanvasDrawLineBehavior : BehaviorExtension, IDrawItemExtension { private ChangeGroup changeGroup; @@ -42,14 +42,6 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions return item; } - protected override void OnInitialized() - { - base.OnInitialized(); - if (ExtendedItem.ContentProperty == null || Metadata.IsPlacementDisabled(ExtendedItem.ComponentType)) - return; - ExtendedItem.AddBehavior(typeof(IDrawItemBehavior), this); - } - #region IDrawItemBehavior implementation public bool CanItemBeDrawn(Type createItemType) @@ -82,50 +74,50 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions } #endregion - } - - sealed class DrawLineMouseGesture : ClickOrDragMouseGesture - { - private LineHandlerExtension l; - private ChangeGroup changeGroup; - - public DrawLineMouseGesture(LineHandlerExtension l, IInputElement relativeTo, ChangeGroup changeGroup) - { - this.l = l; - this.positionRelativeTo = relativeTo; - this.changeGroup = changeGroup; - } - - protected override void OnMouseMove(object sender, MouseEventArgs e) - { - base.OnMouseMove(sender, e); - l.DragListener.ExternalMouseMove(e); - } - protected override void OnMouseUp(object sender, MouseButtonEventArgs e) + sealed class DrawLineMouseGesture : ClickOrDragMouseGesture { - l.DragListener.ExternalStop(); - if (changeGroup != null) + private LineHandlerExtension l; + private ChangeGroup changeGroup; + + public DrawLineMouseGesture(LineHandlerExtension l, IInputElement relativeTo, ChangeGroup changeGroup) { - changeGroup.Commit(); - changeGroup = null; + this.l = l; + this.positionRelativeTo = relativeTo; + this.changeGroup = changeGroup; } - base.OnMouseUp(sender, e); - } - - protected override void OnStopped() - { - if (changeGroup != null) + + protected override void OnMouseMove(object sender, MouseEventArgs e) { - changeGroup.Abort(); - changeGroup = null; + base.OnMouseMove(sender, e); + l.DragListener.ExternalMouseMove(e); } - if (services.Tool.CurrentTool is CreateComponentTool) + + protected override void OnMouseUp(object sender, MouseButtonEventArgs e) { - services.Tool.CurrentTool = services.Tool.PointerTool; + l.DragListener.ExternalStop(); + if (changeGroup != null) + { + changeGroup.Commit(); + changeGroup = null; + } + base.OnMouseUp(sender, e); } - base.OnStopped(); + + protected override void OnStopped() + { + if (changeGroup != null) + { + changeGroup.Abort(); + changeGroup = null; + } + if (services.Tool.CurrentTool is CreateComponentTool) + { + services.Tool.CurrentTool = services.Tool.PointerTool; + } + base.OnStopped(); + } + } - } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasDrawPolyLineBehavior.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasDrawPolyLineBehavior.cs new file mode 100644 index 0000000000..244910d690 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasDrawPolyLineBehavior.cs @@ -0,0 +1,148 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Shapes; +using ICSharpCode.WpfDesign.Extensions; +using ICSharpCode.WpfDesign.Designer.Services; + +namespace ICSharpCode.WpfDesign.Designer.Extensions +{ + [ExtensionFor(typeof(Canvas))] + public class CanvasDrawPolyLineBehavior : BehaviorExtension, IDrawItemExtension + { + private ChangeGroup changeGroup; + + DesignItem CreateItem(DesignContext context, Type componentType) + { + object newInstance = context.Services.ExtensionManager.CreateInstanceWithCustomInstanceFactory(componentType, null); + DesignItem item = context.Services.Component.RegisterComponentForDesigner(newInstance); + changeGroup = item.OpenGroup("Draw Polyline"); + context.Services.ExtensionManager.ApplyDefaultInitializers(item); + return item; + } + + #region IDrawItemBehavior implementation + + public bool CanItemBeDrawn(Type createItemType) + { + return createItemType == typeof(Polyline); + } + + public void StartDrawItem(DesignItem clickedOn, Type createItemType, IDesignPanel panel, System.Windows.Input.MouseEventArgs e) + { + var createdItem = CreateItem(panel.Context, createItemType); + + var startPoint = e.GetPosition(clickedOn.View); + var operation = PlacementOperation.TryStartInsertNewComponents(clickedOn, + new DesignItem[] { createdItem }, + new Rect[] { new Rect(startPoint.X, startPoint.Y, double.NaN, double.NaN) }, + PlacementType.AddItem); + if (operation != null) { + createdItem.Services.Selection.SetSelectedComponents(new DesignItem[] { createdItem }); + operation.Commit(); + } + + createdItem.Properties[Shape.StrokeProperty].SetValue(Colors.Black); + createdItem.Properties[Shape.StrokeThicknessProperty].SetValue(2d); + createdItem.Properties[Shape.StretchProperty].SetValue(Stretch.None); + + createdItem.Properties[Polyline.PointsProperty].CollectionElements.Add(createdItem.Services.Component.RegisterComponentForDesigner(new Point(0,0))); + + new DrawPolylineMouseGesture(createdItem, clickedOn.View, changeGroup).Start(panel, (MouseButtonEventArgs) e); + } + + #endregion + + sealed class DrawPolylineMouseGesture : ClickOrDragMouseGesture + { + private ChangeGroup changeGroup; + private DesignItem newLine; + private Point startPoint; + + public DrawPolylineMouseGesture(DesignItem newLine, IInputElement relativeTo, ChangeGroup changeGroup) + { + this.newLine = newLine; + this.positionRelativeTo = relativeTo; + this.changeGroup = changeGroup; + + startPoint = Mouse.GetPosition(null); + } + + protected override void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + e.Handled = true; + base.OnPreviewMouseLeftButtonDown(sender, e); + } + + protected override void OnMouseMove(object sender, MouseEventArgs e) + { + var delta = e.GetPosition(null) - startPoint; + var point = new Point(delta.X, delta.Y); + + if (((Polyline)newLine.View).Points.Count <= 1) + newLine.Properties[Polyline.PointsProperty].CollectionElements.Add(newLine.Services.Component.RegisterComponentForDesigner(point)); + newLine.Properties[Polyline.PointsProperty].CollectionElements.RemoveAt(((Polyline)newLine.View).Points.Count - 1); + newLine.Properties[Polyline.PointsProperty].CollectionElements.Add(newLine.Services.Component.RegisterComponentForDesigner(point)); + } + + protected override void OnMouseUp(object sender, MouseButtonEventArgs e) + { + var delta = e.GetPosition(null) - startPoint; + var point = new Point(delta.X, delta.Y); + + newLine.Properties[Polyline.PointsProperty].CollectionElements.Add(newLine.Services.Component.RegisterComponentForDesigner(point)); + } + + protected override void OnMouseDoubleClick(object sender, MouseButtonEventArgs e) + { + base.OnMouseDoubleClick(sender, e); + + newLine.Properties[Polyline.PointsProperty].CollectionElements.RemoveAt(((Polyline)newLine.View).Points.Count - 1); + + if (changeGroup != null) + { + changeGroup.Commit(); + changeGroup = null; + } + + Stop(); + } + + protected override void OnStopped() + { + if (changeGroup != null) + { + changeGroup.Abort(); + changeGroup = null; + } + if (services.Tool.CurrentTool is CreateComponentTool) + { + services.Tool.CurrentTool = services.Tool.PointerTool; + } + base.OnStopped(); + } + + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/CreateComponentTool.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/CreateComponentTool.cs index 7e9f9ba767..88469fd72f 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/CreateComponentTool.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/CreateComponentTool.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Linq; using System.Windows; using System; using System.Diagnostics; @@ -202,7 +203,8 @@ namespace ICSharpCode.WpfDesign.Designer.Services IDesignPanel designPanel = (IDesignPanel)sender; DesignPanelHitTestResult result = designPanel.HitTest(e.GetPosition(designPanel), false, true, HitTestType.Default); if (result.ModelHit != null) { - var drawItembehavior = result.ModelHit.GetBehavior(); + var darwItemBehaviors = result.ModelHit.Extensions.OfType(); + var drawItembehavior = darwItemBehaviors.FirstOrDefault(x => x.CanItemBeDrawn(componentType)); if (drawItembehavior != null && drawItembehavior.CanItemBeDrawn(componentType)) { drawItembehavior.StartDrawItem(result.ModelHit, componentType, designPanel, e); } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/MouseGestureBase.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/MouseGestureBase.cs index ae5cc76262..75b4d5551e 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/MouseGestureBase.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/MouseGestureBase.cs @@ -18,6 +18,8 @@ using System; using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; using System.Windows.Input; namespace ICSharpCode.WpfDesign.Designer.Services @@ -71,6 +73,10 @@ namespace ICSharpCode.WpfDesign.Designer.Services designPanel.MouseMove += OnMouseMove; designPanel.MouseUp += OnMouseUp; designPanel.KeyDown += OnKeyDown; + designPanel.PreviewMouseLeftButtonDown += OnPreviewMouseLeftButtonDown; + designPanel.PreviewMouseLeftButtonUp += OnPreviewMouseLeftButtonUp; + designPanel.PreviewMouseRightButtonDown += OnPreviewMouseRightButtonDown; + designPanel.PreviewMouseRightButtonUp += OnPreviewMouseRightButtonUp; } void UnRegisterEvents() @@ -80,6 +86,10 @@ namespace ICSharpCode.WpfDesign.Designer.Services designPanel.MouseMove -= OnMouseMove; designPanel.MouseUp -= OnMouseUp; designPanel.KeyDown -= OnKeyDown; + designPanel.PreviewMouseLeftButtonDown -= OnPreviewMouseLeftButtonDown; + designPanel.PreviewMouseLeftButtonUp -= OnPreviewMouseLeftButtonUp; + designPanel.PreviewMouseRightButtonDown -= OnPreviewMouseRightButtonDown; + designPanel.PreviewMouseRightButtonUp -= OnPreviewMouseRightButtonUp; } void OnKeyDown(object sender, KeyEventArgs e) @@ -95,10 +105,27 @@ namespace ICSharpCode.WpfDesign.Designer.Services Stop(); } - protected virtual void OnMouseDown(object sender, MouseButtonEventArgs e) + protected virtual void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { + if (MouseButtonHelper.IsDoubleClick(sender, e)) + OnMouseDoubleClick(sender, e); } + protected virtual void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) + { } + + protected virtual void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) + { } + + protected virtual void OnPreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) + { } + + protected virtual void OnMouseDoubleClick(object sender, MouseButtonEventArgs e) + { } + + protected virtual void OnMouseDown(object sender, MouseButtonEventArgs e) + { } + protected virtual void OnMouseMove(object sender, MouseEventArgs e) { } @@ -119,5 +146,47 @@ namespace ICSharpCode.WpfDesign.Designer.Services protected virtual void OnStarted(MouseButtonEventArgs e) {} protected virtual void OnStopped() {} + + static class MouseButtonHelper + { + private const long k_DoubleClickSpeed = 500; + private const double k_MaxMoveDistance = 10; + + private static long _LastClickTicks = 0; + private static Point _LastPosition; + private static WeakReference _LastSender; + + internal static bool IsDoubleClick(object sender, MouseButtonEventArgs e) + { + Point position = e.GetPosition(null); + long clickTicks = DateTime.Now.Ticks; + long elapsedTicks = clickTicks - _LastClickTicks; + long elapsedTime = elapsedTicks / TimeSpan.TicksPerMillisecond; + bool quickClick = (elapsedTime <= k_DoubleClickSpeed); + bool senderMatch = (_LastSender != null && sender.Equals(_LastSender.Target)); + + if (senderMatch && quickClick && Distance(position, _LastPosition) <= k_MaxMoveDistance) + { + // Double click! + _LastClickTicks = 0; + _LastSender = null; + return true; + } + + // Not a double click + _LastClickTicks = clickTicks; + _LastPosition = position; + if (!quickClick) + _LastSender = new WeakReference(sender); + return false; + } + + private static double Distance(Point pointA, Point pointB) + { + double x = pointA.X - pointB.X; + double y = pointA.Y - pointB.Y; + return Math.Sqrt(x * x + y * y); + } + } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj index 3bfc285617..50cd7d67d2 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj @@ -87,6 +87,7 @@ + DefaultCommandsContextMenu.xaml Code diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DrawItemBehavior.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DrawItemExtension.cs similarity index 97% rename from src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DrawItemBehavior.cs rename to src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DrawItemExtension.cs index ab611aad7d..a97dffafaa 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DrawItemBehavior.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DrawItemExtension.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.WpfDesign /// Behavior interface implemented by container elements to support resizing /// drawing new Elements /// - public interface IDrawItemBehavior + public interface IDrawItemExtension { bool CanItemBeDrawn(Type createItemType); void StartDrawItem(DesignItem clickedOn, Type createItemType, IDesignPanel panel, MouseEventArgs e); diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj index 812b037ecd..56127c6a68 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj @@ -73,7 +73,7 @@ - +