Shape.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Shapes / Shape.cs / 1 / Shape.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// 
// Description: Shape element is a base class for shapes like Path,
//              Rectangle, GlyphRun etc. 
// 
// History:
// 
//  06/02/2003 : [....] - created.
//  07/29/2004 : [....] - Added ValidateValueCallback for enumeration
//                  properties
// 
//---------------------------------------------------------------------------
 
using System.Diagnostics; 
using System.Windows.Threading;
 
using System.Windows;
using System.Windows.Media;
using System.ComponentModel;
using MS.Internal; 
using MS.Internal.PresentationFramework;
using System; 
 
namespace System.Windows.Shapes
{ 
    /// 
    /// Shape is a base class for shape elements
    /// 
    [Localizability(LocalizationCategory.None, Readability=Readability.Unreadable)] 
    public abstract class Shape : FrameworkElement
    { 
        #region Constructors 

        ///  
        /// Shape Constructor
        /// 
        protected Shape()
        { 
        }
 
        #endregion 

        #region Properties 

        /// 
        /// DependencyProperty for the Stretch property.
        ///  
        public static readonly DependencyProperty StretchProperty
            = DependencyProperty.Register( 
                "Stretch",                  // Property name 
                typeof(Stretch),            // Property type
                typeof(Shape),              // Property owner 
            new FrameworkPropertyMetadata(Stretch.None, FrameworkPropertyMetadataOptions.AffectsArrange));

        /// 
        /// The Stretch property determines how the shape may be stretched to accommodate shape size 
        /// 
        public Stretch Stretch 
        { 
            get { return (Stretch)GetValue(StretchProperty); }
            set { SetValue(StretchProperty, value); } 
        }

        /// 
        /// The RenderedGeometry property returns the final rendered geometry 
        /// 
        public virtual Geometry RenderedGeometry 
        { 
            get
            { 
                EnsureRenderedGeometry();

                Geometry geometry = _renderedGeometry.CloneCurrentValue();
                if (geometry == null ||  geometry == Geometry.Empty) 
                {
                    return Geometry.Empty; 
                } 

                // We need to return a frozen copy 
                if (Object.ReferenceEquals(geometry, _renderedGeometry))
                {
                    // geometry is a reference to _renderedGeometry, so we need to copy
                    geometry = geometry.Clone(); 
                    geometry.Freeze();
                } 
 
                return geometry;
            } 
        }

        /// 
        /// Return the transformation applied to the geometry before rendering 
        /// 
        public virtual Transform GeometryTransform 
        { 
            get
            { 
                BoxedMatrix stretchMatrix = StretchMatrixField.GetValue(this);

                if (stretchMatrix == null)
                { 
                    return Transform.Identity;
                } 
                else 
                {
                    return new MatrixTransform(stretchMatrix.Value); 
                }
            }
        }
 
        private static void OnPenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            // Called when any of the Stroke properties is invalidated. 
            // That means that the cached pen should be recalculated.
            ((Shape)d)._pen = null; 
        }

        /// 
        /// Fill property 
        /// 
        [CommonDependencyProperty] 
        public static readonly DependencyProperty FillProperty = 
                DependencyProperty.Register(
                        "Fill", 
                        typeof(Brush),
                        typeof(Shape),
                        new FrameworkPropertyMetadata(
                                (Brush) null, 
                                FrameworkPropertyMetadataOptions.AffectsRender |
                                FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender)); 
 
        /// 
        /// Fill property 
        /// 
        public Brush Fill
        {
            get { return (Brush) GetValue(FillProperty); } 
            set { SetValue(FillProperty, value); }
        } 
 
        /// 
        /// Stroke property 
        /// 
        [CommonDependencyProperty]
        public static readonly DependencyProperty StrokeProperty =
                DependencyProperty.Register( 
                        "Stroke",
                        typeof(Brush), 
                        typeof(Shape), 
                        new FrameworkPropertyMetadata(
                                (Brush) null, 
                                FrameworkPropertyMetadataOptions.AffectsMeasure |
                                FrameworkPropertyMetadataOptions.AffectsRender |
                                FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
                                new PropertyChangedCallback(OnPenChanged))); 

        ///  
        /// Stroke property 
        /// 
        public Brush Stroke 
        {
            get { return (Brush) GetValue(StrokeProperty); }
            set { SetValue(StrokeProperty, value); }
        } 

