//---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Security;
using System.Windows;
using MS.Internal;
namespace ExtendedGrid.Microsoft.Windows.Controls
{
///
/// Converts instances of various types to and from DataGridLength.
///
public class DataGridLengthConverter : TypeConverter
{
///
/// Checks whether or not this class can convert from a given type.
///
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;
}
}
///
/// Checks whether or not this class can convert to a given type.
///
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return (destinationType == typeof(string)) || (destinationType == typeof(InstanceDescriptor));
}
///
/// Attempts to convert to a DataGridLength from the given object.
///
/// The ITypeDescriptorContext for this call.
/// The CultureInfo which is respected when converting.
/// The object to convert to a DataGridLength.
/// The DataGridLength instance which was constructed.
///
/// An ArgumentNullException is thrown if the source is null.
///
///
/// An ArgumentException is thrown if the source is not null
/// and is not a valid type which can be converted to a DataGridLength.
///
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 (DoubleUtil.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);
}
///
/// Attempts to convert a DataGridLength instance to the given type.
///
/// The ITypeDescriptorContext for this call.
/// The CultureInfo which is respected when converting.
/// The DataGridLength to convert.
/// The type to which to convert the DataGridLength instance.
///
/// The object which was constructed.
///
///
/// An ArgumentNullException is thrown if the value is null.
///
///
/// 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.
///
///
/// Critical: calls InstanceDescriptor ctor which LinkDemands
/// PublicOK: can only make an InstanceDescriptor for DataGridLength, not an arbitrary class
///
[SecurityCritical]
public override object ConvertTo(
ITypeDescriptorContext context,
CultureInfo culture,
object value,
Type destinationType)
{
if (destinationType == null)
{
throw new ArgumentNullException("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);
}
///
/// Converts a DataGridLength instance to a String given the CultureInfo.
///
/// DataGridLength instance to convert.
/// The culture to use.
/// String representation of the object.
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);
}
}
///
/// Parses a DataGridLength from a string given the CultureInfo.
///
/// String to parse from.
/// Culture Info.
/// Newly created DataGridLength instance.
///
/// 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
///
private static DataGridLength ConvertFromString(string s, CultureInfo cultureInfo)
{
string goodString = s.Trim().ToLowerInvariant();
// 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 (goodString == unitString)
{
return new DataGridLength(1.0, (DataGridLengthUnitType)i);
}
}
double value = 0.0;
DataGridLengthUnitType unit = DataGridLengthUnitType.Pixel;
int strLen = goodString.Length;
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 (goodString.EndsWith(unitString, StringComparison.Ordinal))
{
strLenUnit = unitString.Length;
unit = (DataGridLengthUnitType)i;
break;
}
}
// Couldn't match a standard unit type, try a non-standard unit type.
if (strLenUnit == 0)
{
numUnitStrings = _nonStandardUnitStrings.Length;
for (int i = 0; i < numUnitStrings; i++)
{
string unitString = _nonStandardUnitStrings[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(unitString, StringComparison.Ordinal))
{
strLenUnit = unitString.Length;
unitFactor = _pixelUnitFactors[i];
break;
}
}
}
// Check if there is a numerical value to parse
if (strLen == 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.");
string valueString = goodString.Substring(0, strLen - strLenUnit);
value = Convert.ToDouble(valueString, cultureInfo) * unitFactor;
}
return new DataGridLength(value, unit);
}
private static string[] _unitStrings = { "auto", "px", "sizetocells", "sizetoheader", "*" };
private const int NumDescriptiveUnits = 3;
// This array contains strings for unit types not included in the standard set
private static string[] _nonStandardUnitStrings = { "in", "cm", "pt" };
// These are conversion factors to transform other units to pixels
private static double[] _pixelUnitFactors =
{
96.0, // Pixels per Inch
96.0 / 2.54, // Pixels per Centimeter
96.0 / 72.0, // Pixels per Point
};
}
}