114

I am trying to format a textblock which is bound to a TimeSpan property. It works if the property is of type DateTime but it fails if it is a TimeSpan. I can get it done using a converter. But I am trying to find out if there is any alternatives.

Sample Code:

public TimeSpan MyTime { get; set; }

public Window2()
{
    InitializeComponent();
    MyTime = DateTime.Now.TimeOfDay;
    DataContext = this;
}

Xaml

<TextBlock Text="{Binding MyTime,StringFormat=HH:mm}"/>

I am expecting the textblock to show only hours and mintes. But it is showing as:

19:10:46.8048860

Blacktempel
  • 3,935
  • 3
  • 29
  • 53
biju
  • 17,554
  • 10
  • 59
  • 95
  • Do you remember what version of .Net you were running on way back in 2010? I'm having a similar problem with Windows Phone XAML: http://stackoverflow.com/q/18679365/1001985 – McGarnagle Dec 05 '13 at 18:47
  • Note: The {} at the beginning of all the formats is an escape sequence, not a format specifier. It causes XAML to tolerate further brackets in the format, without requiring backslashes. – Grault Dec 22 '14 at 20:43

11 Answers11

162

The format string is intended to work on a DateTime, not a TimeSpan.

You could change your code to work with DateTime.Now instead. Your xaml is fine:

<TextBlock Text="{Binding MyTime,StringFormat=HH:mm}"/>

Update

And from .Net 4 format a TimeSpan as follows:

<TextBlock Text="{Binding MyTime,StringFormat=hh\\:mm}"/>
Tim Lloyd
  • 37,954
  • 10
  • 100
  • 130
  • Yes...But i am looking for some way that i can format a Timespan value. – biju Dec 30 '10 at 14:03
  • 1
    I am filtering my time part from a collection of dates,taking distinct,sorting..blah..its the way my logic goes – biju Dec 30 '10 at 14:07
  • @biju I have updated the sample with a format string for TimeSpan. – Tim Lloyd Dec 30 '10 at 14:08
  • 2
    I don't know if i am doing something wrong..but it is still not working for me here.VisualStudio 2008,Framework 3.5 – biju Dec 30 '10 at 14:17
  • @biju Unfortunately `TimeSpan` formatting is available from .Net 4 only. – Tim Lloyd Dec 30 '10 at 14:23
  • So it works only for .Net 4.0.So in 3.5 the only work around seems to be a converter..right ? – biju Dec 30 '10 at 14:31
  • @biju Yes, use a value converter to change the value to a DateTime. – Tim Lloyd Dec 30 '10 at 14:32
  • 1
    This is the correct answer, not the one marked above. Even for dot net 3.5 use a converter instead of the suggested answer. – MikeKulls Mar 09 '12 at 03:46
  • @MikeKulls: See my updated answer. Use D2 in the `StringFormat` to always get two digits even if minutes is from 0-9 for example. – Fredrik Hedblad Jun 01 '12 at 09:14
  • @Meleak: The reasons for my comments wasn't the single digit issue. I just think this answer is simpler and more flexible, however your solution is good for dotnet 3.5. I'm not sure if I would bother with the converter as I suggested above. – MikeKulls Jun 03 '12 at 22:12
98

In .NET 3.5 you could use a MultiBinding instead

<TextBlock>
    <TextBlock.Text>
        <MultiBinding StringFormat="{}{0}:{1}">
            <Binding Path="MyTime.Hours"/>
            <Binding Path="MyTime.Minutes"/>
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

Update
To answer the comments.

To make sure you output 2 digits even if hours or minutes is 0-9 you can use {0:00} instead of {0}. This will make sure the output for the time 12:01 is 12:01 instead of 12:1.
If you want to output 01:01 as 1:01 use StringFormat="{}{0}:{1:00}"

And Conditional formatting can be used to remove the negative sign for minutes. Instead of {1:00} we can use {1:00;00}

<TextBlock>
    <TextBlock.Text>
        <MultiBinding StringFormat="{}{0:00}:{1:00;00}">
            <Binding Path="MyTime.Hours" />
            <Binding Path="MyTime.Minutes" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>
Fredrik Hedblad
  • 83,499
  • 23
  • 264
  • 266
  • @chibacity: Thank you! I already credited your answer so I can't do it again :) But I favorited the question so I could find back to your solution if I ever needed it! Very nice as well – Fredrik Hedblad Dec 30 '10 at 15:08
  • 1
    Wouldn't this output "10:1" or "4:9" if the minutes part has only one digit? – Cygon Dec 20 '11 at 00:55
  • @Cygon: See my updated answer for how to fix this. Use {0:D2} instead of {0} – Fredrik Hedblad Jun 01 '12 at 09:13
  • This outputs "-07:-12" for a negative Timeduration of 7 hours and 12 minutes. if the duration is minus 7 hours, the output would be "-07:00". But as soon as theres a non-zero minute on a negative duration, the minus is appended to `.Hours` as well as to `.Minutes` – tzippy May 07 '15 at 07:41
  • @FredrikHedblad I want to show `TotalMinutes` instead of `Minutes` as minutes part (to show timespans longest that 1 hour). But `TotalMinutes` property is in double format, so binding using the `` produces rounded output, which is bad - I need to show truncated value instead of rounded. It is possible to achieve it without using a custom value coneverter for truncating double to int? – Dominik Palo Jan 21 '16 at 15:43
52

