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.