Как отформатировать количество десятичных знаков в wpf с помощью стиля / шаблона?

94

Я пишу программу WPF и пытаюсь найти способ форматирования данных в TextBox с помощью некоторого повторяемого метода, такого как стиль или шаблон. У меня много текстовых полей (точнее 95), и каждый из них привязан к своим числовым данным, для каждого из которых может быть определено собственное разрешение. Например, если данные 99,123 с разрешением 2, тогда должно отображаться 99,12. Точно так же значение данных 99 и разрешение 3 должны отображаться как 99,000 (а не 99). Есть ли способ сделать это?

Изменить: я должен уточнить, на текущем экране, над которым я работаю, 95 текстовых полей, но я хочу, чтобы каждое текстовое поле на разных экранах в моей программе отображало правильное количество десятичных знаков. Теперь, когда я думаю об этом, некоторые из них - это TextBoxes (например, экран, над которым я сейчас работаю), а некоторые - DataGrids или ListViews, но если я смогу выяснить, как заставить его работать для TextBoxes, я уверен, что смогу понять это также для других элементов управления.

В этом случае не так много кода, чтобы поделиться, но я постараюсь сделать его более понятным:

У меня есть модель представления, которая содержит следующие свойства (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

и в XAML у меня есть:

<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}" />

РЕДАКТИРОВАТЬ2 (мое решение): Оказалось, что, отойдя на некоторое время от компьютера, я вернулся, чтобы найти простой ответ, который смотрел мне в лицо. Отформатируйте данные в модели представления!

    Public ReadOnly Property Value As String
        Get
            Return (Strings.FormatNumber(Math.Round(_signal.DisplayValue, _signal.DisplayResolution), _signal.DisplayResolution))
        End Get
    End Property
AXG1010
источник
1
использовать IValueConverter? Передайте преобразователю фактическое значение и разрешение, и пусть он сам сделает округление за вас. Трудно предложить, StringFormatне зная, как именно TextBoxгенерируются эти 95 .
Вив
Разместите текущий код и XAML. В противном случае это все домыслы и бесполезные догадки.
Федерико Берасатеги
Я добавил дополнительную информацию к вопросу, которая, надеюсь, сделает его более ясным.
AXG1010

Ответы:

197

Вы должны использовать StringFormatна Binding. Вы можете использовать как стандартные строковые форматы , так и настраиваемые строковые форматы :

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

Обратите внимание, что метод StringFormatработает только в том случае, если целевое свойство имеет строковый тип. Если вы пытаетесь установить что-то вроде Contentсвойства ( typeof(object)), вам нужно будет использовать пользовательский StringFormatConverter( например, здесь ) и передать строку формата как ConverterParameter.

Изменить для обновленного вопроса

Итак, если вы ViewModelопределяете точность, я бы рекомендовал сделать это как MultiBindingи создать свой собственный IMultiValueConverter. На практике это довольно раздражает, переходить от простой привязки к привязке, которую нужно расширить до a MultiBinding, но если точность неизвестна во время компиляции, это почти все, что вы можете сделать. Вам IMultiValueConverterнужно будет взять значение и точность и вывести отформатированную строку. Вы сможете сделать это, используя String.Format.

Однако для таких вещей, как a ContentControl, это гораздо проще сделать с помощью Style:

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

ContentStringFormatТаким образом можно использовать любой элемент управления, который предоставляет объект . К сожалению, TextBoxничего подобного нет.

Абэ Хайдебрехт
источник
6
Пример с StringFormat, установленным на #,#.00, не компилируется - запятая интерпретируется как разделитель атрибутов в Bindingрасширении разметки.
Gigi
@Gigi, вы правы, но вы можете легко использовать его как так: StringFormat={}{0:#,#.00}. Я обновлю ответ, чтобы он работал правильно.
Abe Heidebrecht
StringFormat = N {0} отлично работает. Для точности 2 я хотел бы, чтобы отображалось два десятичных знака, за исключением «10,00», и в этом случае я хочу, чтобы отображалось «10». Есть ли способ сделать это при привязке к точности? Похоже, мне придется использовать конвертер.
Gordon Slysz 06
Я не думаю, что есть способ изменить десятичные дроби, представленные с использованием строковых форматов .NET, поэтому вам, вероятно, лучше написать конвертер.
Abe Heidebrecht
Можете ли вы объяснить, почему использовать два спецификатора 0: #, #. 00 недостаточно, чтобы одного из них было недостаточно?
Лэй Ян
7

Принятый ответ не показывает 0 в целочисленном разряде при вводе типа 0,299. Он показывает .3 в пользовательском интерфейсе WPF. Итак, мое предложение использовать следующий строковый формат

<TextBox Text="{Binding Value,  StringFormat={}{0:#,0.0}}" 
Маниш Дубей
источник
Привет, ваше решение в порядке, но я бы предпочел использовать ключевое слово N1 (2,3 ...), потому что оно позволяет избежать опечаток и, по крайней мере, вы уверены, как оно будет отображаться. Но на самом деле второе предложение не показывает 0, если значение <0, как вы упомянули.
Кевин 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
Некоторые объяснения значительно улучшат качество вашего ответа.
mrun 05