File: System\Windows\ThicknessConverter.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
//
// 
//
// Description: Contains the ThicknessConverter: TypeConverter for the Thickness struct.
//
//
 
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Runtime.CompilerServices;
using System.Globalization;
using System.Reflection;
using MS.Internal;
 
#pragma warning disable 1634, 1691  // suppressing PreSharp warnings
 
namespace System.Windows
{
    /// <summary>
    /// ThicknessConverter - Converter class for converting instances of other types to and from Thickness instances.
    /// </summary> 
    public class ThicknessConverter : TypeConverter
    {
        #region Public Methods
 
        /// <summary>
        /// CanConvertFrom - Returns whether or not this class can convert from a given type.
        /// </summary>
        /// <returns>
        /// bool - True if thie converter can convert from the provided type, false if not.
        /// </returns>
        /// <param name="typeDescriptorContext"> The ITypeDescriptorContext for this call. </param>
        /// <param name="sourceType"> The Type being queried for support. </param>
        public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext, Type sourceType)
        {
            // We can only handle strings, integral and floating types
            TypeCode tc = Type.GetTypeCode(sourceType);
            switch (tc)
            {
                case TypeCode.String:
                case TypeCode.Decimal:
                case TypeCode.Single:
                case TypeCode.Double:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                    return true;
 
                default:
                    return false;
            }
        }
 
        /// <summary>
        /// CanConvertTo - Returns whether or not this class can convert to a given type.
        /// </summary>
        /// <returns>
        /// bool - True if this converter can convert to the provided type, false if not.
        /// </returns>
        /// <param name="typeDescriptorContext"> The ITypeDescriptorContext for this call. </param>
        /// <param name="destinationType"> The Type being queried for support. </param>
        public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, Type destinationType)
        {
            // We can convert to an InstanceDescriptor or to a string.
            return destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string);
        }
 
        /// <summary>
        /// ConvertFrom - Attempt to convert to a Thickness from the given object
        /// </summary>
        /// <returns>
        /// The Thickness which was constructed.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// An ArgumentNullException is thrown if the example object is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// An ArgumentException is thrown if the example object is not null and is not a valid type
        /// which can be converted to a Thickness.
        /// </exception>
        /// <param name="typeDescriptorContext"> The ITypeDescriptorContext for this call. </param>
        /// <param name="cultureInfo"> The CultureInfo which is respected when converting. </param>
        /// <param name="source"> The object to convert to a Thickness. </param>
        public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object source)
        {
            if (source is null)
                throw GetConvertFromException(source);
 
            if (source is string sourceString)
                return FromString(sourceString, cultureInfo);
            else if (source is double sourceValue)
                return new Thickness(sourceValue);
            else
                return new Thickness(Convert.ToDouble(source, cultureInfo));         
        }
 
        /// <summary>
        /// ConvertTo - Attempt to convert a Thickness to the given type
        /// </summary>
        /// <returns>
        /// The object which was constructed.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// An ArgumentNullException is thrown if the example object is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// An ArgumentException is thrown if the object is not null and is not a Thickness,
        /// or if the destinationType isn't one of the valid destination types.
        /// </exception>
        /// <param name="typeDescriptorContext"> The ITypeDescriptorContext for this call. </param>
        /// <param name="cultureInfo"> The CultureInfo which is respected when converting. </param>
        /// <param name="value"> The Thickness to convert. </param>
        /// <param name="destinationType">The type to which to convert the Thickness instance. </param>
        public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object value, Type destinationType)
        {
            ArgumentNullException.ThrowIfNull(value);
            ArgumentNullException.ThrowIfNull(destinationType);
 
            if (value is not Thickness thickness)
                throw new ArgumentException(SR.Format(SR.UnexpectedParameterType, value.GetType(), typeof(Thickness)), nameof(value));
 
            if (destinationType == typeof(string))
                return ToString(thickness, cultureInfo);
            else if (destinationType == typeof(InstanceDescriptor))
            {
                ConstructorInfo ci = typeof(Thickness).GetConstructor(new Type[] { typeof(double), typeof(double), typeof(double), typeof(double) });
                return new InstanceDescriptor(ci, new object[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom });
            }
 
            throw new ArgumentException(SR.Format(SR.CannotConvertType, typeof(Thickness), destinationType.FullName));
        }
 
 
        #endregion Public Methods
 
        //-------------------------------------------------------------------
        //
        //  Internal Methods
        //
        //-------------------------------------------------------------------
 
        #region Internal Methods
 
        /// <summary>
        /// Converts <paramref name="th"/> to its string representation using the specified <paramref name="cultureInfo"/>.
        /// </summary>
        /// <param name="th">The <see cref="Thickness"/> to convert to string.</param>
        /// <param name="cultureInfo">Culture to use when formatting doubles and choosing separator.</param>
        /// <returns>The formatted <paramref name="th"/> as string using the specified <paramref name="cultureInfo"/>.</returns>
        internal static string ToString(Thickness th, CultureInfo cultureInfo)
        {
            char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo);
 
            // Initial capacity [64] is an estimate based on a sum of:
            // 48 = 4x double (twelve digits is generous for the range of values likely)
            //  8 = 4x Unit Type string (approx two characters)
            //  3 = 3x separator characters
            //  1 = 1x scratch space for alignment
 
            DefaultInterpolatedStringHandler handler = new(0, 7, cultureInfo, stackalloc char[64]);
            LengthConverter.FormatLengthAsString(th.Left, ref handler);
            handler.AppendFormatted(listSeparator);
 
            LengthConverter.FormatLengthAsString(th.Top, ref handler);
            handler.AppendFormatted(listSeparator);
 
            LengthConverter.FormatLengthAsString(th.Right, ref handler);
            handler.AppendFormatted(listSeparator);
 
            LengthConverter.FormatLengthAsString(th.Bottom, ref handler);
 
            return handler.ToStringAndClear();
        }
 
        /// <summary>
        /// Constructs a <see cref="Thickness"/> struct out of string representation supplied by <paramref name="s"/> and the specified <paramref name="cultureInfo"/>.
        /// </summary>
        /// <param name="s">The string representation of a <see cref="Thickness"/> struct.</param>
        /// <param name="cultureInfo">The <see cref="CultureInfo"/> which was used to format this string.</param>
        /// <returns>A new instance of <see cref="Thickness"/> struct representing the data contained in <paramref name="s"/>.</returns>
        /// <exception cref="FormatException">Thrown when <paramref name="s"/> contains invalid string representation.</exception>
        internal static Thickness FromString(string s, CultureInfo cultureInfo)
        {
            TokenizerHelper th = new(s, cultureInfo);
            Span<double> lengths = stackalloc double[4];
            int i = 0;
 
            // Peel off each double in the delimited list.
            while (th.NextToken())
            {
                if (i >= 4) // In case we've got more than 4 doubles, we throw
                    throw new FormatException(SR.Format(SR.InvalidStringThickness, s));
 
                lengths[i] = LengthConverter.FromString(th.GetCurrentToken(), cultureInfo);
                i++;
            }
 
            // We have a reasonable interpretation for one value (all four edges),
            // two values (horizontal, vertical),
            // and four values (left, top, right, bottom).
            return i switch
            {
                1 => new Thickness(lengths[0]),
                2 => new Thickness(lengths[0], lengths[1], lengths[0], lengths[1]),
                4 => new Thickness(lengths[0], lengths[1], lengths[2], lengths[3]),
                _ => throw new FormatException(SR.Format(SR.InvalidStringThickness, s)),
            };
        }
 
    #endregion
 
    }
}