George Thomas
George Thomas

Reputation: 4586

String format and Formatted string

I am trying to format a string lets say

"Hello george, how are you?"

I just want "george" in red color. Is there any way I can use String.Format and FormattedString side by side?

I tried using:

var text = new FormattedString();
text.Spans.Add(new Span { 
    Text = Localize.GetString("irs", String.Empty), 
    ForegroundColor = Colors.RedColor 
});
label.FormattedText = String.Format(
    Localize.GetString("instructions", String.Empty), 
    text
);

However this does not work. Is there any proper way to actually do this. I want localization so I don't want to split the text into multiple localization strings.

Upvotes: 1

Views: 2109

Answers (1)

Sharada
Sharada

Reputation: 13601

You can use an intercept provider in string.Format.

For illustrative purposes, I choose XAML format as output, and then convert it into FormattedString (but more concise formats like JSON can also be used).

First, let's implement the intercept provider - which will convert your string.Format's output into Span(s):

public class InterceptProvider : IFormatProvider, ICustomFormatter
{
    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(ICustomFormatter))
            return this;
        else
            return null;
    }

    public string Format(String format, Object obj, IFormatProvider provider)
    {
        string spanText;

        // Use default for all other formatting.
        if (obj is IFormattable)
            spanText = ((IFormattable)obj).ToString(format, System.Globalization.CultureInfo.CurrentCulture);
        else
            spanText = obj.ToString();

        return $"</Span><Span ForegroundColor=\"Red\">{spanText}</Span><Span>";
    }
}

Add an extension method to integrate the interceptor with string.Format.

public static class FormatExtensions
{
    static ColorTypeConverter _typeConverter = new ColorTypeConverter();
    static InterceptProvider _interceptor = new InterceptProvider();

    public static string InterceptFormat(this string sourceStr, params object[] args)
    {
        return $"<FormattedString><Span>{string.Format(_interceptor, sourceStr, args)}</Span></FormattedString>";
    }

And, finally an helper method that converts XAML to FormattedString object.

    public static FormattedString ToFormattedString(this string xamlStr)
    {
        var toReturn = new FormattedString();

        if (string.IsNullOrWhiteSpace(xamlStr))
            return toReturn;

        Span span = null;
        using(var strReader = new StringReader(xamlStr))
        {
            using(var xmlReader = XmlReader.Create(strReader))
            {
                while (xmlReader.Read())
                {

                    if (xmlReader.IsStartElement())
                    {
                        switch (xmlReader.Name)
                        {
                            case "Span":
                                span = new Span();

                                while (xmlReader.MoveToNextAttribute())
                                {
                                    switch (xmlReader.Name)
                                    {
                                        case "ForegroundColor":
                                            var color = xmlReader.Value;
                                            if (!string.IsNullOrEmpty(color))
                                                span.ForegroundColor = (Color)_typeConverter.ConvertFromInvariantString(color);
                                            break;
                                    }
                                }

                                if (xmlReader.IsStartElement() || xmlReader.MoveToContent() == XmlNodeType.Element)
                                {
                                    span.Text = xmlReader.ReadString();
                                    toReturn.Spans.Add(span ?? new Span());
                                }    

                                break;
                        }
                    }
                }
            }    
        }

        return toReturn;
    }

USAGE:

label.FormattedText = Localize.GetString("instructions", String.Empty)
                          .InterceptFormat(text).ToFormattedString();

Or,

lbl.FormattedText = "{0} It is now {1:d} at {1:t}"
                        .InterceptFormat("Morning!", DateTime.Now)
                        .ToFormattedString();

enter image description here

Upvotes: 2

Related Questions