Como formatar o número de casas decimais no wpf usando estilo / modelo?

95

Estou escrevendo um programa WPF e estou tentando descobrir uma maneira de formatar dados em um TextBox por meio de algum método repetível como um estilo ou modelo. Eu tenho muitos TextBoxes (95 para ser exato) e cada um está vinculado a seus próprios dados numéricos, cada um com sua própria resolução definida. Por exemplo, se os dados são 99,123 com uma resolução de 2, ele deve exibir 99,12. Da mesma forma, um valor de dados de 99 e resolução 3 deve ser exibido como 99.000 (não 99). Existe uma maneira de fazer isso?

Edit: Devo esclarecer, há 95 TextBoxes na tela atual em que estou trabalhando, mas quero que cada TextBox das várias telas do meu programa exiba o número correto de casas decimais. Agora que penso sobre isso, alguns deles são TextBoxes (como a tela em que estou trabalhando agora) e alguns são DataGrids ou ListViews, mas se eu conseguir descobrir como fazê-los funcionar para TextBoxes, tenho certeza que posso descobrir para os outros controles também.

Não há muito código para compartilhar neste caso, mas tentarei deixar isso mais claro:

Eu tenho um modelo de visualização que contém as seguintes propriedades (vb.net):

    Public ReadOnly Property Resolution As Integer
        Get
            Return _signal.DisplayResolution
        End Get
    End Property

    Public ReadOnly Property Value As Single
        Get
            Return Math.Round(_signal.DisplayValue, Resolution)
        End Get
    End Property

e no XAML eu tenho:

<UserControl.Resources>
    <vm:SignalViewModel x:Key="Signal" SignalPath="SomeSignal"/>
</UserControl.Resources>
<TextBox Grid.Column="3" IsEnabled="False" Text="{Binding Path=Value, Source={StaticResource Signal}, Mode=OneWay}" />

EDIT2 (minha solução): Acontece que, depois de me afastar do computador por um tempo, voltei para encontrar uma resposta simples que estava me encarando. Formate os dados no modelo de visualização!

    Public ReadOnly Property Value As String
        Get
            Return (Strings.FormatNumber(Math.Round(_signal.DisplayValue, _signal.DisplayResolution), _signal.DisplayResolution))
        End Get
    End Property
AXG1010
fonte
1
usar um IValueConverter? Passe o valor real e a resolução para o conversor e deixe-o fazer o arredondamento para você dentro dele mesmo. É difícil sugerir um StringFormatsem saber exatamente como esses 95 TextBoxsão gerados.
Viv
Publique o código atual e XAML. Caso contrário, são apenas especulações e adivinhações inúteis.
Federico Berasategui
Eu adicionei mais algumas informações à pergunta que espero torná-la mais clara.
AXG1010 de

Respostas:

202

Você deve usar o StringFormatno Binding. Você pode usar formatos de string padrão ou formatos de string personalizados :

<TextBox Text="{Binding Value, StringFormat=N2}" />
<TextBox Text="{Binding Value, StringFormat={}{0:#,#.00}}" />

Observe que StringFormatsó funciona quando a propriedade target é do tipo string. Se você está tentando definir algo como uma Contentpropriedade ( typeof(object)), você precisará usar um personalizado StringFormatConverter( como aqui ) e passar sua string de formato como oConverterParameter .

Editar para pergunta atualizada

Portanto, se você ViewModeldefine a precisão, recomendo fazer isso como um MultiBindinge criar o seu próprio IMultiValueConverter. Isso é muito chato na prática, ir de uma ligação simples para uma que precisa ser expandida para um MultiBinding, mas se a precisão não for conhecida no tempo de compilação, isso é praticamente tudo que você pode fazer. Você IMultiValueConverterprecisaria pegar o valor e a precisão e gerar a string formatada. Você seria capaz de fazer isso usando String.Format.

No entanto, para coisas como um ContentControl, você pode fazer isso muito mais facilmente com um Style:

<Style TargetType="{x:Type ContentControl}">
    <Setter Property="ContentStringFormat" 
            Value="{Binding Resolution, StringFormat=N{0}}" />
</Style>

Qualquer controle que exponha um ContentStringFormatpode ser usado dessa forma. Infelizmente, TextBoxnão tem nada parecido.

Abe Heidebrecht
fonte
6
O exemplo com StringFormat definido como #,#.00não compila - a vírgula é interpretada como um delimitador para atributos na Bindingextensão de marcação.
Gigi
@Gigi, você está certo, mas você pode facilmente usá-lo assim: StringFormat={}{0:#,#.00}. Vou atualizar a resposta para funcionar corretamente.
Abe Heidebrecht
O 'StringFormat = N {0}' funciona muito bem. Para uma precisão de 2, gostaria de mostrar dois decimais, exceto '10,00 ', caso em que desejo' 10 'exibido. Existe uma maneira de fazer isso ao vincular à precisão? Parece que vou ter que usar um conversor.
Gordon Slysz
Não acho que haja uma maneira de alterar os decimais apresentados usando formatos de string .NET, então provavelmente é melhor escrever um conversor.
Abe Heidebrecht
Você pode explicar por que usar dois especificadores 0: #, #. 00, apenas um deles não será suficiente?
Lei Yang de
8

A resposta aceita não mostra 0 no lugar inteiro ao fornecer uma entrada como 0,299. Ele mostra 0,3 na IU do WPF. Então, minha sugestão de usar o seguinte formato de string

<TextBox Text="{Binding Value,  StringFormat={}{0:#,0.0}}" 
Manish Dubey
fonte
Olá, a sua solução é boa, mas prefiro usar a palavra-chave N1 (2,3 ...) porque evita erros de digitação e pelo menos você tem certeza de como ela será exibida. Mas, de fato, a segunda sugestão não mostra o valor 0 se <0 como você mencionou.
Kevin VDF de
-2
    void NumericTextBoxInput(object sender, TextCompositionEventArgs e)
    {
        TextBox txt = (TextBox)sender;
        var regex = new Regex(@"^[0-9]*(?:\.[0-9]{0,1})?$");
        string str = txt.Text + e.Text.ToString();
        int cntPrc = 0;
        if (str.Contains('.'))
        {
            string[] tokens = str.Split('.');
            if (tokens.Count() > 0)
            {
                string result = tokens[1];
                char[] prc = result.ToCharArray();
                cntPrc = prc.Count();
            }
        }
        if (regex.IsMatch(e.Text) && !(e.Text == "." && ((TextBox)sender).Text.Contains(e.Text)) && (cntPrc < 3))
        {
            e.Handled = false;
        }
        else
        {
            e.Handled = true;
        }
    }
Monali Pawar
fonte
9
Algumas explicações melhorariam significativamente a qualidade de sua resposta.
domingo