|
//------------------------------------------------------------------------------
// <copyright file="Pen.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Drawing
{
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System;
using System.Internal;
using Microsoft.Win32;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Drawing.Internal;
using System.Globalization;
using System.Runtime.Versioning;
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen"]/*' />
/// <devdoc>
/// <para>
/// Defines an object used to draw lines and curves.
/// </para>
/// </devdoc>
public sealed class Pen : MarshalByRefObject, ISystemColorTracker, ICloneable, IDisposable {
#if FINALIZATION_WATCH
private string allocationSite = Graphics.GetAllocationStack();
#endif
// handle to native GDI+ pen object.
private IntPtr nativePen;
// GDI+ doesn't understand system colors, so we need to cache the value here
private Color color;
private bool immutable;
/// <devdoc>
/// Creates a Pen from a native GDI+ object.
/// </devdoc>
private Pen(IntPtr nativePen) {
SetNativePen( nativePen );
}
[ResourceExposure(ResourceScope.Process)]
[ResourceConsumption(ResourceScope.Process)]
internal Pen(Color color, bool immutable) : this(color) {
this.immutable = immutable;
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.Pen"]/*' />
/// <devdoc>
/// <para>
/// Initializes a new instance of the Pen
/// class with the specified <see cref='System.Drawing.Pen.Color'/>.
/// </para>
/// </devdoc>
[ResourceExposure(ResourceScope.Process)]
[ResourceConsumption(ResourceScope.Process)]
public Pen(Color color) : this(color, (float)1.0) {
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.Pen1"]/*' />
/// <devdoc>
/// <para>
/// Initializes a new instance of the <see cref='System.Drawing.Pen'/> class with the specified
/// <see cref='System.Drawing.Pen.Color'/> and <see cref='System.Drawing.Pen.Width'/>.
/// </para>
/// </devdoc>
[ResourceExposure(ResourceScope.Process)]
[ResourceConsumption(ResourceScope.Process)]
public Pen(Color color, float width) {
this.color = color;
IntPtr pen = IntPtr.Zero;
int status = SafeNativeMethods.Gdip.GdipCreatePen1(color.ToArgb(),
width,
(int)GraphicsUnit.World,
out pen);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
SetNativePen(pen);
if (this.color.IsSystemColor) {
SystemColorTracker.Add(this);
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.Pen2"]/*' />
/// <devdoc>
/// <para>
/// Initializes a new instance of the Pen class with the
/// specified <see cref='System.Drawing.Pen.Brush'/>.
/// </para>
/// </devdoc>
[ResourceExposure(ResourceScope.Process)]
[ResourceConsumption(ResourceScope.Process)]
public Pen(Brush brush) : this(brush, (float)1.0) {
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.Pen3"]/*' />
/// <devdoc>
/// <para>
/// Initializes a new instance of the <see cref='System.Drawing.Pen'/> class with
/// the specified <see cref='System.Drawing.Brush'/> and width.
/// </para>
/// </devdoc>
[ResourceExposure(ResourceScope.Process)]
[ResourceConsumption(ResourceScope.Process)]
public Pen(Brush brush, float width) {
IntPtr pen = IntPtr.Zero;
if (brush == null)
throw new ArgumentNullException("brush");
int status = SafeNativeMethods.Gdip.GdipCreatePen2(new HandleRef(brush, brush.NativeBrush),
width,
(int)GraphicsUnit.World,
out pen);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
SetNativePen(pen);
}
internal void SetNativePen(IntPtr nativePen) {
if (nativePen == IntPtr.Zero) {
throw new ArgumentNullException("nativePen");
}
this.nativePen = nativePen;
}
/// <devdoc>
/// Gets the GDI+ native object.
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
internal IntPtr NativePen
{
get
{
//Need to comment this line out to allow for checking this.NativePen == IntPtr.Zero.
//Debug.Assert(this.nativePen != IntPtr.Zero, "this.nativePen == null." );
return this.nativePen;
}
}
/**
* Create a copy of the pen object
*/
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.Clone"]/*' />
/// <devdoc>
/// Creates an exact copy of this <see cref='System.Drawing.Pen'/>.
/// </devdoc>
[ResourceExposure(ResourceScope.Process)]
[ResourceConsumption(ResourceScope.Process)]
public object Clone()
{
IntPtr clonePen = IntPtr.Zero;
int status = SafeNativeMethods.Gdip.GdipClonePen(new HandleRef(this, this.NativePen), out clonePen);
if (status != SafeNativeMethods.Gdip.Ok) {
throw SafeNativeMethods.Gdip.StatusException(status);
}
return new Pen(clonePen);
}
/**
* Dispose of resources associated with the Pen object
*/
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.Dispose"]/*' />
/// <devdoc>
/// Cleans up Windows resources for this <see cref='System.Drawing.Pen'/>.
/// </devdoc>
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
void Dispose(bool disposing)
{
#if FINALIZATION_WATCH
if (!disposing && nativePen != IntPtr.Zero)
Debug.WriteLine("**********************\nDisposed through finalization:\n" + allocationSite);
#endif
if (!disposing)
{
// If we are finalizing, then we will be unreachable soon. Finalize calls dispose to
// release resources, so we must make sure that during finalization we are
// not immutable.
//
immutable = false;
}
else if (immutable)
{
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Brush"));
}
if (this.nativePen != IntPtr.Zero)
{
try{
#if DEBUG
int status =
#endif
SafeNativeMethods.Gdip.GdipDeletePen(new HandleRef(this, this.NativePen));
#if DEBUG
Debug.Assert(status == SafeNativeMethods.Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture));
#endif
}
catch( Exception ex ){
if(ClientUtils.IsSecurityOrCriticalException( ex ) ) {
throw;
}
Debug.Fail( "Exception thrown during Dispose: " + ex.ToString() );
}
finally{
this.nativePen = IntPtr.Zero;
}
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.Finalize"]/*' />
/// <devdoc>
/// Cleans up Windows resources for this <see cref='System.Drawing.Pen'/>.
/// </devdoc>
~Pen() {
Dispose(false);
}
/**
* Set/get pen width
*/
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.Width"]/*' />
/// <devdoc>
/// Gets or sets the width of this <see cref='System.Drawing.Pen'/>.
/// </devdoc>
public float Width
{
get
{
float[] width = new float[] { 0};
int status = SafeNativeMethods.Gdip.GdipGetPenWidth(new HandleRef(this, this.NativePen), width);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return width[0];
}
set
{
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenWidth(new HandleRef(this, this.NativePen), value);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
/**
* Set/get line caps: start, end, and dash
*/
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.SetLineCap"]/*' />
/// <devdoc>
/// <para>
/// Sets the values that determine the style of
/// cap used to end lines drawn by this <see cref='System.Drawing.Pen'/>.
/// </para>
/// </devdoc>
public void SetLineCap(LineCap startCap, LineCap endCap, DashCap dashCap) {
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenLineCap197819(new HandleRef(this, this.NativePen),
unchecked((int)startCap), unchecked((int)endCap), unchecked((int)dashCap));
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.StartCap"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the cap style used at the
/// beginning of lines drawn with this <see cref='System.Drawing.Pen'/>.
/// </para>
/// </devdoc>
public LineCap StartCap
{
get {
int startCap = 0;
int status = SafeNativeMethods.Gdip.GdipGetPenStartCap(new HandleRef(this, this.NativePen), out startCap);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return(LineCap) startCap;
}
set {
//validate the enum value
switch(value) {
case LineCap.Flat:
case LineCap.Square:
case LineCap.Round:
case LineCap.Triangle:
case LineCap.NoAnchor:
case LineCap.SquareAnchor:
case LineCap.RoundAnchor:
case LineCap.DiamondAnchor:
case LineCap.ArrowAnchor:
case LineCap.AnchorMask:
case LineCap.Custom:
break;
default:
throw new InvalidEnumArgumentException("value", unchecked((int)value), typeof(LineCap));
}
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenStartCap(new HandleRef(this, this.NativePen), unchecked((int)value));
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.EndCap"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the cap style used at the end of
/// lines drawn with this <see cref='System.Drawing.Pen'/>.
/// </para>
/// </devdoc>
public LineCap EndCap
{
get {
int endCap = 0;
int status = SafeNativeMethods.Gdip.GdipGetPenEndCap(new HandleRef(this, this.NativePen), out endCap);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return(LineCap) endCap;
}
set {
//validate the enum value
switch(value) {
case LineCap.Flat:
case LineCap.Square:
case LineCap.Round:
case LineCap.Triangle:
case LineCap.NoAnchor:
case LineCap.SquareAnchor:
case LineCap.RoundAnchor:
case LineCap.DiamondAnchor:
case LineCap.ArrowAnchor:
case LineCap.AnchorMask:
case LineCap.Custom:
break;
default:
throw new InvalidEnumArgumentException("value", unchecked((int)value), typeof(LineCap));
}
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenEndCap(new HandleRef(this, this.NativePen), unchecked((int)value));
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.DashCap"]/*' />
/// <devdoc>
/// Gets or sets the cap style used at the
/// beginning or end of dashed lines drawn with this <see cref='System.Drawing.Pen'/>.
/// </devdoc>
public DashCap DashCap
{
get {
int dashCap = 0;
int status = SafeNativeMethods.Gdip.GdipGetPenDashCap197819(new HandleRef(this, this.NativePen), out dashCap);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return(DashCap)dashCap;
}
set {
//validate the enum value
if (!ClientUtils.IsEnumValid_NotSequential(value, unchecked((int)value),
(int)DashCap.Flat,
(int)DashCap.Round,
(int)DashCap.Triangle))
{
throw new InvalidEnumArgumentException("value", unchecked((int)value), typeof(DashCap));
}
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenDashCap197819(new HandleRef(this, this.NativePen), unchecked((int)value));
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
/**
* Set/get line join
*/
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.LineJoin"]/*' />
/// <devdoc>
/// Gets or sets the join style for the ends of
/// two overlapping lines drawn with this <see cref='System.Drawing.Pen'/>.
/// </devdoc>
public LineJoin LineJoin
{
get {
int lineJoin = 0;
int status = SafeNativeMethods.Gdip.GdipGetPenLineJoin(new HandleRef(this, this.NativePen), out lineJoin);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return(LineJoin)lineJoin;
}
set {
//valid values are 0x0 to 0x3
if (!ClientUtils.IsEnumValid(value, unchecked((int)value), (int)LineJoin.Miter, (int)LineJoin.MiterClipped))
{
throw new InvalidEnumArgumentException("value", unchecked((int)value), typeof(LineJoin));
}
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenLineJoin(new HandleRef(this, this.NativePen), unchecked((int)value));
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
/**
* Set/get custom start line cap
*/
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.CustomStartCap"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets a custom cap style to use at the beginning of lines
/// drawn with this <see cref='System.Drawing.Pen'/>.
/// </para>
/// </devdoc>
public CustomLineCap CustomStartCap
{
get {
IntPtr lineCap = IntPtr.Zero;
int status = SafeNativeMethods.Gdip.GdipGetPenCustomStartCap(new HandleRef(this, this.NativePen), out lineCap);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return CustomLineCap.CreateCustomLineCapObject(lineCap);
}
set {
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenCustomStartCap(new HandleRef(this, this.NativePen),
new HandleRef(value, (value == null) ? IntPtr.Zero : value.nativeCap));
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
/**
* Set/get custom end line cap
*/
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.CustomEndCap"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets a custom cap style to use at the end of lines
/// drawn with this <see cref='System.Drawing.Pen'/>.
/// </para>
/// </devdoc>
public CustomLineCap CustomEndCap
{
get {
IntPtr lineCap = IntPtr.Zero;
int status = SafeNativeMethods.Gdip.GdipGetPenCustomEndCap(new HandleRef(this, this.NativePen), out lineCap);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return CustomLineCap.CreateCustomLineCapObject(lineCap);
}
set {
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenCustomEndCap(new HandleRef(this, this.NativePen),
new HandleRef(value, (value == null) ? IntPtr.Zero : value.nativeCap));
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.MiterLimit"]/*' />
/// <devdoc>
/// Gets or sets the limit of the thickness of
/// the join on a mitered corner.
/// </devdoc>
public float MiterLimit
{
get {
float[] miterLimit = new float[] { 0};
int status = SafeNativeMethods.Gdip.GdipGetPenMiterLimit(new HandleRef(this, this.NativePen), miterLimit);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return miterLimit[0];
}
set {
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenMiterLimit(new HandleRef(this, this.NativePen), value);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
/**
* Pen Mode
*/
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.Alignment"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets
/// the alignment for objects drawn with this <see cref='System.Drawing.Pen'/>.
/// </para>
/// </devdoc>
public PenAlignment Alignment
{
get {
PenAlignment penMode = 0;
int status = SafeNativeMethods.Gdip.GdipGetPenMode(new HandleRef(this, this.NativePen), out penMode);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return(PenAlignment) penMode;
}
set {
//validate the enum value
//valid values are 0x0 to 0x4
if (!ClientUtils.IsEnumValid(value, unchecked((int)value), (int)PenAlignment.Center, (int)PenAlignment.Right))
{
throw new InvalidEnumArgumentException("value", unchecked((int)value), typeof(PenAlignment));
}
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenMode(new HandleRef(this, this.NativePen), value);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
/**
* Set/get pen transform
*/
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.Transform"]/*' />
/// <devdoc>
/// <para>
/// Gets
/// or sets the geometrical transform for objects drawn with this <see cref='System.Drawing.Pen'/>.
/// </para>
/// </devdoc>
public Matrix Transform
{
get {
Matrix matrix = new Matrix();
int status = SafeNativeMethods.Gdip.GdipGetPenTransform(new HandleRef(this, this.NativePen), new HandleRef(matrix, matrix.nativeMatrix));
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return matrix;
}
set {
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
if (value == null) {
throw new ArgumentNullException("value");
}
int status = SafeNativeMethods.Gdip.GdipSetPenTransform(new HandleRef(this, this.NativePen), new HandleRef(value, value.nativeMatrix));
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.ResetTransform"]/*' />
/// <devdoc>
/// Resets the geometric transform for this
/// <see cref='System.Drawing.Pen'/> to
/// identity.
/// </devdoc>
public void ResetTransform() {
int status = SafeNativeMethods.Gdip.GdipResetPenTransform(new HandleRef(this, this.NativePen));
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.MultiplyTransform"]/*' />
/// <devdoc>
/// <para>
/// Multiplies the transform matrix for this
/// <see cref='System.Drawing.Pen'/> by
/// the specified <see cref='System.Drawing.Drawing2D.Matrix'/>.
/// </para>
/// </devdoc>
public void MultiplyTransform(Matrix matrix) {
MultiplyTransform(matrix, MatrixOrder.Prepend);
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.MultiplyTransform1"]/*' />
/// <devdoc>
/// <para>
/// Multiplies the transform matrix for this
/// <see cref='System.Drawing.Pen'/> by
/// the specified <see cref='System.Drawing.Drawing2D.Matrix'/> in the specified order.
/// </para>
/// </devdoc>
public void MultiplyTransform(Matrix matrix, MatrixOrder order) {
int status = SafeNativeMethods.Gdip.GdipMultiplyPenTransform(new HandleRef(this, this.NativePen),
new HandleRef(matrix, matrix.nativeMatrix),
order);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.TranslateTransform"]/*' />
/// <devdoc>
/// <para>
/// Translates the local geometrical transform
/// by the specified dimmensions. This method prepends the translation to the
/// transform.
/// </para>
/// </devdoc>
public void TranslateTransform(float dx, float dy) {
TranslateTransform(dx, dy, MatrixOrder.Prepend);
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.TranslateTransform1"]/*' />
/// <devdoc>
/// Translates the local geometrical transform
/// by the specified dimmensions in the specified order.
/// </devdoc>
public void TranslateTransform(float dx, float dy, MatrixOrder order) {
int status = SafeNativeMethods.Gdip.GdipTranslatePenTransform(new HandleRef(this, this.NativePen),
dx, dy, order);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.ScaleTransform"]/*' />
/// <devdoc>
/// Scales the local geometric transform by the
/// specified amounts. This method prepends the scaling matrix to the transform.
/// </devdoc>
public void ScaleTransform(float sx, float sy) {
ScaleTransform(sx, sy, MatrixOrder.Prepend);
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.ScaleTransform1"]/*' />
/// <devdoc>
/// <para>
/// Scales the local geometric transform by the
/// specified amounts in the specified order.
/// </para>
/// </devdoc>
public void ScaleTransform(float sx, float sy, MatrixOrder order) {
int status = SafeNativeMethods.Gdip.GdipScalePenTransform(new HandleRef(this, this.NativePen),
sx, sy, order);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.RotateTransform"]/*' />
/// <devdoc>
/// Rotates the local geometric transform by the
/// specified amount. This method prepends the rotation to the transform.
/// </devdoc>
public void RotateTransform(float angle) {
RotateTransform(angle, MatrixOrder.Prepend);
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.RotateTransform1"]/*' />
/// <devdoc>
/// <para>
/// Rotates the local geometric transform by the specified
/// amount in the specified order.
/// </para>
/// </devdoc>
public void RotateTransform(float angle, MatrixOrder order) {
int status = SafeNativeMethods.Gdip.GdipRotatePenTransform(new HandleRef(this, this.NativePen),
angle, order);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
/**
* Set/get pen type (color, line texture, or brush)
*
* @notes GetLineFill returns either a Brush object
* or a LineTexture object.
*/
private void InternalSetColor(Color value) {
int status = SafeNativeMethods.Gdip.GdipSetPenColor(new HandleRef(this, this.NativePen),
color.ToArgb());
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
this.color = value;
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.PenType"]/*' />
/// <devdoc>
/// Gets the style of lines drawn with this
/// <see cref='System.Drawing.Pen'/>.
/// </devdoc>
public PenType PenType
{
get {
int type = -1;
int status = SafeNativeMethods.Gdip.GdipGetPenFillType(new HandleRef(this, this.NativePen), out type);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return(PenType)type;
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.Color"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the color of this <see cref='System.Drawing.Pen'/>.
/// </para>
/// </devdoc>
public Color Color {
get {
if (color == Color.Empty) {
int colorARGB = 0;
int status = SafeNativeMethods.Gdip.GdipGetPenColor(new HandleRef(this, this.NativePen), out colorARGB);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
this.color = Color.FromArgb(colorARGB);
}
// GDI+ doesn't understand system colors, so we can't use GdipGetPenColor in the general case
return this.color;
}
set {
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
if( value != this.color )
{
Color oldColor = this.color;
this.color = value;
InternalSetColor(value);
//
if (value.IsSystemColor && !oldColor.IsSystemColor)
SystemColorTracker.Add(this);
}
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.Brush"]/*' />
/// <devdoc>
/// Gets or sets the <see cref='System.Drawing.Brush'/> that
/// determines attributes of this <see cref='System.Drawing.Pen'/>.
/// </devdoc>
public Brush Brush {
get {
Brush brush = null;
switch (PenType) {
case PenType.SolidColor:
brush = new SolidBrush(GetNativeBrush());
break;
case PenType.HatchFill:
brush = new HatchBrush(GetNativeBrush());
break;
case PenType.TextureFill:
brush = new TextureBrush(GetNativeBrush());
break;
case PenType.PathGradient:
brush = new PathGradientBrush(GetNativeBrush());
break;
case PenType.LinearGradient:
brush = new LinearGradientBrush(GetNativeBrush());
break;
default:
break;
}
return brush;
}
set {
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
if (value == null)
throw new ArgumentNullException("value");
int status = SafeNativeMethods.Gdip.GdipSetPenBrushFill(new HandleRef(this, this.NativePen),
new HandleRef(value, value.NativeBrush));
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
private IntPtr GetNativeBrush()
{
IntPtr nativeBrush = IntPtr.Zero;
int status = SafeNativeMethods.Gdip.GdipGetPenBrushFill(new HandleRef(this, this.NativePen), out nativeBrush);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return nativeBrush;
}
/**
* Set/get dash attributes
*/
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.DashStyle"]/*' />
/// <devdoc>
/// Gets or sets the style used for dashed
/// lines drawn with this <see cref='System.Drawing.Pen'/>.
/// </devdoc>
public DashStyle DashStyle
{
get {
int dashstyle = 0;
int status = SafeNativeMethods.Gdip.GdipGetPenDashStyle(new HandleRef(this, this.NativePen), out dashstyle);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return(DashStyle) dashstyle;
}
set {
//valid values are 0x0 to 0x5
if (!ClientUtils.IsEnumValid(value, unchecked((int)value), (int)DashStyle.Solid, (int)DashStyle.Custom))
{
throw new InvalidEnumArgumentException("value", unchecked((int)value), typeof(DashStyle));
}
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenDashStyle(new HandleRef(this, this.NativePen), unchecked((int)value));
if (status != SafeNativeMethods.Gdip.Ok) {
throw SafeNativeMethods.Gdip.StatusException(status);
}
//if we just set pen style to "custom" without defining the custom dash pattern,
//lets make sure we can return a valid value...
//
if (value == DashStyle.Custom) {
EnsureValidDashPattern();
}
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.EnsureValidDashPattern"]/*' />
/// <devdoc>
/// This method is called after the user sets the pen's dash style to custom.
/// Here, we make sure that there is a default value set for the custom pattern.
/// </devdoc>
private void EnsureValidDashPattern() {
int retval = 0;
int status = SafeNativeMethods.Gdip.GdipGetPenDashCount(new HandleRef(this, this.NativePen), out retval);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
if (retval == 0) {
//just set to a solid pattern
DashPattern = new float[]{1};
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.DashOffset"]/*' />
/// <devdoc>
/// Gets or sets the distance from the start of
/// a line to the beginning of a dash pattern.
/// </devdoc>
public float DashOffset
{
get {
float[] dashoffset = new float[] { 0};
int status = SafeNativeMethods.Gdip.GdipGetPenDashOffset(new HandleRef(this, this.NativePen), dashoffset);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return dashoffset[0];
}
set {
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenDashOffset(new HandleRef(this, this.NativePen), value);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.DashPattern"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets an array of cutom dashes and
/// spaces. The dashes are made up of line segments.
/// </para>
/// </devdoc>
public float[] DashPattern
{
get {
float[] dashArray;
// Figure out how many dash elements we have
int retval = 0;
int status = SafeNativeMethods.Gdip.GdipGetPenDashCount(new HandleRef(this, this.NativePen), out retval);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
int count = retval;
// Allocate temporary native memory buffer
// and pass it to GDI+ to retrieve dash array elements
IntPtr buf = Marshal.AllocHGlobal(checked(4 * count));
status = SafeNativeMethods.Gdip.GdipGetPenDashArray(new HandleRef(this, this.NativePen), buf, count);
try {
if (status != SafeNativeMethods.Gdip.Ok) {
throw SafeNativeMethods.Gdip.StatusException(status);
}
dashArray = new float[count];
Marshal.Copy(buf, dashArray, 0, count);
}
finally {
Marshal.FreeHGlobal(buf);
}
return dashArray;
}
set {
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
//validate the DashPattern value being set
if (value == null || value.Length == 0) {
throw new ArgumentException(SR.GetString(SR.InvalidDashPattern));
}
int count = value.Length;
IntPtr buf = Marshal.AllocHGlobal(checked(4 * count));
try {
Marshal.Copy(value, 0, buf, count);
int status = SafeNativeMethods.Gdip.GdipSetPenDashArray(new HandleRef(this, this.NativePen), new HandleRef(buf, buf), count);
if (status != SafeNativeMethods.Gdip.Ok){
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
finally {
Marshal.FreeHGlobal(buf);
}
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.CompoundArray"]/*' />
/// <devdoc>
/// Gets or sets an array of cutom dashes and
/// spaces. The dashes are made up of line segments.
/// </devdoc>
public float[] CompoundArray
{
get {
int count = 0;
int status = SafeNativeMethods.Gdip.GdipGetPenCompoundCount(new HandleRef(this, this.NativePen), out count);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
float[] array = new float[count];
status = SafeNativeMethods.Gdip.GdipGetPenCompoundArray(new HandleRef(this, this.NativePen), array, count);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
return array;
}
set {
if (immutable)
throw new ArgumentException(SR.GetString(SR.CantChangeImmutableObjects, "Pen"));
int status = SafeNativeMethods.Gdip.GdipSetPenCompoundArray(new HandleRef(this, this.NativePen), value, value.Length);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
}
}
/// <include file='doc\Pen.uex' path='docs/doc[@for="Pen.ISystemColorTracker.OnSystemColorChanged"]/*' />
/// <internalonly/>
void ISystemColorTracker.OnSystemColorChanged() {
if (this.NativePen != IntPtr.Zero)
InternalSetColor(color);
}
}
}
|