        ///  
        /// StrokeThickness property 
        /// 
        [CommonDependencyProperty] 
        public static readonly DependencyProperty StrokeThicknessProperty =
                DependencyProperty.Register(
                        "StrokeThickness",
                        typeof(double), 
                        typeof(Shape),
                        new FrameworkPropertyMetadata( 
                                1.0d, 
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
                                new PropertyChangedCallback(OnPenChanged))); 

        /// 
        /// StrokeThickness property
        ///  
        [TypeConverter(typeof(LengthConverter))]
        public double StrokeThickness 
        { 
            get { return (double) GetValue(StrokeThicknessProperty); }
            set { SetValue(StrokeThicknessProperty, value); } 
        }

        /// 
        /// StrokeStartLineCap property 
        /// 
        public static readonly DependencyProperty StrokeStartLineCapProperty  = 
                DependencyProperty.Register( 
                        "StrokeStartLineCap",
                        typeof(PenLineCap), 
                        typeof(Shape),
                        new FrameworkPropertyMetadata(
                                PenLineCap.Flat,
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, 
                                new PropertyChangedCallback(OnPenChanged)),
                        new ValidateValueCallback(System.Windows.Media.ValidateEnums.IsPenLineCapValid)); 
 
        /// 
        /// StrokeStartLineCap property 
        /// 
        public PenLineCap StrokeStartLineCap
        {
            get { return (PenLineCap) GetValue(StrokeStartLineCapProperty); } 
            set { SetValue(StrokeStartLineCapProperty, value); }
        } 
 

        ///  
        /// StrokeEndLineCap property
        /// 
        public static readonly DependencyProperty StrokeEndLineCapProperty =
                DependencyProperty.Register( 
                        "StrokeEndLineCap",
                        typeof(PenLineCap), 
                        typeof(Shape), 
                        new FrameworkPropertyMetadata(
                                PenLineCap.Flat, 
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
                                new PropertyChangedCallback(OnPenChanged)),
                        new ValidateValueCallback(System.Windows.Media.ValidateEnums.IsPenLineCapValid));
 
        /// 
        /// StrokeEndLineCap property 
        ///  
        public PenLineCap StrokeEndLineCap
        { 
            get { return (PenLineCap) GetValue(StrokeEndLineCapProperty); }
            set { SetValue(StrokeEndLineCapProperty, value); }
        }
 

        ///  
        /// StrokeDashCap property 
        /// 
        public static readonly DependencyProperty StrokeDashCapProperty = 
                DependencyProperty.Register(
                        "StrokeDashCap",
                        typeof(PenLineCap),
                        typeof(Shape), 
                        new FrameworkPropertyMetadata(
                                PenLineCap.Flat, 
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, 
                                new PropertyChangedCallback(OnPenChanged)),
                        new ValidateValueCallback(System.Windows.Media.ValidateEnums.IsPenLineCapValid)); 

        /// 
        /// StrokeDashCap property
        ///  
        public PenLineCap StrokeDashCap
        { 
            get { return (PenLineCap) GetValue(StrokeDashCapProperty); } 
            set { SetValue(StrokeDashCapProperty, value); }
        } 

        /// 
        /// StrokeLineJoin property
        ///  
        public static readonly DependencyProperty StrokeLineJoinProperty =
                DependencyProperty.Register( 
                        "StrokeLineJoin", 
                        typeof(PenLineJoin),
                        typeof(Shape), 
                        new FrameworkPropertyMetadata(
                                PenLineJoin.Miter,
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
                                new PropertyChangedCallback(OnPenChanged)), 
                        new ValidateValueCallback(System.Windows.Media.ValidateEnums.IsPenLineJoinValid));
 
        ///  
        /// StrokeLineJoin property
        ///  
        public PenLineJoin StrokeLineJoin
        {
            get { return (PenLineJoin) GetValue(StrokeLineJoinProperty); }
            set { SetValue(StrokeLineJoinProperty, value); } 
        }
 
        ///  
        /// StrokeMiterLimit property
        ///  
        public static readonly DependencyProperty StrokeMiterLimitProperty =
                DependencyProperty.Register(
                        "StrokeMiterLimit",
                        typeof(double), 
                        typeof(Shape),
                        new FrameworkPropertyMetadata( 
                                10.0, 
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
                                new PropertyChangedCallback(OnPenChanged))); 

        /// 
        /// StrokeMiterLimit property
        ///  
        public double StrokeMiterLimit
        { 
            get { return (double) GetValue(StrokeMiterLimitProperty); } 
            set { SetValue(StrokeMiterLimitProperty, value); }
        } 

