Comment formater le nombre de décimales dans wpf en utilisant le style / modèle?

94

J'écris un programme WPF et j'essaie de trouver un moyen de formater les données dans une zone de texte via une méthode répétable comme un style ou un modèle. J'ai beaucoup de TextBoxes (95 pour être exact) et chacune est liée à ses propres données numériques qui peuvent chacune avoir leur propre résolution définie. Par exemple, si les données sont 99,123 avec une résolution de 2, elles doivent afficher 99,12. De même, une valeur de données de 99 et une résolution de 3 doivent être affichées sous la forme 99 000 (et non 99). Y a-t-il un moyen de faire cela?

Edit: Je devrais clarifier, il y a 95 TextBoxes sur l'écran actuel sur lequel je travaille, mais je veux que chaque TextBox des différents écrans de mon programme affiche le nombre correct de décimales. Maintenant que j'y pense, certains d'entre eux sont des TextBoxes (comme l'écran sur lequel je travaille maintenant) et d'autres sont des DataGrids ou ListViews, mais si je peux comprendre comment le faire fonctionner pour les TextBoxes, je suis sûr que je peux comprendre aussi pour les autres contrôles.

Il n'y a pas beaucoup de code à partager dans ce cas, mais je vais essayer de le clarifier:

J'ai un modèle de vue qui contient les propriétés suivantes (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

et dans le XAML j'ai:

<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 (ma solution): Il s'avère qu'après s'être éloigné de l'ordinateur pendant un moment, je suis revenu pour trouver une réponse simple qui me regardait en face. Formatez les données dans le modèle de vue!

    Public ReadOnly Property Value As String
        Get
            Return (Strings.FormatNumber(Math.Round(_signal.DisplayValue, _signal.DisplayResolution), _signal.DisplayResolution))
        End Get
    End Property
AXG1010
la source
1
utiliser un IValueConverter? Transmettez la valeur réelle et la résolution au convertisseur et laissez-le faire l'arrondi pour vous en lui-même. Il est difficile de suggérer un StringFormatsans savoir exactement comment ces 95 TextBoxsont générés.
Viv
Publiez le code actuel et XAML. Sinon, ce ne sont que des spéculations et des suppositions inutiles.
Federico Berasategui
J'ai ajouté quelques informations supplémentaires à la question qui devraient, espérons-le, la clarifier.
AXG1010

Réponses:

197

Vous devez utiliser le StringFormatsur le Binding. Vous pouvez utiliser des formats de chaîne standard ou des formats de chaîne personnalisés :

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

Notez que le StringFormatfonctionne uniquement lorsque la propriété cible est de type string. Si vous essayez de définir quelque chose comme une Contentpropriété ( typeof(object)), vous devrez utiliser une valeur personnalisée StringFormatConverter( comme ici ) et passer votre chaîne de format en tant que ConverterParameter.

Modifier pour la question mise à jour

Donc, si vous ViewModeldéfinissez la précision, je vous recommande de le faire en tant que MultiBinding, et de créer le vôtre IMultiValueConverter. C'est assez ennuyeux en pratique, de passer d'une simple liaison à une qui doit être étendue à une MultiBinding, mais si la précision n'est pas connue au moment de la compilation, c'est à peu près tout ce que vous pouvez faire. Vous IMultiValueConverterdevrez prendre la valeur et la précision et afficher la chaîne formatée. Vous pourrez le faire en utilisant String.Format.

Cependant, pour des choses comme a ContentControl, vous pouvez le faire beaucoup plus facilement avec un Style:

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

Tout contrôle qui expose un ContentStringFormatpeut être utilisé comme ceci. Malheureusement, TextBoxil n'y a rien de tel.

Abe Heidebrecht
la source
6
L'exemple avec StringFormat défini sur #,#.00ne compile pas - la virgule est interprétée comme un délimiteur pour les attributs dans l' Bindingextension de balisage.
Gigi le
@Gigi, vous avez raison, mais vous pouvez facilement l' utiliser comme ceci: StringFormat={}{0:#,#.00}. Je mettrai à jour la réponse pour qu'elle fonctionne correctement.
Abe Heidebrecht le
Le "StringFormat = N {0}" fonctionne très bien. Pour une précision de 2, j'aimerais afficher deux décimales, sauf pour «10 .00», auquel cas je veux que «10» s'affiche. Existe-t-il un moyen de le faire lors de la liaison à la précision? On dirait que je vais devoir utiliser un convertisseur.
Gordon Slysz
Je ne pense pas qu'il existe un moyen de modifier les décimales présentées à l'aide de formats de chaîne .NET, vous feriez donc probablement mieux d'écrire un convertisseur.
Abe Heidebrecht
Pouvez-vous expliquer pourquoi utiliser deux spécificateurs 0: #, #. 00, un seul d'entre eux ne suffira-t-il pas?
Lei Yang
7

La réponse acceptée n'affiche pas 0 à la place d'un entier en donnant une entrée comme 0,299. Il montre .3 dans WPF UI. Donc, ma suggestion d'utiliser le format de chaîne suivant

<TextBox Text="{Binding Value,  StringFormat={}{0:#,0.0}}" 
Manish Dubey
la source
Salut, votre solution est correcte mais j'aimerais plutôt utiliser le mot-clé N1 (2,3 ...) car cela évite les fautes de frappe et au moins vous êtes sûr de la façon dont il sera affiché. Mais en effet, la deuxième suggestion n'affiche pas le 0 si la valeur <0 comme vous le mentionnez.
Kevin VDF
-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
la source
9
Certaines explications amélioreraient considérablement la qualité de votre réponse.
mrun