Pen
Defines an object used to draw lines and curves. This class cannot be inherited.
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Drawing.Internal;
using System.Runtime.CompilerServices;
using Windows.Win32;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.Graphics.GdiPlus;
namespace System.Drawing
{
[NullableContext(1)]
[Nullable(0)]
public sealed class Pen : MarshalByRefObject, ICloneable, IDisposable, ISystemColorTracker
{
[Nullable(0)]
private unsafe GpPen* _nativePen;
private Color _color;
private bool _immutable;
private bool _dashStyleWasOrIsNotSolid;
[Nullable(0)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
internal unsafe GpPen* NativePen {
[NullableContext(0)]
get {
return _nativePen;
}
}
public unsafe float Width {
get {
float result = default(float);
PInvokeGdiPlus.GdipGetPenWidth(NativePen, &result).ThrowIfFailed();
GC.KeepAlive(this);
return result;
}
set {
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
PInvokeGdiPlus.GdipSetPenWidth(NativePen, value).ThrowIfFailed();
GC.KeepAlive(this);
}
}
public unsafe System.Drawing.Drawing2D.LineCap StartCap {
get {
System.Drawing.Drawing2D.LineCap result = default(System.Drawing.Drawing2D.LineCap);
PInvokeGdiPlus.GdipGetPenStartCap(NativePen, (global::Windows.Win32.Graphics.GdiPlus.LineCap*)(&result)).ThrowIfFailed();
GC.KeepAlive(this);
return result;
}
set {
if (value <= System.Drawing.Drawing2D.LineCap.ArrowAnchor) {
if ((uint)value > 3 && (uint)(value - 16) > 4)
goto IL_0022;
} else if (value != System.Drawing.Drawing2D.LineCap.AnchorMask && value != System.Drawing.Drawing2D.LineCap.Custom) {
goto IL_0022;
}
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
PInvokeGdiPlus.GdipSetPenStartCap(NativePen, (global::Windows.Win32.Graphics.GdiPlus.LineCap)value).ThrowIfFailed();
GC.KeepAlive(this);
return;
IL_0022:
throw new InvalidEnumArgumentException("value", (int)value, typeof(System.Drawing.Drawing2D.LineCap));
}
}
public unsafe System.Drawing.Drawing2D.LineCap EndCap {
get {
System.Drawing.Drawing2D.LineCap result = default(System.Drawing.Drawing2D.LineCap);
PInvokeGdiPlus.GdipGetPenEndCap(NativePen, (global::Windows.Win32.Graphics.GdiPlus.LineCap*)(&result)).ThrowIfFailed();
GC.KeepAlive(this);
return result;
}
set {
if (value <= System.Drawing.Drawing2D.LineCap.ArrowAnchor) {
if ((uint)value > 3 && (uint)(value - 16) > 4)
goto IL_0022;
} else if (value != System.Drawing.Drawing2D.LineCap.AnchorMask && value != System.Drawing.Drawing2D.LineCap.Custom) {
goto IL_0022;
}
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
PInvokeGdiPlus.GdipSetPenEndCap(NativePen, (global::Windows.Win32.Graphics.GdiPlus.LineCap)value).ThrowIfFailed();
GC.KeepAlive(this);
return;
IL_0022:
throw new InvalidEnumArgumentException("value", (int)value, typeof(System.Drawing.Drawing2D.LineCap));
}
}
public unsafe CustomLineCap CustomStartCap {
get {
GpCustomLineCap* cap = default(GpCustomLineCap*);
PInvokeGdiPlus.GdipGetPenCustomStartCap(NativePen, &cap).ThrowIfFailed();
GC.KeepAlive(this);
return CustomLineCap.CreateCustomLineCapObject(cap);
}
set {
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
PInvokeGdiPlus.GdipSetPenCustomStartCap(NativePen, (GpCustomLineCap*)(long)((value == null) ? ((IntPtr)(void*)null) : ((IntPtr)value._nativeCap))).ThrowIfFailed();
GC.KeepAlive(value);
GC.KeepAlive(this);
}
}
public unsafe CustomLineCap CustomEndCap {
get {
GpCustomLineCap* cap = default(GpCustomLineCap*);
PInvokeGdiPlus.GdipGetPenCustomEndCap(NativePen, &cap).ThrowIfFailed();
GC.KeepAlive(this);
return CustomLineCap.CreateCustomLineCapObject(cap);
}
set {
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
PInvokeGdiPlus.GdipSetPenCustomEndCap(NativePen, (GpCustomLineCap*)(long)((value == null) ? ((IntPtr)(void*)null) : ((IntPtr)value._nativeCap))).ThrowIfFailed();
GC.KeepAlive(value);
GC.KeepAlive(this);
}
}
public unsafe System.Drawing.Drawing2D.DashCap DashCap {
get {
System.Drawing.Drawing2D.DashCap result = default(System.Drawing.Drawing2D.DashCap);
PInvokeGdiPlus.GdipGetPenDashCap197819(NativePen, (global::Windows.Win32.Graphics.GdiPlus.DashCap*)(&result)).ThrowIfFailed();
GC.KeepAlive(this);
return result;
}
set {
if (value != 0 && value != System.Drawing.Drawing2D.DashCap.Round && value != System.Drawing.Drawing2D.DashCap.Triangle)
throw new InvalidEnumArgumentException("value", (int)value, typeof(System.Drawing.Drawing2D.DashCap));
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
PInvokeGdiPlus.GdipSetPenDashCap197819(NativePen, (global::Windows.Win32.Graphics.GdiPlus.DashCap)value).ThrowIfFailed();
GC.KeepAlive(this);
}
}
public unsafe System.Drawing.Drawing2D.LineJoin LineJoin {
get {
System.Drawing.Drawing2D.LineJoin result = default(System.Drawing.Drawing2D.LineJoin);
PInvokeGdiPlus.GdipGetPenLineJoin(NativePen, (global::Windows.Win32.Graphics.GdiPlus.LineJoin*)(&result)).ThrowIfFailed();
GC.KeepAlive(this);
return result;
}
set {
if ((value < System.Drawing.Drawing2D.LineJoin.Miter || value > System.Drawing.Drawing2D.LineJoin.MiterClipped) ? true : false)
throw new InvalidEnumArgumentException("value", (int)value, typeof(System.Drawing.Drawing2D.LineJoin));
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
PInvokeGdiPlus.GdipSetPenLineJoin(NativePen, (global::Windows.Win32.Graphics.GdiPlus.LineJoin)value).ThrowIfFailed();
GC.KeepAlive(this);
}
}
public unsafe float MiterLimit {
get {
float result = default(float);
PInvokeGdiPlus.GdipGetPenMiterLimit(NativePen, &result).ThrowIfFailed();
GC.KeepAlive(this);
return result;
}
set {
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
PInvokeGdiPlus.GdipSetPenMiterLimit(NativePen, value).ThrowIfFailed();
GC.KeepAlive(this);
}
}
public unsafe System.Drawing.Drawing2D.PenAlignment Alignment {
get {
System.Drawing.Drawing2D.PenAlignment result = default(System.Drawing.Drawing2D.PenAlignment);
PInvokeGdiPlus.GdipGetPenMode(NativePen, (global::Windows.Win32.Graphics.GdiPlus.PenAlignment*)(&result)).ThrowIfFailed();
GC.KeepAlive(this);
return result;
}
set {
if ((value < System.Drawing.Drawing2D.PenAlignment.Center || value > System.Drawing.Drawing2D.PenAlignment.Right) ? true : false)
throw new InvalidEnumArgumentException("value", (int)value, typeof(System.Drawing.Drawing2D.PenAlignment));
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
PInvokeGdiPlus.GdipSetPenMode(NativePen, (global::Windows.Win32.Graphics.GdiPlus.PenAlignment)value).ThrowIfFailed();
GC.KeepAlive(this);
}
}
public unsafe System.Drawing.Drawing2D.Matrix Transform {
get {
System.Drawing.Drawing2D.Matrix matrix = new System.Drawing.Drawing2D.Matrix();
PInvokeGdiPlus.GdipGetPenTransform(NativePen, matrix.NativeMatrix).ThrowIfFailed();
GC.KeepAlive(this);
return matrix;
}
set {
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
ArgumentNullException.ThrowIfNull(value, "value");
PInvokeGdiPlus.GdipSetPenTransform(NativePen, value.NativeMatrix).ThrowIfFailed();
GC.KeepAlive(value);
GC.KeepAlive(this);
}
}
public unsafe System.Drawing.Drawing2D.PenType PenType {
get {
global::Windows.Win32.Graphics.GdiPlus.PenType result = default(global::Windows.Win32.Graphics.GdiPlus.PenType);
PInvokeGdiPlus.GdipGetPenFillType(NativePen, &result).ThrowIfFailed();
GC.KeepAlive(this);
return (System.Drawing.Drawing2D.PenType)result;
}
}
public unsafe Color Color {
get {
if (_color == Color.Empty) {
if (PenType != 0)
throw new ArgumentException(System.SR.GdiplusInvalidParameter);
ARGB argb = default(ARGB);
PInvokeGdiPlus.GdipGetPenColor(NativePen, (uint*)(&argb)).ThrowIfFailed();
GC.KeepAlive(this);
_color = argb;
}
return _color;
}
set {
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
if (value != _color) {
Color color = _color;
_color = value;
InternalSetColor(value);
if (value.IsSystemColor && !color.IsSystemColor)
SystemColorTracker.Add(this);
}
}
}
public unsafe Brush Brush {
get {
Brush result = null;
switch (PenType) {
case System.Drawing.Drawing2D.PenType.SolidColor:
result = new SolidBrush((GpSolidFill*)GetNativeBrush());
break;
case System.Drawing.Drawing2D.PenType.HatchFill:
result = new HatchBrush((GpHatch*)GetNativeBrush());
break;
case System.Drawing.Drawing2D.PenType.TextureFill:
result = new TextureBrush((GpTexture*)GetNativeBrush());
break;
case System.Drawing.Drawing2D.PenType.PathGradient:
result = new PathGradientBrush((GpPathGradient*)GetNativeBrush());
break;
case System.Drawing.Drawing2D.PenType.LinearGradient:
result = new LinearGradientBrush((GpLineGradient*)GetNativeBrush());
break;
}
return result;
}
set {
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
ArgumentNullException.ThrowIfNull(value, "value");
PInvokeGdiPlus.GdipSetPenBrushFill(NativePen, value.Pointer()).ThrowIfFailed();
GC.KeepAlive(this);
}
}
public unsafe System.Drawing.Drawing2D.DashStyle DashStyle {
get {
System.Drawing.Drawing2D.DashStyle result = default(System.Drawing.Drawing2D.DashStyle);
PInvokeGdiPlus.GdipGetPenDashStyle(NativePen, (global::Windows.Win32.Graphics.GdiPlus.DashStyle*)(&result)).ThrowIfFailed();
GC.KeepAlive(this);
return result;
}
set {
if ((value < System.Drawing.Drawing2D.DashStyle.Solid || value > System.Drawing.Drawing2D.DashStyle.Custom) ? true : false)
throw new InvalidEnumArgumentException("value", (int)value, typeof(System.Drawing.Drawing2D.DashStyle));
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
PInvokeGdiPlus.GdipSetPenDashStyle(NativePen, (global::Windows.Win32.Graphics.GdiPlus.DashStyle)value).ThrowIfFailed();
GC.KeepAlive(this);
if (value == System.Drawing.Drawing2D.DashStyle.Custom)
EnsureValidDashPattern();
if (value != 0)
_dashStyleWasOrIsNotSolid = true;
}
}
public unsafe float DashOffset {
get {
float result = default(float);
PInvokeGdiPlus.GdipGetPenDashOffset(NativePen, &result).ThrowIfFailed();
GC.KeepAlive(this);
return result;
}
set {
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
PInvokeGdiPlus.GdipSetPenDashOffset(NativePen, value).ThrowIfFailed();
GC.KeepAlive(this);
}
}
public unsafe float[] DashPattern {
get {
int num = default(int);
PInvokeGdiPlus.GdipGetPenDashCount(NativePen, &num).ThrowIfFailed();
GC.KeepAlive(this);
float[] array;
if (num > 0) {
array = new float[num];
fixed (float* dash = array) {
PInvokeGdiPlus.GdipGetPenDashArray(NativePen, dash, num).ThrowIfFailed();
}
} else {
if (DashStyle == System.Drawing.Drawing2D.DashStyle.Solid && !_dashStyleWasOrIsNotSolid)
throw new InvalidOperationException();
array = ((DashStyle != 0) ? new float[1] {
1
} : Array.Empty<float>());
}
GC.KeepAlive(this);
return array;
}
set {
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
if (value == null || value.Length == 0)
throw new ArgumentException(System.SR.InvalidDashPattern);
fixed (float* dash = value) {
PInvokeGdiPlus.GdipSetPenDashArray(NativePen, dash, value.Length).ThrowIfFailed();
GC.KeepAlive(this);
}
}
}
public unsafe float[] CompoundArray {
get {
int num = default(int);
PInvokeGdiPlus.GdipGetPenCompoundCount(NativePen, &num).ThrowIfFailed();
if (num != 0) {
float[] obj = new float[num];
fixed (float* dash = obj) {
PInvokeGdiPlus.GdipGetPenCompoundArray(NativePen, dash, num).ThrowIfFailed();
GC.KeepAlive(this);
return obj;
}
}
return Array.Empty<float>();
}
set {
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
ArgumentNullException.ThrowIfNull(value, "value");
fixed (float* dash = value) {
PInvokeGdiPlus.GdipSetPenCompoundArray(NativePen, dash, value.Length).ThrowIfFailed();
GC.KeepAlive(this);
}
}
}
[NullableContext(0)]
private unsafe Pen(GpPen* nativePen)
{
SetNativePen(nativePen);
}
internal Pen(Color color, bool immutable)
: this(color)
{
_immutable = immutable;
}
public Pen(Color color)
: this(color, 1)
{
}
public unsafe Pen(Color color, float width)
{
_color = color;
GpPen* nativePen = default(GpPen*);
PInvokeGdiPlus.GdipCreatePen1((uint)color.ToArgb(), width, Unit.UnitWorld, &nativePen).ThrowIfFailed();
SetNativePen(nativePen);
if (_color.IsSystemColor)
SystemColorTracker.Add(this);
}
public Pen(Brush brush)
: this(brush, 1)
{
}
public unsafe Pen(Brush brush, float width)
{
ArgumentNullException.ThrowIfNull(brush, "brush");
GpPen* nativePen = default(GpPen*);
PInvokeGdiPlus.GdipCreatePen2(brush.NativeBrush, width, Unit.UnitWorld, &nativePen).ThrowIfFailed();
GC.KeepAlive(brush);
SetNativePen(nativePen);
}
[NullableContext(0)]
internal unsafe void SetNativePen(GpPen* nativePen)
{
_nativePen = nativePen;
}
public unsafe object Clone()
{
GpPen* nativePen = default(GpPen*);
PInvokeGdiPlus.GdipClonePen(NativePen, &nativePen).ThrowIfFailed();
GC.KeepAlive(this);
return new Pen(nativePen);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private unsafe void Dispose(bool disposing)
{
if (!disposing)
_immutable = false;
else if (_immutable) {
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
}
if (_nativePen != null) {
if (Gdip.Initialized)
PInvokeGdiPlus.GdipDeletePen(NativePen);
_nativePen = null;
}
}
~Pen()
{
Dispose(false);
}
public unsafe void SetLineCap(System.Drawing.Drawing2D.LineCap startCap, System.Drawing.Drawing2D.LineCap endCap, System.Drawing.Drawing2D.DashCap dashCap)
{
if (_immutable)
throw new ArgumentException(System.SR.Format(System.SR.CantChangeImmutableObjects, "Pen"));
PInvokeGdiPlus.GdipSetPenLineCap197819(NativePen, (global::Windows.Win32.Graphics.GdiPlus.LineCap)startCap, (global::Windows.Win32.Graphics.GdiPlus.LineCap)endCap, (global::Windows.Win32.Graphics.GdiPlus.DashCap)dashCap).ThrowIfFailed();
GC.KeepAlive(this);
}
public unsafe void ResetTransform()
{
PInvokeGdiPlus.GdipResetPenTransform(NativePen).ThrowIfFailed();
GC.KeepAlive(this);
}
public void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix)
{
MultiplyTransform(matrix, System.Drawing.Drawing2D.MatrixOrder.Prepend);
}
public unsafe void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix, System.Drawing.Drawing2D.MatrixOrder order)
{
ArgumentNullException.ThrowIfNull(matrix, "matrix");
if (matrix.NativeMatrix != null) {
PInvokeGdiPlus.GdipMultiplyPenTransform(NativePen, matrix.NativeMatrix, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order).ThrowIfFailed();
GC.KeepAlive(matrix);
GC.KeepAlive(this);
}
}
public void TranslateTransform(float dx, float dy)
{
TranslateTransform(dx, dy, System.Drawing.Drawing2D.MatrixOrder.Prepend);
}
public unsafe void TranslateTransform(float dx, float dy, System.Drawing.Drawing2D.MatrixOrder order)
{
PInvokeGdiPlus.GdipTranslatePenTransform(NativePen, dx, dy, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order).ThrowIfFailed();
GC.KeepAlive(this);
}
public void ScaleTransform(float sx, float sy)
{
ScaleTransform(sx, sy, System.Drawing.Drawing2D.MatrixOrder.Prepend);
}
public unsafe void ScaleTransform(float sx, float sy, System.Drawing.Drawing2D.MatrixOrder order)
{
PInvokeGdiPlus.GdipScalePenTransform(NativePen, sx, sy, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order).ThrowIfFailed();
GC.KeepAlive(this);
}
public void RotateTransform(float angle)
{
RotateTransform(angle, System.Drawing.Drawing2D.MatrixOrder.Prepend);
}
public unsafe void RotateTransform(float angle, System.Drawing.Drawing2D.MatrixOrder order)
{
PInvokeGdiPlus.GdipRotatePenTransform(NativePen, angle, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order).ThrowIfFailed();
GC.KeepAlive(this);
}
private unsafe void InternalSetColor(Color value)
{
PInvokeGdiPlus.GdipSetPenColor(NativePen, (uint)_color.ToArgb()).ThrowIfFailed();
GC.KeepAlive(this);
_color = value;
}
[NullableContext(0)]
private unsafe GpBrush* GetNativeBrush()
{
GpBrush* result = default(GpBrush*);
PInvokeGdiPlus.GdipGetPenBrushFill(NativePen, &result).ThrowIfFailed();
GC.KeepAlive(this);
return result;
}
private unsafe void EnsureValidDashPattern()
{
int num = default(int);
PInvokeGdiPlus.GdipGetPenDashCount(NativePen, &num);
GC.KeepAlive(this);
if (num == 0)
DashPattern = new float[1] {
1
};
}
unsafe void ISystemColorTracker.OnSystemColorChanged()
{
if (NativePen != null)
InternalSetColor(_color);
}
}
}