如何使用样式/模板格式化wpf中的小数位数?

时间:2021-11-03 16:44:45

I am writing a WPF program and I am trying to figure out a way to format data in a TextBox through some repeatable method like a style or template. I have a lot of TextBoxes (95 to be exact) and each one is bound to its own numeric data which can each have their own resolution defined. For example if the data is 99.123 with a resolution of 2 then it should display 99.12. Similarly a data value of 99 and resolution 3 should be displayed as 99.000 (not 99). Is there a way to do this?

我正在编写一个WPF程序,我正在试图找到一种方法,通过样式或模板等可重复的方法来格式化文本框中的数据。我有很多文本框(确切地说,是95个),每个文本框都绑定到自己的数字数据上,这些数据可以各自定义自己的分辨率。例如,如果数据是99.123,分辨率为2,那么它应该显示99.12。同样,99和3的数据值应该显示为99.000(而不是99)。有没有办法做到这一点?

Edit: I should clarify, there are 95 TextBoxes on the current screen I'm working on, but I want every TextBox throughout the various screens in my program to display the correct number of decimal places. Now that I think about it, some of these are TextBoxes (like the screen I'm working on now) and some are DataGrids or ListViews, but if I can figure out how to get it working for TextBoxes I'm sure I can figure it out for the other controls as well.

编辑:我要澄清一下,我正在处理的当前屏幕上有95个文本框,但我希望程序中各个屏幕上的每个文本框都显示正确的小数位数。现在我想一下,其中有些是文本框(比如我现在正在做的屏幕),有些是DataGrids或ListViews,但如果我能弄清楚如何让它在文本框中工作,我相信我也能在其他控件中找到它。

There's not much code to share in this case but I'll attempt to make it clearer:

在这种情况下没有多少代码可以共享,但我将尝试使其更清晰:

I have a View Model which contains the following properties (vb.net):

我有一个视图模型,它包含以下属性(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

and in the XAML I have:

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

EDIT2 (my solution): It turns out that after walking away from the computer for a while, I came back to find a simple answer that was staring me in the face. Format the data in the view model!

EDIT2(我的解决方案):事实证明,在离开电脑一段时间后,我回来了,发现一个简单的答案正盯着我的脸。在视图模型中格式化数据!

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

3 个解决方案

#1


134  

You should use the StringFormat on the Binding. You can use either standard string formats, or custom string formats:

您应该在绑定上使用StringFormat。您可以使用标准的字符串格式,也可以使用自定义的字符串格式:

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

Note that the StringFormat only works when the target property is of type string. If you are trying to set something like a Content property (typeof(object)), you will need to use a custom StringFormatConverter (like here), and pass your format string as the ConverterParameter.

请注意,StringFormat只有在目标属性为string时才会起作用。如果您试图设置内容属性(typeof(object))之类的内容,您将需要使用自定义的StringFormatConverter(如这里所示),并将您的格式字符串作为ConverterParameter传递。

Edit for updated question

编辑更新问题

So, if your ViewModel defines the precision, I'd recommend doing this as a MultiBinding, and creating your own IMultiValueConverter. This is pretty annoying in practice, to go from a simple binding to one that needs to be expanded out to a MultiBinding, but if the precision isn't known at compile time, this is pretty much all you can do. Your IMultiValueConverter would need to take the value, and the precision, and output the formatted string. You'd be able to do this using String.Format.

因此,如果您的ViewModel定义了精度,我建议您将其作为一个多绑定,并创建您自己的IMultiValueConverter。这在实践中是相当烦人的,从一个简单的绑定到一个需要扩展到多绑定的绑定,但是如果在编译时不知道精度,这几乎是您所能做的所有事情。您的IMultiValueConverter需要获取值和精度,并输出格式化的字符串。您可以使用String.Format实现此操作。

However, for things like a ContentControl, you can much more easily do this with a Style:

但是,对于像ContentControl这样的东西,您可以更容易地使用样式来实现:

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

Any control that exposes a ContentStringFormat can be used like this. Unfortunately, TextBox doesn't have anything like that.

任何公开ContentStringFormat的控件都可以这样使用。不幸的是,TextBox并没有类似的东西。

#2


1  

The accepted answer does not show 0 in integer place on giving input like 0.299. It shows .3 in WPF UI. So my suggestion to use following string format

接受的答案不显示整数位置上的0,比如0.299。它显示了WPF UI中的。3。所以我建议使用以下字符串格式。

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

#3


-1  

    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;
        }
    }

#1


134  

You should use the StringFormat on the Binding. You can use either standard string formats, or custom string formats:

您应该在绑定上使用StringFormat。您可以使用标准的字符串格式,也可以使用自定义的字符串格式:

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

Note that the StringFormat only works when the target property is of type string. If you are trying to set something like a Content property (typeof(object)), you will need to use a custom StringFormatConverter (like here), and pass your format string as the ConverterParameter.

请注意,StringFormat只有在目标属性为string时才会起作用。如果您试图设置内容属性(typeof(object))之类的内容,您将需要使用自定义的StringFormatConverter(如这里所示),并将您的格式字符串作为ConverterParameter传递。

Edit for updated question

编辑更新问题

So, if your ViewModel defines the precision, I'd recommend doing this as a MultiBinding, and creating your own IMultiValueConverter. This is pretty annoying in practice, to go from a simple binding to one that needs to be expanded out to a MultiBinding, but if the precision isn't known at compile time, this is pretty much all you can do. Your IMultiValueConverter would need to take the value, and the precision, and output the formatted string. You'd be able to do this using String.Format.

因此,如果您的ViewModel定义了精度,我建议您将其作为一个多绑定,并创建您自己的IMultiValueConverter。这在实践中是相当烦人的,从一个简单的绑定到一个需要扩展到多绑定的绑定,但是如果在编译时不知道精度,这几乎是您所能做的所有事情。您的IMultiValueConverter需要获取值和精度,并输出格式化的字符串。您可以使用String.Format实现此操作。

However, for things like a ContentControl, you can much more easily do this with a Style:

但是,对于像ContentControl这样的东西,您可以更容易地使用样式来实现:

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

Any control that exposes a ContentStringFormat can be used like this. Unfortunately, TextBox doesn't have anything like that.

任何公开ContentStringFormat的控件都可以这样使用。不幸的是,TextBox并没有类似的东西。

#2


1  

The accepted answer does not show 0 in integer place on giving input like 0.299. It shows .3 in WPF UI. So my suggestion to use following string format

接受的答案不显示整数位置上的0,比如0.299。它显示了WPF UI中的。3。所以我建议使用以下字符串格式。

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

#3


-1  

    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;
        }
    }