        /// 
        /// StrokeDashOffset property
        ///  
        public static readonly DependencyProperty StrokeDashOffsetProperty =
                DependencyProperty.Register( 
                        "StrokeDashOffset", 
                        typeof(double),
                        typeof(Shape), 
                        new FrameworkPropertyMetadata(
                                0.0,
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
                                new PropertyChangedCallback(OnPenChanged))); 

        ///  
        /// StrokeDashOffset property 
        /// 
        public double StrokeDashOffset 
        {
            get { return (double) GetValue(StrokeDashOffsetProperty); }
            set { SetValue(StrokeDashOffsetProperty, value); }
        } 

        ///  
        /// StrokeDashArray property 
        /// 
        public static readonly DependencyProperty StrokeDashArrayProperty = 
                DependencyProperty.Register(
                        "StrokeDashArray",
                        typeof(DoubleCollection),
                        typeof(Shape), 
                        new FrameworkPropertyMetadata(
                                new FreezableDefaultValueFactory(DoubleCollection.Empty), 
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, 
                                new PropertyChangedCallback(OnPenChanged)));
 
        /// 
        /// StrokeDashArray property
        /// 
        public DoubleCollection StrokeDashArray 
        {
            get { return (DoubleCollection) GetValue(StrokeDashArrayProperty); } 
            set { SetValue(StrokeDashArrayProperty, value); } 
        }
 
        #endregion

        #region Protected Methods
        ///  
        /// Updates DesiredSize of the shape.  Called by parent UIElement during is the first pass of layout.
        ///  
        /// Constraint size is an "upper limit" that should not exceed. 
        /// Shape's desired size.
        protected override Size MeasureOverride(Size constraint) 
        {
            CacheDefiningGeometry();

            Size newSize; 

            Stretch mode = Stretch; 
 
            if (mode == Stretch.None)
            { 
                newSize = GetNaturalSize();
            }
            else
            { 
                newSize = GetStretchedRenderSize(mode, GetStrokeThickness(), constraint, GetDefiningGeometryBounds());
            } 
 
            if (SizeIsInvalidOrEmpty(newSize))
            { 
                // We've encountered a numerical error. Don't draw anything.
                newSize = new Size(0,0);
                _renderedGeometry = Geometry.Empty;
            } 

            return newSize; 
        } 

        ///  
        /// Compute the rendered geometry and the stretching transform.
        /// 
        protected override Size ArrangeOverride(Size finalSize)
        { 
            Size newSize;
 
            Stretch mode = Stretch; 

            if (mode == Stretch.None) 
            {
                StretchMatrixField.ClearValue(this);

                ResetRenderedGeometry(); 

                newSize = finalSize; 
            } 
            else
            { 
                newSize = GetStretchedRenderSizeAndSetStretchMatrix(
                    mode, GetStrokeThickness(), finalSize, GetDefiningGeometryBounds());
            }
 
            if (SizeIsInvalidOrEmpty(newSize))
            { 
                // We've encountered a numerical error. Don't draw anything. 
                newSize = new Size(0,0);
                _renderedGeometry = Geometry.Empty; 
            }

            return newSize;
        } 

        ///  
        /// Render callback. 
        /// 
        protected override void OnRender(DrawingContext drawingContext) 
        {
            EnsureRenderedGeometry();

            if (_renderedGeometry != Geometry.Empty) 
            {
                drawingContext.DrawGeometry(Fill, GetPen(), _renderedGeometry); 
            } 
        }
 
        #endregion

        #region Protected Properties
 
        /// 
        /// Get the geometry that defines this shape 
        ///  
        protected abstract Geometry DefiningGeometry
        { 
            get;
        }

        #endregion Protected Properties 

        #region Internal Methods 
 
        internal bool SizeIsInvalidOrEmpty(Size size)
        { 
            return (DoubleUtil.IsNaN(size.Width) ||
                    DoubleUtil.IsNaN(size.Height) ||
                    size.IsEmpty);
        } 

        internal bool IsPenNoOp 
        { 
            get
            { 
                double strokeThickness = StrokeThickness;
                return (Stroke == null) || DoubleUtil.IsNaN(strokeThickness) || DoubleUtil.IsZero(strokeThickness);
            }
        } 

        internal double GetStrokeThickness() 
        { 
            if (IsPenNoOp)
            { 
                return 0;
            }
            else
            { 
                return Math.Abs(StrokeThickness);
            } 
 
        }
 
