diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/GrayOutDesignerExceptActiveArea.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/GrayOutDesignerExceptActiveArea.cs new file mode 100644 index 0000000000..6a038047bb --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/GrayOutDesignerExceptActiveArea.cs @@ -0,0 +1,75 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Windows; +using System.Windows.Media; + +namespace ICSharpCode.WpfDesign.Designer.Controls +{ + /// + /// Gray out everything except a specific area. + /// + sealed class GrayOutDesignerExceptActiveArea : FrameworkElement + { + //Geometry infiniteRectangle = new RectangleGeometry(new Rect(0, 0, double.PositiveInfinity, double.PositiveInfinity)); + Geometry infiniteRectangle = new RectangleGeometry(new Rect(0, 0, 10000, 10000)); + Geometry activeAreaGeometry; + Geometry combinedGeometry; + Brush grayOutBrush; + + public GrayOutDesignerExceptActiveArea() + { + Color c = SystemColors.GrayTextColor; + c.R = (byte)Math.Max(0, c.R - 20); + c.G = (byte)Math.Max(0, c.G - 20); + c.B = (byte)Math.Max(0, c.B - 20); + c.A = 30; + this.GrayOutBrush = new SolidColorBrush(c); + } + + public Brush GrayOutBrush { + get { return grayOutBrush; } + set { grayOutBrush = value; } + } + + public Geometry ActiveAreaGeometry { + get { return activeAreaGeometry; } + set { + activeAreaGeometry = value; + combinedGeometry = new CombinedGeometry(GeometryCombineMode.Exclude, infiniteRectangle, activeAreaGeometry); + } + } + + public void SetActiveArea(UIElement activeContainer) + { + this.ActiveAreaGeometry = new RectangleGeometry(new Rect(activeContainer.RenderSize), 0, 0, (Transform)activeContainer.TransformToVisual(this)); + } + + protected override void OnRender(DrawingContext drawingContext) + { + drawingContext.DrawGeometry(grayOutBrush, null, combinedGeometry); + } + + internal static void Start(ref GrayOutDesignerExceptActiveArea grayOut, IDesignPanel designPanel, UIElement activeContainer) + { + if (designPanel != null) { + grayOut = new GrayOutDesignerExceptActiveArea(); + designPanel.MarkerCanvas.Children.Add(grayOut); + grayOut.SetActiveArea(activeContainer); + } + } + + internal static void Stop(ref GrayOutDesignerExceptActiveArea grayOut, IDesignPanel designPanel) + { + if (grayOut != null) { + designPanel.MarkerCanvas.Children.Remove(grayOut); + grayOut = null; + } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasChildResizeSupport.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasChildResizeSupport.cs index 2352b2ee97..0eed23e71a 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasChildResizeSupport.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasChildResizeSupport.cs @@ -6,17 +6,18 @@ // using System; -using ICSharpCode.WpfDesign.Extensions; using System.Windows; using System.Windows.Controls; + using ICSharpCode.WpfDesign.Adorners; +using ICSharpCode.WpfDesign.Extensions; namespace ICSharpCode.WpfDesign.Designer.Extensions { /// /// Provides behavior for . /// - [ExtensionFor(typeof(Canvas))] + [ExtensionFor(typeof(Canvas), OverrideExtension = typeof(DefaultChildResizeSupport))] public sealed class CanvasChildResizeSupport : BehaviorExtension, IChildResizeSupport { /// diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/DefaultChildResizeSupport.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/DefaultChildResizeSupport.cs index 77e3da4188..70ca7ba111 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/DefaultChildResizeSupport.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/DefaultChildResizeSupport.cs @@ -6,9 +6,9 @@ // using System; -using ICSharpCode.WpfDesign.Extensions; -using ICSharpCode.WpfDesign.Adorners; using System.Windows; +using ICSharpCode.WpfDesign.Adorners; +using ICSharpCode.WpfDesign.Extensions; namespace ICSharpCode.WpfDesign.Designer.Extensions { @@ -16,21 +16,17 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions /// Implements IChildResizeSupport supporting size changes using the /// Width, Height, Margin properties. /// - //[ExtensionFor(typeof(FrameworkElement))] - sealed class DefaultChildResizeSupport : IChildResizeSupport + [ExtensionFor(typeof(FrameworkElement))] + public sealed class DefaultChildResizeSupport : BehaviorExtension, IChildResizeSupport { - // the default behavior is not an extension because it does not depend - // on a parent container instance - public static readonly DefaultChildResizeSupport Instance = new DefaultChildResizeSupport(); + internal static readonly DefaultChildResizeSupport Instance = new DefaultChildResizeSupport(); - /* /// protected override void OnInitialized() { base.OnInitialized(); this.ExtendedItem.AddBehavior(typeof(IChildResizeSupport), this); } - */ /// public bool CanResizeChild(DesignItem childItem) diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/PanelSelectionHandler.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/PanelSelectionHandler.cs new file mode 100644 index 0000000000..c9b4b53d00 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/PanelSelectionHandler.cs @@ -0,0 +1,162 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +using ICSharpCode.WpfDesign.Adorners; +using ICSharpCode.WpfDesign.Designer.Controls; +using ICSharpCode.WpfDesign.Designer.Services; +using ICSharpCode.WpfDesign.Extensions; + +namespace ICSharpCode.WpfDesign.Designer.Extensions +{ + /// + /// Handles selection multiple controls inside a Panel. + /// + [ExtensionFor(typeof(Panel))] + public class PanelSelectionHandler : BehaviorExtension, IHandlePointerToolMouseDown + { + /// + protected override void OnInitialized() + { + base.OnInitialized(); + this.ExtendedItem.AddBehavior(typeof(IHandlePointerToolMouseDown), this); + } + + /// + public void HandleSelectionMouseDown(IDesignPanel designPanel, MouseButtonEventArgs e, DesignPanelHitTestResult result) + { + new RangeSelectionGesture(result.ModelHit).Start(designPanel, e); + } + } + + sealed class RangeSelectionGesture : MouseGestureBase + { + DesignItem container; + AdornerPanel adornerPanel; + DragFrame selectionFrame; + Point startPoint; + GrayOutDesignerExceptActiveArea grayOut; + + public RangeSelectionGesture(DesignItem container) + { + this.container = container; + } + + protected override void OnStarted(MouseButtonEventArgs e) + { + startPoint = e.GetPosition(container.View); + + adornerPanel = new AdornerPanel(); + adornerPanel.SetAdornedElement(container.View, container); + + selectionFrame = new DragFrame(); + SetPlacement(e.GetPosition(container.View)); + adornerPanel.Children.Add(selectionFrame); + + designPanel.Adorners.Add(adornerPanel); + + GrayOutDesignerExceptActiveArea.Start(ref grayOut, designPanel, container.View); + } + + protected override void OnMouseMove(object sender, MouseEventArgs e) + { + SetPlacement(e.GetPosition(container.View)); + } + + protected override void OnMouseUp(object sender, MouseButtonEventArgs e) + { + Point endPoint = e.GetPosition(container.View); + Rect frameRect = new Rect( + Math.Min(startPoint.X, endPoint.X), + Math.Min(startPoint.Y, endPoint.Y), + Math.Abs(startPoint.X - endPoint.X), + Math.Abs(startPoint.Y - endPoint.Y) + ); + + ICollection items = GetChildDesignItemsInContainer(container, new RectangleGeometry(frameRect)); + if (items.Count == 0) { + items.Add(container); + } + services.Selection.SetSelectedComponents(items, SelectionTypes.Auto); + Stop(); + } + + static ICollection GetChildDesignItemsInContainer( + DesignItem container, Geometry geometry) + { + HashSet resultItems = new HashSet(); + ViewService viewService = container.Services.View; + + HitTestFilterCallback filterCallback = delegate(DependencyObject potentialHitTestTarget) { + FrameworkElement element = potentialHitTestTarget as FrameworkElement; + if (element != null) { + // ensure we are able to select elements with width/height=0 + if (element.ActualWidth == 0 || element.ActualHeight == 0) { + DependencyObject tmp = element; + DesignItem model = null; + while (tmp != null) { + model = viewService.GetModel(tmp); + if (model != null) break; + tmp = VisualTreeHelper.GetParent(tmp); + } + if (model != container) { + resultItems.Add(model); + return HitTestFilterBehavior.ContinueSkipChildren; + } + } + } + return HitTestFilterBehavior.Continue; + }; + + HitTestResultCallback resultCallback = delegate(HitTestResult result) { + if (((GeometryHitTestResult) result).IntersectionDetail == IntersectionDetail.FullyInside) { + // find the model for the visual contained in the selection area + DependencyObject tmp = result.VisualHit; + DesignItem model = null; + while (tmp != null) { + model = viewService.GetModel(tmp); + if (model != null) break; + tmp = VisualTreeHelper.GetParent(tmp); + } + if (model != container) { + resultItems.Add(model); + } + } + return HitTestResultBehavior.Continue; + }; + + VisualTreeHelper.HitTest(container.View, filterCallback, resultCallback, new GeometryHitTestParameters(geometry)); + return resultItems; + } + + void SetPlacement(Point endPoint) + { + RelativePlacement p = new RelativePlacement(); + p.XOffset = Math.Min(startPoint.X, endPoint.X); + p.YOffset = Math.Min(startPoint.Y, endPoint.Y); + p.WidthOffset = Math.Max(startPoint.X, endPoint.X) - p.XOffset; + p.HeightOffset = Math.Max(startPoint.Y, endPoint.Y) - p.YOffset; + AdornerPanel.SetPlacement(selectionFrame, p); + } + + protected override void OnStopped() + { + if (adornerPanel != null) { + designPanel.Adorners.Remove(adornerPanel); + adornerPanel = null; + } + GrayOutDesignerExceptActiveArea.Stop(ref grayOut, designPanel); + selectionFrame = null; + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/ResizeThumbExtension.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/ResizeThumbExtension.cs index b257301f63..d71673112f 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/ResizeThumbExtension.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/ResizeThumbExtension.cs @@ -6,14 +6,13 @@ // using System; -using System.Collections.Generic; using System.Windows; -using System.Windows.Input; -using System.Windows.Controls; using System.Windows.Controls.Primitives; +using System.Windows.Input; + using ICSharpCode.WpfDesign.Adorners; -using ICSharpCode.WpfDesign.Extensions; using ICSharpCode.WpfDesign.Designer.Controls; +using ICSharpCode.WpfDesign.Extensions; namespace ICSharpCode.WpfDesign.Designer.Extensions { @@ -25,6 +24,8 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions { AdornerPanel adornerPanel; DragFrame dragFrame; + IChildResizeSupport resizeBehavior; + GrayOutDesignerExceptActiveArea grayOut; /// public ResizeThumbExtension() @@ -49,13 +50,15 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions resizeThumb.DragCompleted += OnDragCompleted(horizontalAlignment, verticalAlignment); } - IChildResizeSupport resizeBehavior; - void OnDragStarted(object sender, DragStartedEventArgs e) { if (dragFrame == null) dragFrame = new DragFrame(); + if (this.ExtendedItem.Parent != null) { + GrayOutDesignerExceptActiveArea.Start(ref grayOut, this.Services.GetService(), this.ExtendedItem.Parent.View); + } + AdornerPanel.SetPlacement(dragFrame, Placement.FillContent); adornerPanel.Children.Add(dragFrame); adornerPanel.Cursor = Cursors.SizeNWSE; @@ -83,8 +86,9 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions return delegate (object sender, DragCompletedEventArgs e) { adornerPanel.Children.Remove(dragFrame); adornerPanel.ClearValue(AdornerPanel.CursorProperty); + GrayOutDesignerExceptActiveArea.Stop(ref grayOut, this.Services.GetService()); - if (resizeBehavior != null) { + if (e.Canceled == false && resizeBehavior != null) { using (ChangeGroup group = this.ExtendedItem.OpenGroup("Resize")) { resizeBehavior.Resize(this.ExtendedItem, FixChange(e.HorizontalChange, horizontalAlignment), @@ -127,7 +131,7 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions if (parentItem == null) // resizing the root element resizeBehavior = RootElementResizeSupport.Instance; else - resizeBehavior = parentItem.GetBehavior() ?? DefaultChildResizeSupport.Instance; + resizeBehavior = parentItem.GetBehavior(); UpdateAdornerVisibility(); OnPrimarySelectionChanged(null, null); diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/ToolService.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/ToolService.cs index 2eea4409a3..7cef641b78 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/ToolService.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/ToolService.cs @@ -64,11 +64,23 @@ namespace ICSharpCode.WpfDesign.Designer.Services { if (e.ChangedButton == MouseButton.Left && MouseGestureBase.IsOnlyButtonPressed(e, MouseButton.Left)) { e.Handled = true; - new SelectionGesture().Start((IDesignPanel)sender, e); + IDesignPanel designPanel = (IDesignPanel)sender; + DesignPanelHitTestResult result = designPanel.HitTest(e, false, true); + if (result.ModelHit != null) { + IHandlePointerToolMouseDown b = result.ModelHit.GetBehavior(); + if (b != null) { + b.HandleSelectionMouseDown(designPanel, e, result); + } else { + designPanel.Context.Services.Selection.SetSelectedComponents(new DesignItem[] { result.ModelHit }, SelectionTypes.Auto); + } + } } } } + /// + /// Base class for classes handling mouse gestures on the design surface. + /// abstract class MouseGestureBase { /// @@ -89,12 +101,19 @@ namespace ICSharpCode.WpfDesign.Designer.Services public void Start(IDesignPanel designPanel, MouseButtonEventArgs e) { + if (designPanel == null) + throw new ArgumentNullException("designPanel"); + if (e == null) + throw new ArgumentNullException("e"); + if (isStarted) + throw new InvalidOperationException("Gesture already was started"); + + isStarted = true; this.designPanel = designPanel; this.services = designPanel.Context.Services; - isStarted = true; designPanel.IsAdornerLayerHitTestVisible = false; - RegisterEvents(); if (designPanel.CaptureMouse()) { + RegisterEvents(); OnStarted(e); } else { Stop(); @@ -148,22 +167,4 @@ namespace ICSharpCode.WpfDesign.Designer.Services protected virtual void OnStarted(MouseButtonEventArgs e) {} protected virtual void OnStopped() {} } - - sealed class SelectionGesture : MouseGestureBase - { - protected override void OnStarted(MouseButtonEventArgs e) - { - base.OnStarted(e); - DesignPanelHitTestResult result = designPanel.HitTest(e, false, true); - if (result.ModelHit != null) { - services.Selection.SetSelectedComponents(new DesignItem[] { result.ModelHit }, SelectionTypes.Auto); - } - } - - protected override void OnStopped() - { - //designPanel.cur - base.OnStopped(); - } - } } 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 a219e5638f..a4ef1fc68f 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj @@ -60,6 +60,7 @@ + @@ -73,6 +74,7 @@ + diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs index 1ffc86195c..86c9c88791 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs @@ -9,6 +9,7 @@ using System; using System.Xml; using ICSharpCode.WpfDesign.XamlDom; using ICSharpCode.WpfDesign.Designer.Services; +using ICSharpCode.WpfDesign.Designer.Extensions; using ICSharpCode.WpfDesign.Extensions; using ICSharpCode.WpfDesign.PropertyEditor; diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs index cc8b9eb262..8acd9496ae 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs @@ -152,7 +152,7 @@ namespace ICSharpCode.WpfDesign #endregion #region Manage behavior - Dictionary _behaviorObjects = new Dictionary(); + readonly Dictionary _behaviorObjects = new Dictionary(); /// /// Adds a bevahior extension object to this design item. @@ -163,6 +163,8 @@ namespace ICSharpCode.WpfDesign throw new ArgumentNullException("bevahiorInterface"); if (behaviorImplementation == null) throw new ArgumentNullException("behaviorImplementation"); + if (!bevahiorInterface.IsInstanceOfType(behaviorImplementation)) + throw new ArgumentException("behaviorImplementation must implement bevahiorInterface", "behaviorImplementation"); _behaviorObjects.Add(bevahiorInterface, behaviorImplementation); } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItemProperty.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItemProperty.cs index 09a89bc875..d1d73371ad 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItemProperty.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItemProperty.cs @@ -6,7 +6,6 @@ // using System; -using System.Collections.ObjectModel; using System.Collections.Generic; using System.ComponentModel; using System.Windows; diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Behavior.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/MouseInteraction.cs similarity index 84% rename from src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Behavior.cs rename to src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/MouseInteraction.cs index 60179c73bd..4b02cbf41f 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Behavior.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/MouseInteraction.cs @@ -6,11 +6,27 @@ // using System; -using ICSharpCode.WpfDesign.Adorners; using System.Windows; +using System.Windows.Input; + +using ICSharpCode.WpfDesign.Adorners; namespace ICSharpCode.WpfDesign { + // Interfaces for mouse interaction on the design surface. + + /// + /// Behavior interface implemented by elements to handle the mouse down event + /// on them. + /// + public interface IHandlePointerToolMouseDown + { + /// + /// Called to handle the mouse down event. + /// + void HandleSelectionMouseDown(IDesignPanel designPanel, MouseButtonEventArgs e, DesignPanelHitTestResult result); + } + /// /// Behavior interface implemented by container elements to support resizing /// child elements. diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Tools.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Tools.cs index ee11ad506b..1fad496b3e 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Tools.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Tools.cs @@ -9,7 +9,6 @@ using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; -using System.Windows.Media; using System.Windows.Input; using ICSharpCode.WpfDesign.Adorners; @@ -83,7 +82,8 @@ namespace ICSharpCode.WpfDesign /// /// A canvas that is on top of the design surface and all adorners. - /// Used for temporary drawings that are not attached to any element, e.g. the selection frame. + /// Used for temporary drawings that are not attached to any element, e.g. graying out everything + /// except the target container in drag'n'drop operations. /// Canvas MarkerCanvas { get; } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj index ded52df23d..2e4b67ba13 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj @@ -61,7 +61,7 @@ - +