File: System.Activities.Presentation\System\Activities\Presentation\Base\Core\Internal\PropertyEditing\PropertyPanel.cs
Project: ndp\cdf\src\NetFx40\Tools\System.Activities.Presentation.csproj (System.Activities.Presentation)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Presentation.Internal.PropertyEditing 
{
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
 
    // <summary>
    // PropertyPanel is a simplified version of a horizontal StackPanel that we use for PropertyContainer
    // visuals in lieu of Grid, which was too heavy-weight and bogging down perf.  It exposes a property,
    // LastChildWidth, that specifies the forced width of the last child in the panel.  All other
    // children are stacked on the left and eat up the remainder of the space left on the panel.
    //
    // The panel also deals with drawing compartments for itself and the last child and it deals with
    // visually nesting sub-properties based on their depth (Level).
    // </summary>
    internal class PropertyPanel : Panel 
    {
 
        // LastChildWidth DP
 
        public static readonly DependencyProperty OutlineBrushProperty =
            DependencyProperty.Register("OutlineBrush",
            typeof(Brush),
            typeof(PropertyPanel),
            new FrameworkPropertyMetadata((Brush)null,
            FrameworkPropertyMetadataOptions.AffectsRender |
            FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
 
        public static readonly DependencyProperty SelectionBrushProperty =
            DependencyProperty.Register("SelectionBrush",
            typeof(Brush),
            typeof(PropertyPanel),
            new FrameworkPropertyMetadata((Brush)null,
            FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
 
        public static readonly DependencyProperty OutlineThicknessProperty =
            DependencyProperty.Register("OutlineThickness",
            typeof(double),
            typeof(PropertyPanel),
            new FrameworkPropertyMetadata((double)1,
            FrameworkPropertyMetadataOptions.AffectsRender));
 
        public static readonly DependencyProperty IgnoreFirstChildBackgroundProperty =
            DependencyProperty.Register("IgnoreFirstChildBackground",
            typeof(bool),
            typeof(PropertyPanel),
            new FrameworkPropertyMetadata(
            false,
            FrameworkPropertyMetadataOptions.AffectsRender));
 
        public static DependencyProperty LastChildWidthProperty = DependencyProperty.Register(
            "LastChildWidth",
            typeof(double),
            typeof(PropertyPanel),
            new FrameworkPropertyMetadata(
            (double)0,
            FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender));
 
        public static DependencyProperty LevelProperty = DependencyProperty.Register(
            "Level",
            typeof(int),
            typeof(PropertyPanel),
            new FrameworkPropertyMetadata(
            (int)0,
            FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
 
        public static DependencyProperty LevelIndentProperty = DependencyProperty.Register(
            "LevelIndent",
            typeof(double),
            typeof(PropertyPanel),
            new FrameworkPropertyMetadata(
            (double)13,
            FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
 
        // <summary>
        // Gets or sets the pixel width of the last child added into this panel.
        // </summary>
        public double LastChildWidth 
        {
            get { return (double)this.GetValue(LastChildWidthProperty); }
            set { this.SetValue(LastChildWidthProperty, value); }
        }
 
 
 
        // Level DP
 
        // <summary>
        // Gets or sets the indentation level for the first child in this panel.  Levels are
        // measured in ints, with 0 = no indentation, 1 = 1st sub-property, ...
        // The actual amount of space taken up by each level is controled by LevelIndent property
        // </summary>
        public int Level 
        {
            get { return (int)this.GetValue(LevelProperty); }
            set { this.SetValue(LevelProperty, value); }
        }
 
 
 
        // LevelIndent DP
 
        // <summary>
        // Gets or sets the pixel width that the first child is indented for each level that
        // it belongs to
        // </summary>
        public double LevelIndent 
        {
            get { return (double)this.GetValue(LevelIndentProperty); }
            set { this.SetValue(LevelIndentProperty, value); }
        }
 
 
 
        // OutlineBrush DP
 
        // <summary>
        // Gets or sets the line brush to use for the panel compartments
        // </summary>
        public Brush OutlineBrush 
        {
            get { return (Brush)GetValue(OutlineBrushProperty); }
            set { SetValue(OutlineBrushProperty, value); }
        }
 
 
 
        // SelectionBrush DP
 
        // <summary>
        // Gets or sets the brush to be used as the background for everything but the last
        // element in the panel
        // </summary>
        public Brush SelectionBrush 
        {
            get { return (Brush)GetValue(SelectionBrushProperty); }
            set { SetValue(SelectionBrushProperty, value); }
        }
 
 
 
        // OutlineThickness DP
 
        // <summary>
        // Gets or sets the line thickness for the panel compartments (not as Thickness, but
        // instead as a simple double)
        // </summary>
        public double OutlineThickness 
        {
            get { return (double)GetValue(OutlineThicknessProperty); }
            set { SetValue(OutlineThicknessProperty, value); }
        }
 
 
 
        // IgnoreFirstChildBackground DP
 
        // <summary>
        // Gets or sets a flag indicating whether the SelectionBrush background should
        // or should not be applied to the first child of the panel
        // </summary>
        public bool IgnoreFirstChildBackground 
        {
            get { return (bool)GetValue(IgnoreFirstChildBackgroundProperty); }
            set { SetValue(IgnoreFirstChildBackgroundProperty, value); }
        }
 
 
 
        // Stacks the children to the left, leaving LastChildWidth amount of space for the last child
        protected override Size MeasureOverride(Size availableSize) 
        {
 
            double lastChildWidth = Math.Max(0, this.LastChildWidth);
            double indent = this.LevelIndent * this.Level;
            double availableWidth = Math.Max(0, availableSize.Width - lastChildWidth - indent);
            int childrenCount = InternalChildren.Count;
            int lastIndex = childrenCount - 1;
            Size actualSize = new Size();
 
            for (int i = 0; i < childrenCount; i++) 
            {
                UIElement child = InternalChildren[i];
 
                if (i == lastIndex) 
                {
                    InternalChildren[i].Measure(new Size(lastChildWidth, availableSize.Height));
                }
                else 
                {
                    InternalChildren[i].Measure(new Size(availableWidth, availableSize.Height));
                }
 
                availableWidth -= child.DesiredSize.Width;
                //Compute the actual size for the propertypanel
                actualSize.Height = Math.Max(actualSize.Height, child.DesiredSize.Height);
                actualSize.Width += child.DesiredSize.Width;
            }
 
            return actualSize;
        }
 
        // Stacks the children to the left, leaving LastChildWidth amount of space for the last child
        protected override Size ArrangeOverride(Size finalSize) 
        {
 
            double lastChildWidth = Math.Max(0, this.LastChildWidth);
            double indent = this.LevelIndent * this.Level;
            double availableWidth = Math.Max(0, finalSize.Width - lastChildWidth - indent);
            double left = indent;
            int childrenCount = InternalChildren.Count;
            int lastIndex = childrenCount - 1;
 
            for (int i = 0; i < childrenCount; i++) 
            {
 
                UIElement child = InternalChildren[i];
                double desiredWidth = child.DesiredSize.Width;
                if (i == lastIndex) 
                {
                    child.Arrange(new Rect(Math.Max(0, finalSize.Width - lastChildWidth), 0, lastChildWidth, finalSize.Height));
                }
                else 
                {
                    child.Arrange(new Rect(left, 0, Math.Min(desiredWidth, availableWidth), finalSize.Height));
                }
 
                left += desiredWidth;
                availableWidth -= desiredWidth;
                availableWidth = Math.Max(0, availableWidth);
            }
 
            return finalSize;
        }
 
        // Custom renders compartments and dividers
        protected override void OnRender(DrawingContext dc) 
        {
 
            Size renderSize = this.RenderSize;
            Brush outlineBrush = this.OutlineBrush;
            double outlineThickness = this.OutlineThickness;
            double halfThickness = outlineThickness / 2.0;
            double dividerRight = Math.Max(0, this.LastChildWidth);
            double dividerLeft = renderSize.Width - dividerRight - outlineThickness;
 
            Brush selectionBrush = this.SelectionBrush;
 
            if (selectionBrush != null) 
            {
                bool ignoreFirstChildBackground = this.IgnoreFirstChildBackground;
                double firstChildWidth = 0;
 
                if (ignoreFirstChildBackground && this.Children.Count > 0) 
                {
                    firstChildWidth = this.Children[0].RenderSize.Width;
                }
 
                dc.DrawRectangle(selectionBrush, null, new Rect(
                    firstChildWidth,
                    0,
                    Math.Max(dividerLeft - firstChildWidth, 0),
                    renderSize.Height));
            }
 
            base.OnRender(dc);
 
            // Use Guidelines to avoid anti-aliasing (fuzzy border lines)
            dc.PushGuidelineSet(new GuidelineSet(
                // X coordinates for guidelines (vertical lines)
                new double[] { 0, dividerLeft, dividerLeft + outlineThickness, renderSize.Width - outlineThickness, renderSize.Width },
                // Y coordinates for guidelines (horizontal lines)
                new double[] { 0, renderSize.Height - outlineThickness, renderSize.Height }));
 
            Pen outlinePen = new Pen(outlineBrush, outlineThickness);
 
            // Bottom edge
            dc.DrawLine(
                outlinePen,
                new Point(0, renderSize.Height - halfThickness),
                new Point(renderSize.Width, renderSize.Height - halfThickness));
 
            // Top edge
            dc.DrawLine(
                outlinePen,
                new Point(0, 0 - halfThickness),
                new Point(renderSize.Width, 0 - halfThickness));
 
            // Right edge
            dc.DrawLine(
                outlinePen,
                new Point(renderSize.Width - halfThickness, 0),
                new Point(renderSize.Width - halfThickness, renderSize.Height));
 
            // Divider
            dc.DrawLine(
                outlinePen,
                new Point(dividerLeft + halfThickness, 0),
                new Point(dividerLeft + halfThickness, renderSize.Height));
 
            dc.Pop();
        }
    }
}