        internal Pen GetPen()
        {
            if (IsPenNoOp)
            { 
                return null;
            } 
 
            if (_pen == null)
            { 
                double thickness = 0.0;
                double strokeThickness = StrokeThickness;

                thickness = Math.Abs(strokeThickness); 

                // This pen is internal to the system and 
                // must not participate in freezable treeness 
                _pen = new Pen();
                _pen.CanBeInheritanceContext = false; 

                _pen.Thickness = thickness;
                _pen.Brush = Stroke;
                _pen.StartLineCap = StrokeStartLineCap; 
                _pen.EndLineCap = StrokeEndLineCap;
                _pen.DashCap = StrokeDashCap; 
                _pen.LineJoin = StrokeLineJoin; 
                _pen.MiterLimit = StrokeMiterLimit;
 
                // StrokeDashArray is usually going to be its default value and GetValue
                // on a mutable default has a per-instance cost associated with it so we'll
                // try to avoid caching the default value
                DoubleCollection strokeDashArray = null; 
                bool hasModifiers;
                if (GetValueSource(StrokeDashArrayProperty, null, out hasModifiers) 
                    != BaseValueSourceInternal.Default || hasModifiers) 
                {
                    strokeDashArray = StrokeDashArray; 
                }

                // Avoid creating the DashStyle if we can
                double strokeDashOffset = StrokeDashOffset; 
                if (strokeDashArray != null || strokeDashOffset != 0.0)
                { 
                    _pen.DashStyle = new DashStyle(strokeDashArray, strokeDashOffset); 
                }
            } 

            return _pen;
        }
 
        // Double verification helpers.  Property system will verify type for us; we only need to verify the value.
        internal static bool IsDoubleFiniteNonNegative(object o) 
        { 
            double d = (double)o;
            return !(Double.IsInfinity(d) || DoubleUtil.IsNaN(d) || d < 0.0); 
        }
        internal static bool IsDoubleFinite(object o)
        {
            double d = (double)o; 
            return !(Double.IsInfinity(d) || DoubleUtil.IsNaN(d));
        } 
        internal static bool IsDoubleFiniteOrNaN(object o) 
        {
            double d = (double)o; 
            return !(Double.IsInfinity(d));
        }

        internal virtual void CacheDefiningGeometry() {} 

        internal Size GetStretchedRenderSize(Stretch mode, double strokeThickness, Size availableSize, Rect geometryBounds) 
        { 
            double xScale, yScale, dX, dY;
            Size renderSize; 

            GetStretchMetrics(mode, strokeThickness, availableSize, geometryBounds,
                out xScale, out yScale, out dX, out dY, out renderSize);
 
            return renderSize;
        } 
 
        internal Size GetStretchedRenderSizeAndSetStretchMatrix(Stretch mode, double strokeThickness, Size availableSize, Rect geometryBounds)
        { 
            double xScale, yScale, dX, dY;
            Size renderSize;

            GetStretchMetrics(mode, strokeThickness, availableSize, geometryBounds, 
                out xScale, out yScale, out dX, out dY, out renderSize);
 
            // Construct the matrix 
            Matrix stretchMatrix = Matrix.Identity;
            stretchMatrix.ScaleAt(xScale, yScale, geometryBounds.Location.X, geometryBounds.Location.Y); 
            stretchMatrix.Translate(dX, dY);
            StretchMatrixField.SetValue(this, new BoxedMatrix(stretchMatrix));

            ResetRenderedGeometry(); 

            return renderSize; 
        } 

        internal void ResetRenderedGeometry() 
        {
            // reset rendered geometry
            _renderedGeometry = null;
        } 

