Como formatar TimeSpan em XAML

92

Estou tentando formatar um bloco de texto vinculado a uma TimeSpanpropriedade. Funciona se a propriedade for do tipo, DateTimemas falhará se for a TimeSpan. Posso fazer isso usando um conversor. Mas estou tentando descobrir se há alternativas.

Código de amostra:

public TimeSpan MyTime { get; set; }

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

Xaml

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

Estou esperando que o bloco de texto mostre apenas horas e minutos. Mas está sendo mostrado como:

19: 10: 46,8048860

biju
fonte
Você se lembra de qual versão do .Net você estava executando em 2010? Estou tendo um problema semelhante com o Windows Phone XAML: stackoverflow.com/q/18679365/1001985
McGarnagle
Nota: O {} no início de todos os formatos é uma sequência de escape, não um especificador de formato. Isso faz com que o XAML tolere mais colchetes no formato, sem exigir barras invertidas.
Grault

Respostas:

88

No .NET 3.5, você pode usar um MultiBinding em vez

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

Atualizar
Para responder aos comentários.

Para garantir a saída de 2 dígitos, mesmo que as horas ou minutos sejam de 0 a 9, você pode usar {0:00} em vez de {0}. Isso garantirá que a saída para o horário 12:01 seja 12:01 em vez de 12: 1.
Se você deseja produzir 01:01 como 01:01, useStringFormat="{}{0}:{1:00}"

E a formatação condicional pode ser usada para remover o sinal negativo de minutos. Em vez de {1:00}, podemos usar {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
fonte
@chibacity: Obrigado! Já creditei sua resposta, então não posso fazer de novo :) Mas eu adicionei a pergunta aos favoritos para poder encontrar sua solução se eu precisar! Muito bom também
Fredrik Hedblad
1
Isso não geraria "10: 1" ou "4: 9" se a parte dos minutos tivesse apenas um dígito?
Cygon
@Cygon: Veja minha resposta atualizada para saber como consertar isso. Use {0: D2} em vez de {0}
Fredrik Hedblad 01 de
Isso gera "-07: -12" para uma duração negativa de 7 horas e 12 minutos. se a duração for menos 7 horas, a saída será "-07: 00". Mas assim que houver um minuto diferente de zero em uma duração negativa, o menos é anexado a .Hours, bem como a.Minutes
tzippy
@FredrikHedblad Desejo mostrar em TotalMinutesvez de Minutescomo parte de minutos (para mostrar intervalos de tempo mais longos que 1 hora). Mas a TotalMinutespropriedade está em formato duplo, então a vinculação usando o <Binding Path="MyTime.TotalMinutes" />produz uma saída arredondada, o que é ruim - preciso mostrar o valor truncado em vez de arredondado. É possível fazer isso sem usar um conversor de valor personalizado para truncar double para int?
Dominik Palo
139

A string de formato se destina a funcionar em um DateTime, não um TimeSpan.

Você pode alterar seu código para trabalhar DateTime.Now. Seu xaml está bom:

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

Atualizar

E do formato .Net 4 a da TimeSpanseguinte forma:

<TextBlock Text="{Binding MyTime,StringFormat=hh\\:mm}"/>
Tim Lloyd
fonte
Sim ... Mas estou procurando uma maneira de formatar um valor de Timespan.
biju
1
Estou filtrando minha parte do tempo de uma coleção de datas, separando, classificando ... blah ... é do jeito que minha lógica vai
biju
@biju Eu atualizei o exemplo com uma string de formato para TimeSpan.
Tim Lloyd
2
Não sei se estou fazendo algo errado ... mas ainda não está funcionando para mim aqui.VisualStudio 2008, Framework 3.5
biju
1
Esta é a resposta correta, não aquela marcada acima. Mesmo para o dot net 3.5, use um conversor em vez da resposta sugerida.
MikeKulls,
44

Apenas para adicionar ao pool, estou usando com sucesso esta ligação para exibir um TimeSpan em um aplicativo WPF de produção:

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

Fiz algumas tentativas para acertar as barras invertidas :)

Cygon
fonte
5
Se você quiser ir para frações de segundo, você precisa escapar da vírgula também: {0: hh \\: mm \\: ss \\.
Ffff

StringFormat = \ {0: hh \\: mm \\: ss \\. Fff \}
Stepan Ivanenko,
Meu grande erro o tempo todo foi usar H como string de formato DateTime. Mas obviamente não há formato
24/12
22

StringFormatdeve estar na forma de uma string de formato. Neste caso, seria semelhante a:

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

Observação: se você deseja exibir o número total de horas e minutos e o intervalo de tempo passa a ser maior que 24 horas, há uma ressalva com sua abordagem: Aqui está uma solução alternativa .

Peter Lillevold
fonte
@biju - sintaxe atualizada. Faltavam aspas simples em torno do valor StringFormat.
Peter Lillevold
E sim, como mostra o exemplo de @chibacity, um \ duplo é necessário para escapar de:
Peter Lillevold
1
e, de fato, isso só funciona no .Net 4.0! A string de formato é totalmente ignorada em .Net 3.5.
Peter Lillevold
2
Observe também que isso funciona com um TextBlock, mas NÃO (!) Com um Label.
Shackles
2
@Shackles StringFormat é usado apenas se o destino de Binding for uma propriedade do tipo String. Label.Content é Object, portanto, é ignorado. TextBlock.Text é uma string, então é usado.
15ee8f99-57ff-4f92-890c-b56153
16

Para Multi bindings, você precisa prestar atenção desde .NET 4.

Uma breve visão geral abaixo, testada com .NET 4.6:

Encadernação regular:

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

ou você pode usar "em vez de ' no multibinding:

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

Nota: usando StringFormat = "{} {0: hh \: \: mm \: ss} -> {1: hh \: mm \: ss}" será não trabalho em um MultiBinding, isso irá resultar em um resultado em branco.

JuFo
fonte
13

Estou ciente de que esta questão é antiga agora, mas estou surpreso que ninguém sugeriu este simples StringFormatque funcionará TimeSpandiretamente em:

<TextBlock Text="{Binding MyTime, StringFormat={}{0:hh}:{0:mm}, FallbackValue=00:00}"/>
Sheridan
fonte
2
Muita confusão em torno disso. Parece que a sintaxe mudou com o .NET 4.
McGarnagle
12

O WPF no .NET 4 agora tem intervalo de tempo das strings http://msdn.microsoft.com/en-us/library/ee372286.aspx

Estou usando o seguinte <TextBlock FontSize="12" Text="{Binding Path=TimeLeft, StringFormat={}{0:g}}" />

Ira
fonte
1
Na verdade, funciona em .NET 4-4.5. Nenhuma outra solução neste tópico o faz.
erodewald
Talvez isso fosse verdade em fevereiro de 2012, mas não é mais.
Sheridan
8

Se quiser usar StringFormat em um rótulo que usa a propriedade Content, você pode usar ContentStringFormat para formatar seu intervalo de tempo:

<Label Content={Binding MyTimespan}" ContentStringFormat="{}{0:hh}:{0:mm}:{0:ss}"
mcflux101
fonte
O que você afirma em sua resposta merece um destaque duplo: em controles XAML que usam a Contentpropriedade (ou seja, não a Textpropriedade como, por exemplo, a TextBlock), a propriedade ContentStringFormat deve ser usada. Especificar StringFormatcomo parte da vinculação não funcionará.
Informagic
2

TimeSpan StringFormat com milissegundos:

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