File: Microsoft\Windows\Controls\Ribbon\RibbonControlLengthConverter.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\System.Windows.Controls.Ribbon\System.Windows.Controls.Ribbon_dxtfdo3u_wpftmp.csproj (System.Windows.Controls.Ribbon)
// 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.
 
 
using System.ComponentModel;
using System.Globalization;
using MS.Internal;
using System.Diagnostics;
using System.ComponentModel.Design.Serialization;
using System.Reflection;
 
#if RIBBON_IN_FRAMEWORK
namespace System.Windows.Controls.Ribbon
#else
namespace Microsoft.Windows.Controls.Ribbon
#endif
{
    /// <summary>
    ///   A class used for converting between RibbonControlLengths and strings/numbers.
    /// </summary>
    public class RibbonControlLengthConverter : TypeConverter
    {
        #region Private Fields
 
        // Note: keep this array in sync with the RibbonControlLengthUnitType enum
        private static string[] _unitStrings = { "auto", "px", "items", "*" };
 
        //  this array contains strings for unit types that are not present in the RibbonControlLengthUnitType enum
        static private string[] _pixelUnitStrings = { "in", "cm", "pt" };
        static private double[] _pixelUnitFactors = 
        { 
            96.0,             // Pixels per Inch
            96.0 / 2.54,      // Pixels per Centimeter
            96.0 / 72.0,      // Pixels per Point
        };
 
        #endregion
 
        #region Public Methods
 
        /// <summary>
        ///   Checks whether or not this class can convert from a given type.
        /// </summary>
        public override bool CanConvertFrom(ITypeDescriptorContext context, 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>
        ///   Checks whether or not this class can convert to a given type.
        /// </summary>
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return (destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string));
        }
 
        /// <summary>
        ///   Attempts to convert to a RibbonControlLength from the given object.
        /// </summary>
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            if (value != null)
            {
                string stringValue = value as string;
                if (stringValue != null)
                {
                    return FromString(stringValue, culture);
                }
                else
                {
                    //  conversion from numeric type
                    double doubleValue;
                    RibbonControlLengthUnitType type;
 
                    doubleValue = Convert.ToDouble(value, culture);
 
                    if (double.IsNaN(doubleValue))
                    {
                        //  this allows for conversion from Width / Height = "Auto" 
                        doubleValue = 1.0;
                        type = RibbonControlLengthUnitType.Auto;
                    }
                    else
                    {
                        type = RibbonControlLengthUnitType.Pixel;
                    }
 
                    return new RibbonControlLength(doubleValue, type);
                }
            }
 
            throw GetConvertFromException(value);
        }
 
        /// <summary>
        ///   Attempts to convert a RibbonControlLength instance to the given type.
        /// </summary>
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            ArgumentNullException.ThrowIfNull(destinationType);
 
            if (value != null && value is RibbonControlLength)
            {
                RibbonControlLength length = (RibbonControlLength)value;
 
                if (destinationType == typeof(string))
                {
                    return ToString(length, culture);
                }
 
                if (destinationType == typeof(InstanceDescriptor))
                {
                    ConstructorInfo ci = typeof(RibbonControlLength).GetConstructor(new Type[] { typeof(double), typeof(RibbonControlLengthUnitType) });
                    return new InstanceDescriptor(ci, new object[] { length.Value, length.RibbonControlLengthUnitType });
                }
            }
 
            throw GetConvertToException(value, destinationType);
        }
 
        #endregion
 
        #region Internal Methods
 
        /// <summary>
        ///   Converts a RibbonControlLength instance to a String given the CultureInfo.
        /// </summary>
        internal static string ToString(RibbonControlLength length, CultureInfo cultureInfo)
        {
            switch (length.RibbonControlLengthUnitType)
            {
                //  for Auto print out "Auto". value is always "1.0"
                case RibbonControlLengthUnitType.Auto:
                    return "Auto";
                
                case RibbonControlLengthUnitType.Item:
                    return Convert.ToString(length.Value, cultureInfo) + "items";
 
                //  Star has one special case when value is "1.0".
                //  in this case drop value part and print only "Star"
                case RibbonControlLengthUnitType.Star:
                    return (DoubleUtil.IsOne(length.Value) ? "*" : Convert.ToString(length.Value, cultureInfo) + "*");
 
                //  for Pixel print out the numeric value. "px" can be omitted.
                default:
                    return Convert.ToString(length.Value, cultureInfo);
            }
        }
 
        /// <summary>
        ///   Parses a RibbonControlLength from a string given the CultureInfo.
        /// </summary>
        internal static RibbonControlLength FromString(string s, CultureInfo cultureInfo)
        {
            string goodString = s.Trim().ToLowerInvariant();
 
            double value = 0.0;
            RibbonControlLengthUnitType unit = RibbonControlLengthUnitType.Pixel;
 
            int i;
            int strLen = goodString.Length;
            int strLenUnit = 0;
            double unitFactor = 1.0;
 
            //  this is where we would handle trailing whitespace on the input string.
            //  peel [unit] off the end of the string
            i = 0;
 
            if (goodString == _unitStrings[i])
            {
                strLenUnit = _unitStrings[i].Length;
                unit = (RibbonControlLengthUnitType)i;
            }
            else
            {
                for (i = 1; i < _unitStrings.Length; ++i)
                {
                    //  Note: this is NOT a culture specific comparison.
                    //  this is by design: we want the same unit string table to work across all cultures.
                    if (goodString.EndsWith(_unitStrings[i], StringComparison.Ordinal))
                    {
                        strLenUnit = _unitStrings[i].Length;
                        unit = (RibbonControlLengthUnitType)i;
                        break;
                    }
                }
            }
 
            //  we couldn't match a real unit from RibbonControlLengthUnitTypes.
            //  try again with a converter-only unit (a pixel equivalent).
            if (i >= _unitStrings.Length)
            {
                for (i = 0; i < _pixelUnitStrings.Length; ++i)
                {
                    //  Note: this is NOT a culture specific comparison.
                    //  this is by design: we want the same unit string table to work across all cultures.
                    if (goodString.EndsWith(_pixelUnitStrings[i], StringComparison.Ordinal))
                    {
                        strLenUnit = _pixelUnitStrings[i].Length;
                        unitFactor = _pixelUnitFactors[i];
                        break;
                    }
                }
            }
 
            //  this is where we would handle leading whitespace on the input string.
            //  this is also where we would handle whitespace between [value] and [unit].
            //  check if we don't have a [value].  This is acceptable for certain UnitTypes.
            if (strLen == strLenUnit && (unit == RibbonControlLengthUnitType.Auto || unit == RibbonControlLengthUnitType.Star))
            {
                value = 1;
            }
            //  we have a value to parse.
            else
            {
                Debug.Assert(unit == RibbonControlLengthUnitType.Pixel || unit == RibbonControlLengthUnitType.Item ||
                    DoubleUtil.AreClose(unitFactor, 1.0));
 
                ReadOnlySpan<char> valueString = goodString.AsSpan(0, strLen - strLenUnit);
                value = double.Parse(valueString, provider: cultureInfo) * unitFactor;
            }
 
            return new RibbonControlLength(value, unit);
        }
 
        #endregion
    }
}