Jak sformatować liczbę miejsc dziesiętnych w WPF za pomocą stylu / szablonu?


95

Piszę program WPF i próbuję wymyślić sposób formatowania danych w TextBox za pomocą powtarzalnej metody, takiej jak styl lub szablon. Mam wiele pól tekstowych (dokładnie 95) i każdy z nich jest powiązany z własnymi danymi liczbowymi, z których każdy może mieć zdefiniowaną własną rozdzielczość. Na przykład, jeśli dane to 99,123 z rozdzielczością 2, to powinno wyświetlić 99,12. Podobnie wartość danych 99 i rozdzielczość 3 powinny być wyświetlane jako 99.000 (nie 99). Czy jest na to sposób?

Edycja: Powinienem wyjaśnić, na bieżącym ekranie, nad którym pracuję, jest 95 pól tekstowych, ale chcę, aby każdy TextBox na różnych ekranach w moim programie wyświetlał prawidłową liczbę miejsc dziesiętnych. Teraz, gdy o tym myślę, niektóre z nich to TextBoxes (tak jak ekran, nad którym teraz pracuję), a niektóre to DataGrids lub ListViews, ale jeśli mogę dowiedzieć się, jak to działa dla TextBox, jestem pewien, że mogę to zrozumieć to również dla innych kontrolek.

W tym przypadku nie ma zbyt wiele kodu do udostępnienia, ale spróbuję to wyjaśnić:

Mam model widoku, który zawiera następujące właściwości (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

aw XAML mam:

<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 (moje rozwiązanie): Okazuje się, że po pewnym odejściu od komputera wróciłem, aby znaleźć prostą odpowiedź, która patrzyła mi w twarz. Sformatuj dane w modelu widoku!

    Public ReadOnly Property Value As String
        Get
            Return (Strings.FormatNumber(Math.Round(_signal.DisplayValue, _signal.DisplayResolution), _signal.DisplayResolution))
        End Get
    End Property

1
użyj IValueConverter? Przekaż rzeczywistą wartość i rozdzielczość do konwertera i pozwól mu wykonać zaokrąglenie w sobie. Trudno jest zasugerować, StringFormatnie wiedząc, jak dokładnie te 95 TextBoxsą generowane.
— Viv

Opublikuj bieżący kod i XAML. W przeciwnym razie to wszystko spekulacje i niepomocne domysły.
— Federico Berasategui

Dodałem więcej informacji do pytania, które, mam nadzieję, powinny uczynić je jaśniejszym.
— AXG1010,

Odpowiedzi:


202

Powinieneś użyć StringFormatna Binding. Możesz użyć standardowych formatów ciągów lub niestandardowych formatów ciągów :

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

Zauważ, że StringFormatdziała tylko wtedy, gdy właściwość target jest typu string. Jeśli próbujesz ustawić coś takiego jak Contentwłaściwość ( typeof(object)), będziesz musiał użyć niestandardowego StringFormatConverter( takiego jak tutaj ) i przekazać swój ciąg formatu jako ConverterParameter.

Edytuj, aby zaktualizować pytanie

Tak więc, jeśli ViewModeldefiniujesz precyzję, polecam zrobienie tego jako MultiBindingi utworzenie własnego IMultiValueConverter. W praktyce jest to dość denerwujące, przechodzenie od prostego wiązania do takiego, które wymaga rozszerzenia do a MultiBinding, ale jeśli precyzja nie jest znana w czasie kompilacji, to prawie wszystko, co możesz zrobić. Twój IMultiValueConvertermusiałby wziąć wartość i precyzję i wyprowadzić sformatowany ciąg. Byłbyś w stanie to zrobić za pomocą String.Format.

Jednak w przypadku rzeczy takich jak a ContentControlmożesz to znacznie łatwiej zrobić za pomocą Style:

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

Każdy element sterujący, który ujawnia a, ContentStringFormatmoże być używany w ten sposób. Niestety TextBoxnic takiego nie ma.


6
Przykład z ustawieniem StringFormat na #,#.00nie kompiluje się - przecinek jest interpretowany jako ogranicznik dla atrybutów w Bindingrozszerzeniu znaczników.
— Gigi

@Gigi, masz rację, ale łatwo można go używać tak: StringFormat={}{0:#,#.00}. Zaktualizuję odpowiedź, aby działała poprawnie.
— Abe Heidebrecht

„StringFormat = N {0}” działa świetnie. Dla dokładności 2 chciałbym pokazać dwa miejsca po przecinku, z wyjątkiem „10 .00”, w którym to przypadku chcę, aby było wyświetlane „10”. Czy istnieje sposób, aby to zrobić, wiążąc się z precyzją? Wygląda na to, że będę musiał użyć konwertera.
— Gordon Slysz

Myślę, że nie ma sposobu na zmianę liczb dziesiętnych prezentowanych przy użyciu formatów ciągów .NET, więc prawdopodobnie lepiej będzie napisać konwerter.
— Abe Heidebrecht

Czy możesz wyjaśnić, dlaczego użycie dwóch specyfikatorów 0: #, #. 00, czy nie wystarczy jeden z nich?
— Lei Yang,

8

Zaakceptowana odpowiedź nie pokazuje 0 w miejscu całkowitym po podaniu danych wejściowych typu 0,299. Pokazuje .3 w interfejsie użytkownika WPF. Więc moja sugestia, aby użyć następującego formatu ciągu

<TextBox Text="{Binding Value,  StringFormat={}{0:#,0.0}}" 

Cześć, Twoje rozwiązanie jest w porządku, ale wolałbym użyć słowa kluczowego N1 (2,3 ...), ponieważ pozwala uniknąć błędów literówek i przynajmniej masz pewność, jak zostanie wyświetlone. Ale rzeczywiście druga sugestia nie pokazuje 0, jeśli wartość <0, jak wspomniałeś.
— 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;
        }
    }

9
Niektóre wyjaśnienia znacznie poprawiłyby jakość Twojej odpowiedzi.
— mrun
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.