        internal void GetStretchMetrics(Stretch mode, double strokeThickness, Size availableSize, Rect geometryBounds, 
                                             out double xScale, out double yScale, out double dX, out double dY, out Size stretchedSize) 
        {
            if (!geometryBounds.IsEmpty) 
            {
                double margin = strokeThickness / 2;
                bool hasThinDimension = false;
 
                // Initialization for mode == Fill
                xScale = Math.Max(availableSize.Width - strokeThickness, 0); 
                yScale = Math.Max(availableSize.Height - strokeThickness, 0); 
                dX = margin - geometryBounds.Left;
                dY = margin - geometryBounds.Top; 

                // Compute the scale factors from the geometry to the size.
                // The scale factors are ratios, and they have already been initialize to the numerators.
                // To prevent fp overflow, we need to make sure that numerator / denomiator < limit; 
                // To do that without actually deviding, we check that denominator > numerator / limit.
                // We take 1/epsilon as the limit, so the check is denominator > numerator * epsilon 
 
                if (geometryBounds.Width > xScale * Double.Epsilon)
                { 
                    xScale /= geometryBounds.Width;
                }
                else
                { 
                    xScale = 1;
                    hasThinDimension = true; 
                } 

                if (geometryBounds.Height > yScale * Double.Epsilon) 
                {
                    yScale /= geometryBounds.Height;
                }
                else 
                {
                    yScale = 1; 
                    hasThinDimension = true; 
                }
 
                // Because this case was handled by the caller
                Debug.Assert(mode != Stretch.None);

                // We are initialized for Fill, but for the other modes 
                // If one of our dimensions is thin, uniform stretches are
                // meaningless, so we treat the stretch as fill. 
                if (mode != Stretch.Fill && !hasThinDimension) 
                {
                    if (mode == Stretch.Uniform) 
                    {
                        if (yScale > xScale)
                        {
                            // Resize to fit the size's width 
                            yScale = xScale;
                        } 
                        else // if xScale >= yScale 
                        {
                            // Resize to fit the size's height 
                            xScale = yScale;
                        }
                    }
                    else 
                    {
                        Debug.Assert(mode == Stretch.UniformToFill); 
 
                        if (xScale > yScale)
                        { 
                            // Resize to fill the size vertically, spilling out horizontally
                            yScale = xScale;
                        }
                        else // if yScale >= xScale 
                        {
                            // Resize to fill the size horizontally, spilling out vertically 
                            xScale = yScale; 
                        }
                    } 
                }

                stretchedSize = new Size(geometryBounds.Width * xScale + strokeThickness, geometryBounds.Height * yScale + strokeThickness);
            } 
            else
            { 
                xScale = yScale = 1; 
                dX = dY = 0;
                stretchedSize = new Size(0,0); 
            }
        }

        ///  
        /// Get the natural size of the geometry that defines this shape
        ///  
        internal virtual Size GetNaturalSize() 
        {
            Geometry geometry = DefiningGeometry; 

            Debug.Assert(geometry != null);

            // 
            // For the purposes of computing layout size, don't consider dashing. This will give us
            // slightly different bounds, but the computation will be faster and more stable. 
            // 
            // NOTE: If GetPen() is ever made public, we will need to change this logic so the user
            // isn't affected by our surreptitious change of DashStyle. 
            //
            Pen pen = GetPen();
            DashStyle style = null;
 
            if (pen != null)
            { 
                style = pen.DashStyle; 

                if (style != null) 
                {
                    pen.DashStyle = null;
                }
            } 

            Rect bounds = geometry.GetRenderBounds(pen); 
 
            if (style != null)
            { 
                pen.DashStyle = style;
            }

            return new Size(Math.Max(bounds.Right, 0), 
                Math.Max(bounds.Bottom, 0));
        } 
 
        /// 
        /// Get the bonds of the geometry that defines this shape 
        /// 
        internal virtual Rect GetDefiningGeometryBounds()
        {
            Geometry geometry = DefiningGeometry; 

            Debug.Assert(geometry != null); 
 
            return geometry.Bounds;
        } 

        internal void EnsureRenderedGeometry()
        {
            if (_renderedGeometry == null) 
            {
                _renderedGeometry = DefiningGeometry; 
 
                Debug.Assert(_renderedGeometry != null);
 
                if (Stretch != Stretch.None)
                {
                    Geometry currentValue = _renderedGeometry.CloneCurrentValue();
                    if (Object.ReferenceEquals(_renderedGeometry, currentValue)) 
                    {
                        _renderedGeometry = currentValue.Clone(); 
                    } 
                    else
                    { 
                        _renderedGeometry = currentValue;
                    }

                    Transform renderedTransform  = _renderedGeometry.Transform; 

                    BoxedMatrix boxedStretchMatrix = StretchMatrixField.GetValue(this); 
                    Matrix stretchMatrix = (boxedStretchMatrix == null) ? Matrix.Identity : boxedStretchMatrix.Value; 
                    if (renderedTransform == null || renderedTransform.IsIdentity)
                    { 
                        _renderedGeometry.Transform = new MatrixTransform(stretchMatrix);
                    }
                    else
                    { 
                        _renderedGeometry.Transform = new MatrixTransform(renderedTransform.Value * stretchMatrix);
                    } 
                } 
            }
        } 

        #endregion Internal Methods

        #region Private Fields 

        private Pen _pen = null; 
 
        private Geometry _renderedGeometry = Geometry.Empty;
 
        private static UncommonField StretchMatrixField = new UncommonField(null);

        #endregion Private Fields
    } 

    internal class BoxedMatrix 
    { 
        public BoxedMatrix(Matrix value)
        { 
            Value = value;
        }

        public Matrix Value; 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK