0

How do you format a TimeSpan in XAML with a custom format? I want hour and minutes.

Based on the official documentation, the way to do this in C# seems like it should be:

interval.ToString(@"h\:mm");

I want to be able to format the TimeSpan in XAML, however, from within a binding. This solution seems viable, but I wanted to create a general converter, that I could pass a format string into. My converter is as follows:

public class TimeSpanFormatConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        string result = "";
        if (value == null)
        {
            return null;
        }

        if (parameter == null)
        {
            return value;
        }

        if (value is TimeSpan timeSpan)
        {
            try
            {
                result = timeSpan.ToString((string)parameter);
            }
            catch (Exception e)
            {
                result = "";
            }
        }

        return result;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

In theory, I could use this converter as follows:

<Page.Resources>
    <converters:TimeSpanFormatConverter x:key="TimeSpanConverter"></converters:TimeSpanFormatConverter>
</Page.Resources>

<Grid>
    <!-- Some properties omitted for brevity. -->
    <ListView>
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="models:MyModel">
                <Grid>

                    <!-- PROBLEM IS HERE -->
                    <TextBlock Text="{x:Bind Interval, Converter={StaticResource TimeSpanConverter}, ConverterParameter='h\:mm'}"></TextBlock>

                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

Note here that 'MyModel' has a property called 'Interval' that is of type 'TimeSpan'.

This does not work, however, because I need the backslash. The XAML parsing removes the backslash, thus passing in "h:mm" to the converter (which I verified through the debugger).

It also does not like two backslashes, as that throws a compiler error from the generated .g.cs file, saying "\:" is an "unrecognized escape sequence".

No variations of encoding the back slash have worked. I have tried:

h:mm
h\:mm
h\\:mm
h\\\:mm
h&#92;:mm
h&#92;\:mm
h&#92;&#92;:mm
h&#92;&#92;&#58;mm

What is the magic string of letters that need to be put into the ConverterParameter?

As an alternative, the MultiBinding solution explained here looked promising, but according to Visual Studio, MultiBinding is not supported in UWP.

  • You don't need to escape `:`. What does it say when you try putting in `h:mm`? – Furkan Kambay Jun 08 '19 at 20:57
  • BTW, I would create a custom control for this. Say, ``. Much cleaner. – Furkan Kambay Jun 08 '19 at 20:58
  • Putting in `h:mm` results in the line `result = timeSpan.ToString((string)parameter);` throwing a "Input string was not in a correct format." exception. – Michael Kintscher they-them Jun 08 '19 at 21:01
  • Why did you try `\` and not `:`? Can you try this exactly: `h:mm` -- just learned that TimeSpan doesn't have the same format method as DateTime. Looking for the correct format string now. – Furkan Kambay Jun 08 '19 at 21:05
  • Just tried that. That still results in "h:mm" going into the converter, which results in the same "Input string was not in a correct format" exception thrown that that same line. Maybe the backslash is tied to the "@" symbol with the string? Not sure. I know that `result = timespan.ToString(@"h\:mm");` gives me the result I want. I just need to figure out how to encode that in the XAML. – Michael Kintscher they-them Jun 08 '19 at 21:11
  • Yeah for that you'd need to put `\\ ` in the XAML. And that would look ugly, so maybe put `h mm` in XAML and replace spaces with `:` in code; that could work. But I think you should instead create a custom control for this. – Furkan Kambay Jun 08 '19 at 21:14
  • What I mean is the answer to this question will be ugly. If you're going to use this in multiple places, you should create a custom control. If not, I'm gonna try and find the solution now. – Furkan Kambay Jun 08 '19 at 21:24
  • I can only try in WPF and `h\\:mm` and `h\\\:m` worked for me. I can suggest creating a static list of `MyModel`s somewhere and initialize it with a few sample values, then set `ItemsSource` to it so it's design-time. Then you can just change the ConverterParameter and see the changes in the Designer immediately. – Furkan Kambay Jun 08 '19 at 21:41

1 Answers1

0

because I need the backslash. The XAML parsing removes the backslash, thus passing in "h:mm" to the converter (which I verified through the debugger).

Yep, it is correct, The ConverterParameter is object but not string, This may cause the backslash to be removed when xaml parses. I think you could create StringFormat property for your TimeSpanFormatConverter and pass the Format when Converter initialize.

public class TimeSpanFormatConverter : IValueConverter
{
    public string StringFormat { get; set; }
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        string result = "";
        if (value == null)
        {
            return null;
        }

        if (parameter == null)
        {
            return value;
        }

        if (value is TimeSpan timeSpan)
        {
            try
            {
                result = timeSpan.ToString(StringFormat);
            }
            catch (Exception e)
            {
                result = "";
            }
        }

        return result;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

Usage

<Page.Resources>
    <local:TimeSpanFormatConverter x:Key="TimeSpanConverter" StringFormat="h\:mm"/>
</Page.Resources>
<Grid>
    <TextBlock VerticalAlignment="Center" Text="{x:Bind Interval, Converter={StaticResource TimeSpanConverter},Mode=TwoWay}"></TextBlock>
</Grid>
Nico Zhu
  • 32,367
  • 2
  • 15
  • 36