| File: System\Windows\TextDecorationCollectionConverter.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="string"/> to <see cref="TextDecorationCollection"/> only. /// </summary> public sealed class TextDecorationCollectionConverter : TypeConverter { /// <summary> /// Returns whether this converter can convert the object to the specified /// <paramref name="destinationType"/>, using the specified <paramref name="context"/>. /// </summary> /// <param name="context">Context information used for conversion.</param> /// <param name="destinationType">Type being evaluated for conversion.</param> /// <returns> /// <see langword="false"/> will always be returned because <see cref="TextDecorations"/> cannot be converted to any other type. /// </returns> public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { // Return false for any other target type. Don't call base.CanConvertTo() because it would be confusing // in some cases. For example, for destination typeof(string), base TypeConverter just converts the // ITypeDescriptorContext to the full name string of the given type. return destinationType == typeof(InstanceDescriptor); } /// <summary> /// Returns whether this class can convert specific <see cref="Type"/> into <see cref="TextDecorationCollection"/>. /// </summary> /// <param name="context"> ITypeDescriptorContext </param> /// <param name="sourceType">Type being evaluated for conversion.</param> /// <returns> /// <see langword="true"/> if <paramref name="sourceType"/> is <see langword="string"/>, otherwise <see langword="false"/>. /// </returns> public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return sourceType == typeof(string); } /// <summary> /// Converts <paramref name="input"/> of <see langword="string"/> type to its <see cref="TextDecorationCollection"/> representation. /// </summary> /// <param name="context">Context information used for conversion, ignored currently.</param> /// <param name="culture">The culture specifier to use, ignored currently.</param> /// <param name="input">The string to convert from.</param> /// <returns>A <see cref="TextDecorationCollection"/> representing the <see langword="string"/> specified by <paramref name="input"/>.</returns> public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object input) { if (input is null) throw GetConvertFromException(input); if (input is not string value) throw new ArgumentException(SR.Format(SR.General_BadType, "ConvertFrom"), nameof(input)); return ConvertFromString(value); } /// <summary> /// Converts <paramref name="text"/> to its <see cref="TextDecorationCollection"/> representation. /// </summary> /// <param name="text">The string to be converted into TextDecorationCollection object.</param> /// <returns>A <see cref="TextDecorationCollection"/> representing the <see cref="string"/> specified by <paramref name="text"/>.</returns> /// <remarks> /// The text parameter can be either be <see langword="null"/>; <see cref="string.Empty"/>; the string "None" /// or a combination of the predefined <see cref="TextDecorations"/> names delimited by commas (,). /// One or more blanks spaces can precede or follow each text decoration name or comma. /// There can't be duplicate TextDecoration names in the string. The operation is case-insensitive. /// </remarks> public static new TextDecorationCollection ConvertFromString(string text) { if (text is null) return null; // Flags indicating which pre-defined TextDecoration have been matched Decorations matchedDecorations = Decorations.None; // Sanitize the input ReadOnlySpan<char> decorationsSpan = text.AsSpan().Trim(); // Test for "None", which equals to empty collection and needs to be specified alone if (decorationsSpan.IsEmpty || decorationsSpan.Equals("None", StringComparison.OrdinalIgnoreCase)) return new TextDecorationCollection(); // Create new collection, save re-allocations TextDecorationCollection textDecorations = new(1 + decorationsSpan.Count(',')); foreach (Range segment in decorationsSpan.Split(',')) { ReadOnlySpan<char> decoration = decorationsSpan[segment].Trim(); if (decoration.Equals("Overline", StringComparison.OrdinalIgnoreCase) && !matchedDecorations.HasFlag(Decorations.OverlineMatch)) { textDecorations.Add(TextDecorations.OverLine[0]); matchedDecorations |= Decorations.OverlineMatch; } else if (decoration.Equals("Baseline", StringComparison.OrdinalIgnoreCase) && !matchedDecorations.HasFlag(Decorations.BaselineMatch)) { textDecorations.Add(TextDecorations.Baseline[0]); matchedDecorations |= Decorations.BaselineMatch; } else if (decoration.Equals("Underline", StringComparison.OrdinalIgnoreCase) && !matchedDecorations.HasFlag(Decorations.UnderlineMatch)) { textDecorations.Add(TextDecorations.Underline[0]); matchedDecorations |= Decorations.UnderlineMatch; } else if (decoration.Equals("Strikethrough", StringComparison.OrdinalIgnoreCase) && !matchedDecorations.HasFlag(Decorations.StrikethroughMatch)) { textDecorations.Add(TextDecorations.Strikethrough[0]); matchedDecorations |= Decorations.StrikethroughMatch; } else { throw new ArgumentException(SR.Format(SR.InvalidTextDecorationCollectionString, text)); } } return textDecorations; } /// <summary> /// Converts a <paramref name="value"/> of <see cref="TextDecorationCollection"/> to the specified <paramref name="destinationType"/>. /// </summary> /// <param name="context">Context information used for conversion.</param> /// <param name="culture">The culture specifier to use.</param> /// <param name="value">Duration value to convert from.</param> /// <param name="destinationType">Type being evaluated for conversion.</param> /// <returns><see langword="null"/> will always be returned because <see cref="TextDecorations"/> cannot be converted to any other type.</returns> public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(InstanceDescriptor) && value is IEnumerable<TextDecoration>) { ConstructorInfo ci = typeof(TextDecorationCollection).GetConstructor(new Type[] { typeof(IEnumerable<TextDecoration>) }); return new InstanceDescriptor(ci, new object[] { value }); } // Pass unhandled cases to base class (which will throw exceptions for null value or destinationType.) return base.ConvertTo(context, culture, value, destinationType); } /// <summary> /// Abstraction helper of matched decorations during conversion. /// </summary> [Flags] private enum Decorations : byte { None = 0, OverlineMatch = 1 << 0, BaselineMatch = 1 << 1, UnderlineMatch = 1 << 2, StrikethroughMatch = 1 << 3, } } }