using System; using System.Collections; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; namespace Vanara.PInvoke { /// The POINT structure defines the x- and y-coordinates of a point. // https://docs.microsoft.com/en-us/windows/win32/api/windef/ns-windef-point [PInvokeData("windef.h", MSDNShortId = "NS:windef.tagPOINT")] [TypeConverter(typeof(POINTConverter))] [StructLayout(LayoutKind.Sequential), Serializable] [ComVisible(true)] public struct POINT : IEquatable { /// Represents a POINT that has X and Y values set to zero. public static readonly POINT Empty = new(); /// Specifies the x-coordinate of the point. public int X; /// Specifies the y-coordinate of the point. public int Y; /// Initializes a new instance of the struct with the specified coordinates. /// The horizontal position of the point. /// The vertical position of the point. public POINT(int X, int Y) { this.X = X; this.Y = Y; } /// Initializes a new instance of the struct from a . /// A that specifies the coordinates for the new . public POINT(SIZE sz) { X = sz.cx; Y = sz.cy; } /// Initializes a new instance of the struct using coordinates specified by an integer value. /// A 32-bit integer that specifies the coordinates for the new . public POINT(int dw) { unchecked { X = (short)Macros.LOWORD((uint)dw); Y = (short)Macros.HIWORD((uint)dw); } } /// Gets a value indicating whether this is empty. /// if both X and Y are 0; otherwise, . [Browsable(false)] public bool IsEmpty => X == 0 && Y == 0; /// Adds the specified to the specified . /// The to add. /// The to add. /// The that is the result of the addition operation. public static POINT Add(POINT pt, SIZE sz) => new(pt.X + sz.cx, pt.Y + sz.cy); /// Performs an explicit conversion from to . /// The to be converted. /// The that results from the conversion. public static explicit operator SIZE(POINT p) => new(p.X, p.Y); /// Performs an implicit conversion from to . /// The . /// The result of the conversion. public static implicit operator Point(POINT p) => new(p.X, p.Y); /// Performs an implicit conversion from to . /// The . /// The result of the conversion. public static implicit operator POINT(Point p) => new(p.X, p.Y); /// Translates a by the negative of a given . /// The to translate. /// A that specifies the pair of numbers to subtract from the coordinates of pt. /// A structure that is translated by the negative of a given structure. public static POINT operator -(POINT pt, SIZE sz) => Subtract(pt, sz); /// /// Compares two objects. The result specifies whether the values of the X or Y properties of the two objects are unequal. /// /// A to compare. /// A to compare. /// /// if the values of either the X properties or the Y properties of left and right differ; otherwise, . /// public static bool operator !=(POINT left, POINT right) => !(left == right); /// Translates a by a given . /// The to translate. /// A that specifies the pair of numbers to add to the coordinates of pt. /// The translated . public static POINT operator +(POINT pt, SIZE sz) => Add(pt, sz); /// /// Compares two objects. The result specifies whether the values of the X and Y properties of the two objects are equal. /// /// A to compare. /// A to compare. /// if the X and Y values of left and right are equal; otherwise, . public static bool operator ==(POINT left, POINT right) => left.X == right.X && left.Y == right.Y; /// Returns the result of subtracting specified from the specified . /// The to be subtracted from. /// The to subtract from the . /// The that is the result of the subtraction operation. public static POINT Subtract(POINT pt, SIZE sz) => new(pt.X - sz.cx, pt.Y - sz.cy); /// Specifies whether this point instance contains the same coordinates as another point. /// The point to test for equality. /// if has the same coordinates as this point instance. public bool Equals(POINT other) => other.X == X && other.Y == Y; /// Specifies whether this point instance contains the same coordinates as the specified object. /// The to test for equality. /// /// if is a and has the same coordinates as this point instance. /// public override bool Equals(object obj) => obj switch { POINT p => Equals(p), Point p => Equals((POINT)p), _ => false }; /// Returns a hash code for this instance. /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() => unchecked(X ^ Y); /// Translates the by the specified amount. /// The amount to offset the x-coordinate. /// The amount to offset the y-coordinate. public void Offset(int dx, int dy) { X += dx; Y += dy; } /// Translates the by the specified . /// The used offset this . public void Offset(POINT p) => Offset(p.X, p.Y); /// Converts this to a human-readable string. /// A that represents this . public override string ToString() => $"{{X={X},Y={Y}}}"; } internal class POINTConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string strValue) { string text = strValue.Trim(); if (text.Length == 0) return null; culture ??= CultureInfo.CurrentCulture; string[] tokens = text.Split(culture.TextInfo.ListSeparator[0]); if (tokens.Length == 2) { TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); int[] values = Array.ConvertAll(tokens, i => (int)intConverter.ConvertFromString(context, culture, i)); return new POINT(values[0], values[1]); } } return base.ConvertFrom(context, culture, value); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType is null) { throw new ArgumentNullException(nameof(destinationType)); } if (value is POINT pt) { if (destinationType == typeof(string)) { culture ??= CultureInfo.CurrentCulture; string sep = culture.TextInfo.ListSeparator + " "; TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); return string.Concat(intConverter.ConvertToString(context, culture, pt.X), sep, intConverter.ConvertToString(context, culture, pt.Y)); } if (destinationType == typeof(InstanceDescriptor)) { ConstructorInfo ctor = typeof(POINT).GetConstructor(new[] { typeof(int), typeof(int) }); if (ctor is not null) { return new InstanceDescriptor(ctor, new[] { pt.X, pt.Y }); } } } return base.ConvertTo(context, culture, value, destinationType); } public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { if (propertyValues is null) { throw new ArgumentNullException(nameof(propertyValues)); } object x = propertyValues["X"]; object y = propertyValues["Y"]; return x is int ix && y is int iy ? new POINT(ix, iy) : throw new ArgumentException("Invalid property values.", nameof(propertyValues)); } public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) => true; #if NET6_0_OR_GREATER [RequiresUnreferencedCode(null)] #endif public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(POINT), attributes); return props.Sort(new[] { "X", "Y" }); } public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; } }