diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/AdornerLayer.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/AdornerLayer.cs index 7f1f612021..80c408b216 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/AdornerLayer.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/AdornerLayer.cs @@ -89,12 +89,31 @@ namespace ICSharpCode.WpfDesign.Designer.Controls AdornerPanelCollection _adorners; readonly UIElement _designPanel; + #if DEBUG + int _totalAdornerCount; + #endif + + internal AdornerLayer(UIElement designPanel) { this._designPanel = designPanel; + this.LayoutUpdated += OnLayoutUpdated; + _adorners = new AdornerPanelCollection(this); - ClearAdorners(); + } + + void OnLayoutUpdated(object sender, EventArgs e) + { + UpdateAllAdorners(false); +// Debug.WriteLine("Adorner LayoutUpdated. AdornedElements=" + _dict.Count + +// ", visible adorners=" + VisualChildrenCount + ", total adorners=" + (_totalAdornerCount)); + } + + protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) + { + base.OnRenderSizeChanged(sizeInfo); + UpdateAllAdorners(true); } internal AdornerPanelCollection Adorners { @@ -106,22 +125,32 @@ namespace ICSharpCode.WpfDesign.Designer.Controls sealed class AdornerInfo { internal readonly List adorners = new List(); + internal bool isVisible; } // adorned element => AdornerInfo - Dictionary _dict; + Dictionary _dict = new Dictionary(); void ClearAdorners() { + if (_dict.Count == 0) + return; // already empty + this.Children.Clear(); _dict = new Dictionary(); + + #if DEBUG + _totalAdornerCount = 0; + Debug.WriteLine("AdornerLayer cleared."); + #endif } - AdornerInfo GetAdornerInfo(UIElement adornedElement) + AdornerInfo GetOrCreateAdornerInfo(UIElement adornedElement) { AdornerInfo info; if (!_dict.TryGetValue(adornedElement, out info)) { info = _dict[adornedElement] = new AdornerInfo(); + info.isVisible = adornedElement.IsDescendantOf(_designPanel); } return info; } @@ -138,8 +167,19 @@ namespace ICSharpCode.WpfDesign.Designer.Controls if (adornerPanel.AdornedElement == null) throw new DesignerException("adornerPanel.AdornedElement must be set"); - GetAdornerInfo(adornerPanel.AdornedElement).adorners.Add(adornerPanel); + AdornerInfo info = GetOrCreateAdornerInfo(adornerPanel.AdornedElement); + info.adorners.Add(adornerPanel); + + if (info.isVisible) { + AddAdornerToChildren(adornerPanel); + } + Debug.WriteLine("Adorner added. AdornedElements=" + _dict.Count + + ", visible adorners=" + VisualChildrenCount + ", total adorners=" + (++_totalAdornerCount)); + } + + void AddAdornerToChildren(AdornerPanel adornerPanel) + { UIElementCollection children = this.Children; int i = 0; for (i = 0; i < children.Count; i++) { @@ -149,8 +189,6 @@ namespace ICSharpCode.WpfDesign.Designer.Controls } } children.Insert(i, adornerPanel); - - this.InvalidateMeasure(); } protected override Size MeasureOverride(Size availableSize) @@ -166,7 +204,9 @@ namespace ICSharpCode.WpfDesign.Designer.Controls { foreach (AdornerPanel adorner in this.Children) { adorner.Arrange(new Rect(new Point(0, 0), adorner.DesiredSize)); - adorner.RenderTransform = (Transform)adorner.AdornedElement.TransformToAncestor(_designPanel); + if (adorner.AdornedElement.IsDescendantOf(_designPanel)) { + adorner.RenderTransform = (Transform)adorner.AdornedElement.TransformToAncestor(_designPanel); + } } return finalSize; } @@ -181,11 +221,58 @@ namespace ICSharpCode.WpfDesign.Designer.Controls return false; if (info.adorners.Remove(adornerPanel)) { - this.Children.Remove(adornerPanel); + if (info.isVisible) { + this.Children.Remove(adornerPanel); + } + + if (info.adorners.Count == 0) { + _dict.Remove(adornerPanel.AdornedElement); + } + + Debug.WriteLine("Adorner removed. AdornedElements=" + _dict.Count + + ", visible adorners=" + VisualChildrenCount + ", total adorners=" + (--_totalAdornerCount)); + return true; } else { return false; } } + + public void UpdateAdornersForElement(UIElement element, bool forceInvalidate) + { + AdornerInfo info = GetExistingAdornerInfo(element); + if (info != null) { + UpdateAdornersForElement(element, info, forceInvalidate); + } + } + + void UpdateAdornersForElement(UIElement element, AdornerInfo info, bool forceInvalidate) + { + if (element.IsDescendantOf(_designPanel)) { + if (!info.isVisible) { + info.isVisible = true; + // make adorners visible: + info.adorners.ForEach(AddAdornerToChildren); + } + if (forceInvalidate) { + foreach (AdornerPanel p in info.adorners) { + p.InvalidateMeasure(); + } + } + } else { + if (info.isVisible) { + info.isVisible = false; + // make adorners invisible: + info.adorners.ForEach(this.Children.Remove); + } + } + } + + void UpdateAllAdorners(bool forceInvalidate) + { + foreach (KeyValuePair pair in _dict) { + UpdateAdornersForElement(pair.Key, pair.Value, forceInvalidate); + } + } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignPanel.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignPanel.cs index b8f95e81d2..df6e1cfd4d 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignPanel.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignPanel.cs @@ -37,8 +37,9 @@ namespace ICSharpCode.WpfDesign.Designer } DesignContext _context; - EatAllHitTestRequests _eatAllHitTestRequests; - AdornerLayer _adornerLayer; + readonly EatAllHitTestRequests _eatAllHitTestRequests; + readonly AdornerLayer _adornerLayer; + readonly Canvas _markerCanvas; public DesignPanel() { @@ -46,6 +47,8 @@ namespace ICSharpCode.WpfDesign.Designer _eatAllHitTestRequests = new EatAllHitTestRequests(); _eatAllHitTestRequests.IsHitTestVisible = false; _adornerLayer = new AdornerLayer(this); + _markerCanvas = new Canvas(); + _markerCanvas.IsHitTestVisible = false; } #region Visual Child Management @@ -60,10 +63,12 @@ namespace ICSharpCode.WpfDesign.Designer // remove _adornerLayer and _eatAllHitTestRequests RemoveVisualChild(_adornerLayer); RemoveVisualChild(_eatAllHitTestRequests); + RemoveVisualChild(_markerCanvas); } else if (base.Child == null) { // Child is being set from null to some value AddVisualChild(_adornerLayer); AddVisualChild(_eatAllHitTestRequests); + AddVisualChild(_markerCanvas); } base.Child = value; } @@ -78,6 +83,8 @@ namespace ICSharpCode.WpfDesign.Designer return _eatAllHitTestRequests; else if (index == 2) return _adornerLayer; + else if (index == 3) + return _markerCanvas; } return base.GetVisualChild(index); } @@ -85,7 +92,7 @@ namespace ICSharpCode.WpfDesign.Designer protected override int VisualChildrenCount { get { if (base.Child != null) - return 3; + return 4; else return base.VisualChildrenCount; } @@ -97,6 +104,7 @@ namespace ICSharpCode.WpfDesign.Designer if (this.Child != null) { _adornerLayer.Measure(constraint); _eatAllHitTestRequests.Measure(constraint); + _markerCanvas.Measure(constraint); } return result; } @@ -105,8 +113,10 @@ namespace ICSharpCode.WpfDesign.Designer { Size result = base.ArrangeOverride(arrangeSize); if (this.Child != null) { - _adornerLayer.Arrange(new Rect(new Point(0, 0), arrangeSize)); - _eatAllHitTestRequests.Arrange(new Rect(new Point(0, 0), arrangeSize)); + Rect r = new Rect(new Point(0, 0), arrangeSize); + _adornerLayer.Arrange(r); + _eatAllHitTestRequests.Arrange(r); + _markerCanvas.Arrange(r); } return result; } @@ -197,6 +207,10 @@ namespace ICSharpCode.WpfDesign.Designer return _adornerLayer.Adorners; } } + + public Canvas MarkerCanvas { + get { return _markerCanvas; } + } } internal delegate void Action(); diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.cs index a76ddc2fdb..4ef17d1527 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.cs @@ -86,6 +86,7 @@ namespace ICSharpCode.WpfDesign.Designer _designContext = null; _designPanel.Context = null; _designPanel.Child = null; + _designPanel.Adorners.Clear(); } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/SelectedElementRectangleExtension.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/SelectedElementRectangleExtension.cs index b86a0ec701..05bb2b37f4 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/SelectedElementRectangleExtension.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/SelectedElementRectangleExtension.cs @@ -33,9 +33,14 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions r.StrokeDashArray = new DoubleCollection(new double[] { 0, 2 }); r.IsHitTestVisible = false; - Placement placement = new Placement(); + RelativePlacement placement = new RelativePlacement(); placement.WidthRelativeToContentWidth = 1; placement.HeightRelativeToContentHeight = 1; + placement.WidthOffset = 2; + placement.HeightOffset = 2; + + placement.XOffset = -1; + placement.YOffset = -1; this.AddAdorner(r, placement); } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/AdornerPanel.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/AdornerPanel.cs index cbd4e4beed..56921893dc 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/AdornerPanel.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/AdornerPanel.cs @@ -24,45 +24,56 @@ namespace ICSharpCode.WpfDesign.Adorners /// public static readonly DependencyProperty PlacementProperty = DependencyProperty.RegisterAttached( "Placement", typeof(Placement), typeof(AdornerPanel), - new FrameworkPropertyMetadata(new Placement(), FrameworkPropertyMetadataOptions.AffectsParentMeasure) + new FrameworkPropertyMetadata(Placement.FillContent, FrameworkPropertyMetadataOptions.AffectsParentMeasure) ); /// - /// Gets the placement of the specified adorner visual. + /// Gets the placement of the specified adorner. /// - public static Placement GetPlacement(Visual visual) + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] + public static Placement GetPlacement(UIElement adorner) { - if (visual == null) - throw new ArgumentNullException("visual"); - return (Placement)visual.GetValue(PlacementProperty); + if (adorner == null) + throw new ArgumentNullException("adorner"); + return (Placement)adorner.GetValue(PlacementProperty); } /// - /// Sets the placement of the specified adorner visual. + /// Sets the placement of the specified adorner. /// - public static void SetPlacement(Visual visual, Placement placement) + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] + public static void SetPlacement(UIElement adorner, Placement placement) { - if (visual == null) - throw new ArgumentNullException("visual"); + if (adorner == null) + throw new ArgumentNullException("adorner"); if (placement == null) throw new ArgumentNullException("placement"); - visual.SetValue(PlacementProperty, placement); + adorner.SetValue(PlacementProperty, placement); } #endregion UIElement _adornedElement; AdornerOrder _Order = AdornerOrder.Content; + /// + /// Gets/Sets the element adorned by this AdornerPanel. + /// Do not change this property after the panel was added to an AdornerLayer! + /// public UIElement AdornedElement { get { return _adornedElement; } set { _adornedElement = value; } } + /// + /// Gets/Sets the order used to display the AdornerPanel relative to other AdornerPanels. + /// Do not change this property after the panel was added to an AdornerLayer! + /// public AdornerOrder Order { get { return _Order; } set { _Order = value; } } + /// protected override Size MeasureOverride(Size availableSize) { if (this.AdornedElement != null) { @@ -78,37 +89,57 @@ namespace ICSharpCode.WpfDesign.Adorners } } + /// protected override Size ArrangeOverride(Size finalSize) { foreach (UIElement element in base.InternalChildren) { - element.Arrange(new Rect(finalSize)); + GetPlacement(element).Arrange(this, element, finalSize); } return finalSize; } - private IEnumerable VisualChildren { + private DependencyObject[] VisualChildren { get { int count = VisualTreeHelper.GetChildrenCount(this); - for (int i = 0; i < count; i++) { - yield return VisualTreeHelper.GetChild(this, i); + DependencyObject[] children = new DependencyObject[count]; + for (int i = 0; i < children.Length; i++) { + children[i] = VisualTreeHelper.GetChild(this, i); } + return children; } } } + /// + /// Describes where an Adorner is positioned on the Z-Layer. + /// public struct AdornerOrder : IComparable { + /// + /// The adorner is in the background layer. + /// public static readonly AdornerOrder Background = new AdornerOrder(100); + + /// + /// The adorner is in the content layer. + /// public static readonly AdornerOrder Content = new AdornerOrder(200); + + /// + /// The adorner is in the foreground layer. + /// public static readonly AdornerOrder Foreground = new AdornerOrder(300); int i; - public AdornerOrder(int i) + internal AdornerOrder(int i) { this.i = i; } + /// + /// Compares the to another AdornerOrder. + /// public int CompareTo(AdornerOrder other) { return i.CompareTo(other.i); diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/AdornerProvider.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/AdornerProvider.cs index 43e0aa00e6..7374cfe15f 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/AdornerProvider.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/AdornerProvider.cs @@ -75,7 +75,7 @@ namespace ICSharpCode.WpfDesign.Adorners /// /// Creates a new AdornerProvider instance. /// - public AdornerProvider() + protected AdornerProvider() { _adorners = new AdornerPanelCollection(this); } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/Placement.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/Placement.cs index c787fc5f37..742ea73480 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/Placement.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/Placement.cs @@ -18,18 +18,33 @@ namespace ICSharpCode.WpfDesign.Adorners /// /// Defines how a design-time adorner is placed. /// - public class Placement + public abstract class Placement { - PlacementSpace space = PlacementSpace.Render; + /// + /// A placement instance that places the adorner above the content, using the same bounds as the content. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly Placement FillContent = new FillContentPlacement(); /// - /// Gets/Sets the space in which the adorner is placed. + /// Arranges the adorner element on the specified adorner panel. /// - public PlacementSpace Space { - get { return space; } - set { space = value; } - } + public abstract void Arrange(AdornerPanel panel, UIElement adorner, Size adornedElementSize); + sealed class FillContentPlacement : Placement + { + public override void Arrange(AdornerPanel panel, UIElement adorner, Size adornedElementSize) + { + adorner.Arrange(new Rect(adornedElementSize)); + } + } + } + + /// + /// Placement class providing properties for different kinds of relative placements. + /// + public sealed class RelativePlacement : Placement + { double widthRelativeToDesiredWidth, heightRelativeToDesiredHeight; /// @@ -84,23 +99,51 @@ namespace ICSharpCode.WpfDesign.Adorners set { heightOffset = value; } } - Size CalculateSize(Visual adornerVisual, UIElement adornedElement) + Size CalculateSize(UIElement adorner, Size adornedElementSize) { Size size = new Size(widthOffset, heightOffset); if (widthRelativeToDesiredWidth != 0 || heightRelativeToDesiredHeight != 0) { - UIElement adornerElement = adornerVisual as UIElement; - if (adornerElement == null) { - throw new DesignerException("Cannot calculate the size relative to the adorner's desired size if the adorner is not an UIElement."); - } - size.Width += widthRelativeToDesiredWidth * adornerElement.DesiredSize.Width; - size.Height += heightRelativeToDesiredHeight * adornerElement.DesiredSize.Height; + size.Width += widthRelativeToDesiredWidth * adorner.DesiredSize.Width; + size.Height += heightRelativeToDesiredHeight * adorner.DesiredSize.Height; } - size.Width += widthRelativeToContentWidth * adornedElement.RenderSize.Width; - size.Height += heightRelativeToContentHeight * adornedElement.RenderSize.Height; + size.Width += widthRelativeToContentWidth * adornedElementSize.Width; + size.Height += heightRelativeToContentHeight * adornedElementSize.Height; return size; } + + double xOffset, yOffset; + + /// + /// Gets/Sets an offset that is added to the adorner position. + /// + public double XOffset { + get { return xOffset; } + set { xOffset = value; } + } + + /// + /// Gets/Sets an offset that is added to the adorner position. + /// + public double YOffset { + get { return yOffset; } + set { yOffset = value; } + } + + Point CalculatePosition(Size adornedElementSize, Size adornerSize) + { + return new Point(xOffset, yOffset); + } + + /// + /// Arranges the adorner element on the specified adorner panel. + /// + public override void Arrange(AdornerPanel panel, UIElement adorner, Size adornedElementSize) + { + Size adornerSize = CalculateSize(adorner, adornedElementSize); + adorner.Arrange(new Rect(CalculatePosition(adornedElementSize, adornerSize), adornerSize)); + } } - + /// /// Describes the space in which an adorner is placed. /// @@ -119,7 +162,7 @@ namespace ICSharpCode.WpfDesign.Adorners /// Designer } - + /// /// The possible layers where adorners can be placed. /// diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Tools.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Tools.cs index e95641d582..dbd10d7700 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Tools.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Tools.cs @@ -125,6 +125,14 @@ namespace ICSharpCode.WpfDesign /// ICollection Adorners { get; } + /* + /// + /// 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. + /// + Canvas MarkerCanvas { get; } + */ + // The following members were missing in , but of course // are supported on the DesignPanel: