| File: System\Windows\DurationConverter.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore) |
// 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.Design.Serialization; using System.ComponentModel; using System.Globalization; using System.Reflection; namespace System.Windows { /// <summary> /// Provides a type converter to convert from <see cref="Duration"/> to <see cref="string"/> and vice versa. /// </summary> public class DurationConverter : TypeConverter { /// <summary> /// Returns whether this class can convert specific <see cref="Type"/> into <see cref="Duration"/>. /// </summary> /// <param name="td">Context information used for conversion.</param> /// <param name="t">Type being evaluated for conversion.</param> /// <returns><see langword="true"/> if the given <paramref name="t"/> can be converted from, <see langword="false"/> otherwise.</returns> public override bool CanConvertFrom(ITypeDescriptorContext td, Type t) { return t == typeof(string); } /// <summary> /// Returns whether this class can convert specified value to <see cref="string"/>. /// </summary> /// <param name="context">Context information used for conversion.</param> /// <param name="destinationType">Type being evaluated for conversion.</param> /// <returns><see langword="true"/> if conversion to <see cref="string"/> is possible, <see langword="false"/> otherwise.</returns> public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { return destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string); } /// <summary> /// Converts <paramref name="value"/> of <see cref="string"/> type to its <see cref="Duration"/> representation. /// </summary> /// <param name="td">Context information used for conversion.</param> /// <param name="cultureInfo">The culture specifier to use.</param> /// <param name="value">The string to convert from.</param> /// <returns>A <see cref="Duration"/> representing the <see cref="string"/> specified by <paramref name="value"/>.</returns> public override object ConvertFrom(ITypeDescriptorContext td, CultureInfo cultureInfo, object value) { // In case value is not a string, we can try to check InstanceDescriptor or we just throw NotSupportedException if (value is not string stringValue) return base.ConvertFrom(td, cultureInfo, value); // Sanitize the input ReadOnlySpan<char> valueSpan = stringValue.AsSpan().Trim(); // In case it is not a pre-defined value, we will try to parse the TimeSpan and if it throws, // we will catch it as TimeSpanConverter does and rethrow with the inner exception information. if (valueSpan.Equals("Automatic", StringComparison.Ordinal)) return Duration.Automatic; else if (valueSpan.Equals("Forever", StringComparison.Ordinal)) return Duration.Forever; else return ParseTimeSpan(valueSpan, cultureInfo); } /// <summary> /// Facilities parsing from <paramref name="valueSpan"/> to <see cref="TimeSpan"/> and initializes new <see cref="Duration"/> instance. /// </summary> /// <param name="valueSpan">The string to convert from.</param> /// <param name="cultureInfo">The culture specifier to use.</param> /// <returns>A newly initialized <see cref="Duration"/> instance from the <paramref name="valueSpan"/> string.</returns> /// <remarks>This function is decoupled from the <see cref="ConvertFrom(ITypeDescriptorContext, CultureInfo, object)"/> for performance reasons.</remarks> /// <exception cref="FormatException">Thrown when parsing of <paramref name="valueSpan"/> to <see cref="TimeSpan"/> instance fails.</exception> private static Duration ParseTimeSpan(ReadOnlySpan<char> valueSpan, CultureInfo cultureInfo) { try { return new Duration(TimeSpan.Parse(valueSpan, cultureInfo)); } catch (FormatException e) { throw new FormatException($"{valueSpan} is not a valid value for {nameof(TimeSpan)}.", e); } } /// <summary> /// Converts a <paramref name="value"/> of <see cref="Duration"/> to its <see cref="string"/> representation. /// </summary> /// <param name="context">Context information used for conversion.</param> /// <param name="cultureInfo">The culture specifier to use, currently ignored during conversion.</param> /// <param name="value">Duration value to convert from.</param> /// <param name="destinationType">Type being evaluated for conversion.</param> /// <returns>A <see cref="string"/> representing the <see cref="Duration"/> specified by <paramref name="value"/>.</returns> public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cultureInfo, object value, Type destinationType) { ArgumentNullException.ThrowIfNull(destinationType); // Check that we actually support the conversion, NULL value results in string.Empty, others throw if (value is not Duration duration || (destinationType != typeof(InstanceDescriptor) && destinationType != typeof(string))) return base.ConvertTo(context, cultureInfo, value, destinationType); // The current behaviour produces culture-invariant result in case of a Duration with HasTimeSpan, // whereas ConvertFrom accepts localized input, provided the correct cultureInfo was supplied. if (destinationType == typeof(string)) return duration.ToStringInvariant(); // InstanceDescriptor reflection magic if (duration.HasTimeSpan) { MemberInfo mi = typeof(Duration).GetConstructor(new Type[] { typeof(TimeSpan) }); return new InstanceDescriptor(mi, new object[] { duration.TimeSpan }); } else if (duration == Duration.Forever) { MemberInfo mi = typeof(Duration).GetProperty("Forever"); return new InstanceDescriptor(mi, null); } else { Debug.Assert(duration == Duration.Automatic); // Only other legal duration type MemberInfo mi = typeof(Duration).GetProperty("Automatic"); return new InstanceDescriptor(mi, null); } } } }