Just to add to the pool, I'm successfully using this binding to display a TimeSpan in a production WPF app:

Binding="{Binding Time, Mode=TwoWay, StringFormat=\{0:h\\:mm\}}"

Took some tries to get the backslashes right :)

Cygon
  • 9,444
  • 8
  • 42
  • 50
24

If you want to use StringFormat in a Label that uses the Content property, you can use ContentStringFormat to format your timespan:

<Label Content={Binding MyTimespan}" ContentStringFormat="{}{0:hh}:{0:mm}:{0:ss}"
garcipat
  • 376
  • 2
  • 9
  • 3
    What you state in your answer deserves a double highlighting: in XAML controls that use the `Content` property (i.e., not the `Text` property as, for instance, a `TextBlock`), the property `ContentStringFormat` _must_ be used. Specifying `StringFormat` as part of the binding won’t work. – Informagic Sep 08 '19 at 20:06
21

StringFormat must be in the form of a format string. In this case it would look like:

<TextBlock Text="{Binding MyTime,StringFormat=`Time values are {0:hh\\:mm}`}"/>

Note: if you want to display the total number of hours and minutes and the timespan happens to be greater than 24 hours, there's a caveat with your approach: Here's a workaround.

Peter Lillevold
  • 33,668
  • 7
  • 97
  • 131
19

For Multi bindings you need to pay attention since .NET 4.

A short overview below, tested with .NET 4.6:

Regular binding:

<TextBlock Text="{Binding Start, StringFormat='{}{0:hh\\:mm\\:ss}'}" />

Multi binding:

<TextBlock.Text>
    <MultiBinding StringFormat="{}{0:hh':'mm':'ss} -> {1:hh':'mm':'ss}">
        <Binding Path="Start" Mode="OneWay" UpdateSourceTrigger="PropertyChanged" />
        <Binding Path="End" Mode="OneWay" UpdateSourceTrigger="PropertyChanged" />
    </MultiBinding>
</TextBlock.Text>

or you could use " instead of ' in the multibinding:

<MultiBinding StringFormat='{}{0:hh":"mm":"ss} -> {1:hh":"mm":"ss}'>

Note: using StringFormat="{}{0:hh\:\:mm\:ss} -> {1:hh\:mm\:ss}" will not work on a MultiBinding, this will result in a blank result.

juFo
  • 17,849
  • 10
  • 105
  • 142
13

I'm aware that this question is old now, but I'm surprised that no one suggested this simple StringFormat which will work on a TimeSpan directly:

<TextBlock Text="{Binding MyTime, StringFormat={}{0:hh}:{0:mm}, FallbackValue=00:00}"/>
Sheridan
  • 68,826
  • 24
  • 143
  • 183
12

WPF in .NET 4 now has timespan from strings http://msdn.microsoft.com/en-us/library/ee372286.aspx

I am using the following <TextBlock FontSize="12" Text="{Binding Path=TimeLeft, StringFormat={}{0:g}}" />

Ira
  • 121
  • 1
  • 2
6

TimeSpan StringFormat with milliseconds:

<TextBlock Text="{Binding MyTime, StringFormat=\{0:hh\\:mm\\:ss\\.fff\}}"/>
Stepagrus
  • 1,189
  • 1
  • 12
  • 19
1

Mi solutions was this:

(duplicate the element 0 as many times you need)

<TextBlock Text="{Binding MyTime, StringFormat='{0:hh}:{0:mm}'}"/>
Boris Sclauzero
  • 111
  • 1
  • 5
1

There are some StringFormat options working here - but if you want to have total freedom of TimeSpan to string conversion, while remaining well within clean XAML style, there is also the option of creating a simple IValueConverter :

using System;
using System.Windows.Data;

namespace Bla
{
    [System.Windows.Data.ValueConversion(typeof(TimeSpan), typeof(string))]
    public class TimespanToSpecialStringConverter : IValueConverter
    {
        #region IValueConverter Members
        public object Convert(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
        {
            if (targetType != typeof(string))
                throw new InvalidOperationException("The target must be a string");                
            var timeSpan = (TimeSpan)value;
            string minutes = timeSpan.Minutes < 10 ? "0" + timeSpan.Minutes : ""+timeSpan.Minutes;
            string seconds = timeSpan.Seconds < 10 ? "0" + timeSpan.Seconds : "" + timeSpan.Seconds;
            return "" + timeSpan.TotalHours + ":" + minutes + ":" + seconds;
        }

        public object ConvertBack(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
        {
            if (targetType != typeof(TimeSpan))
                throw new InvalidOperationException("The target must be a TimeSpan");                
            return TimeSpan.Zero;
        }
        #endregion
    }
}

then, its possible to have, for example a StaticResource in a user control :

<UserControl.Resources>
    <local:TimespanToSpecialStringConverter x:Key="TimespanToSpecialStringConverter" />
</UserControl.Resources>

and finally apply the TimespanToSpecialStringConverter within a typical databinding :

<TextBlock Text="{Binding Path=ATimespanDependencyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource TimespanToSpecialStringConverter}}" />

Now you can programatically change the string conversion to your needs while having clean XAML :) Remember, this is only another option if you need full flexibility.

PS: Now I have read, that you were already using a Converter. So this answer does not 100% fit to the question about what 'other' alternatives are possible. However, I hope it is left here, since many people might find this a usefull way to go.

thewhiteambit
  • 1,365
  • 16
  • 31