| File: System\Windows\Controls\DataGridLengthConverter.cs | Web Access |
| Project: src\wpf\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. using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Globalization; using System.Reflection; using MS.Internal; namespace System.Windows.Controls { /// <summary> /// Converts instances of various types to and from DataGridLength. /// </summary> public class DataGridLengthConverter : TypeConverter { /// <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: case TypeCode.Byte: 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(string)) || (destinationType == typeof(InstanceDescriptor)); } /// <summary> /// Attempts to convert to a DataGridLength from the given object. /// </summary> /// <param name="context">The ITypeDescriptorContext for this call.</param> /// <param name="culture">The CultureInfo which is respected when converting.</param> /// <param name="value">The object to convert to a DataGridLength.</param> /// <returns>The DataGridLength instance which was constructed.</returns> /// <exception cref="ArgumentNullException"> /// An ArgumentNullException is thrown if the source is null. /// </exception> /// <exception cref="ArgumentException"> /// An ArgumentException is thrown if the source is not null /// and is not a valid type which can be converted to a DataGridLength. /// </exception> public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value != null) { string stringSource = value as string; if (stringSource != null) { // Convert from string return ConvertFromString(stringSource, culture); } else { // Conversion from numeric type DataGridLengthUnitType type; double doubleValue = Convert.ToDouble(value, culture); if (double.IsNaN(doubleValue)) { // This allows for conversion from Width / Height = "Auto" doubleValue = 1.0; type = DataGridLengthUnitType.Auto; } else { type = DataGridLengthUnitType.Pixel; } if (!Double.IsInfinity(doubleValue)) { return new DataGridLength(doubleValue, type); } } } // The default exception to throw in ConvertFrom throw GetConvertFromException(value); } /// <summary> /// Attempts to convert a DataGridLength instance to the given type. /// </summary> /// <param name="context">The ITypeDescriptorContext for this call.</param> /// <param name="culture">The CultureInfo which is respected when converting.</param> /// <param name="value">The DataGridLength to convert.</param> /// <param name="destinationType">The type to which to convert the DataGridLength instance.</param> /// <returns> /// The object which was constructed. /// </returns> /// <exception cref="ArgumentNullException"> /// An ArgumentNullException is thrown if the value is null. /// </exception> /// <exception cref="ArgumentException"> /// An ArgumentException is thrown if the value is not null and is not a DataGridLength, /// or if the destinationType isn't one of the valid destination types. /// </exception> public override object ConvertTo( ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { ArgumentNullException.ThrowIfNull(destinationType); if ((value != null) && (value is DataGridLength)) { DataGridLength length = (DataGridLength)value; if (destinationType == typeof(string)) { return ConvertToString(length, culture); } if (destinationType == typeof(InstanceDescriptor)) { ConstructorInfo ci = typeof(DataGridLength).GetConstructor(new Type[] { typeof(double), typeof(DataGridLengthUnitType) }); return new InstanceDescriptor(ci, new object[] { length.Value, length.UnitType }); } } // The default exception to throw from ConvertTo throw GetConvertToException(value, destinationType); } /// <summary> /// Converts a DataGridLength instance to a String given the CultureInfo. /// </summary> /// <param name="gl">DataGridLength instance to convert.</param> /// <param name="cultureInfo">The culture to use.</param> /// <returns>String representation of the object.</returns> internal static string ConvertToString(DataGridLength length, CultureInfo cultureInfo) { switch (length.UnitType) { case DataGridLengthUnitType.Auto: case DataGridLengthUnitType.SizeToCells: case DataGridLengthUnitType.SizeToHeader: return length.UnitType.ToString(); // Star has one special case when value is "1.0" in which the value can be dropped. case DataGridLengthUnitType.Star: return DoubleUtil.IsOne(length.Value) ? "*" : $"{Convert.ToString(length.Value, cultureInfo)}*"; // Print out the numeric value. "px" can be omitted. default: return Convert.ToString(length.Value, cultureInfo); } } /// <summary> /// Parses a DataGridLength from a string given the CultureInfo. /// </summary> /// <param name="s">String to parse from.</param> /// <param name="cultureInfo">Culture Info.</param> /// <returns>Newly created DataGridLength instance.</returns> /// <remarks> /// Formats: /// "[value][unit]" /// [value] is a double /// [unit] is a string in DataGridLength._unitTypes connected to a DataGridLengthUnitType /// "[value]" /// As above, but the DataGridLengthUnitType is assumed to be DataGridLengthUnitType.Pixel /// "[unit]" /// As above, but the value is assumed to be 1.0 /// This is only acceptable for a subset of DataGridLengthUnitType: Auto /// </remarks> private static DataGridLength ConvertFromString(string s, CultureInfo cultureInfo) { ReadOnlySpan<char> valueSpan = s.AsSpan().Trim(); // Check if the string matches any of the descriptive unit types. // In these cases, there is no need to parse a value. for (int i = 0; i < NumDescriptiveUnits; i++) { string unitString = _unitStrings[i]; if (valueSpan.Equals(unitString, StringComparison.OrdinalIgnoreCase)) { return new DataGridLength(1.0, (DataGridLengthUnitType)i); } } double value = 0.0; DataGridLengthUnitType unit = DataGridLengthUnitType.Pixel; int strLenUnit = 0; double unitFactor = 1.0; // Check if the string contains a non-descriptive unit at the end. int numUnitStrings = _unitStrings.Length; for (int i = NumDescriptiveUnits; i < numUnitStrings; i++) { string unitString = _unitStrings[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 (valueSpan.EndsWith(unitString, StringComparison.OrdinalIgnoreCase)) { strLenUnit = unitString.Length; unit = (DataGridLengthUnitType)i; break; } } // Couldn't match a standard unit type, try a non-standard unit type. if (strLenUnit == 0) { PixelUnit pixelUnit; if (PixelUnit.TryParsePixelPerInch(valueSpan, out pixelUnit) || PixelUnit.TryParsePixelPerCentimeter(valueSpan, out pixelUnit) || PixelUnit.TryParsePixelPerPoint(valueSpan, out pixelUnit)) { strLenUnit = pixelUnit.Name.Length; unitFactor = pixelUnit.Factor; } } // Check if there is a numerical value to parse if (valueSpan.Length == strLenUnit) { // There is no numerical value to parse if (unit == DataGridLengthUnitType.Star) { // Star's value defaults to 1. Anyone else would be 0. value = 1.0; } } else { // Parse a numerical value Debug.Assert( (unit == DataGridLengthUnitType.Pixel) || DoubleUtil.AreClose(unitFactor, 1.0), "unitFactor should not be other than 1.0 unless the unit type is Pixel."); ReadOnlySpan<char> valueString = valueSpan.Slice(0, valueSpan.Length - strLenUnit); value = double.Parse(valueString, provider: cultureInfo) * unitFactor; } return new DataGridLength(value, unit); } private static string[] _unitStrings = { "auto", "px", "sizetocells", "sizetoheader", "*" }; private const int NumDescriptiveUnits = 3; } }