如何在 XAML 中格式化 TimeSpan

我试图格式化一个绑定到 TimeSpan属性的文本块。如果属性的类型是 DateTime,那么它可以工作,但是如果属性的类型是 TimeSpan,那么它就会失败。我可以用转换器完成。但我想知道还有没有其他选择。

示例代码:

public TimeSpan MyTime { get; set; }


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

赛姆

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

我希望文本块只显示小时和分钟,但它显示如下:

19:10:46.8048860

95657 次浏览

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.

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}"/>

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>

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 :)

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}}" />

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}"/>

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.

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}"

TimeSpan StringFormat with milliseconds:

<TextBlock Text="{Binding MyTime, StringFormat=\{0:hh\\:mm\\:ss\\.fff\}}"/>

Mi solutions was this:

(duplicate the element 0 as many times you need)

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

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.