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

110

我正在编写一个 WPF 程序,试图找到一种像样式或模板这样可重复的方法来格式化 TextBox 中的数据。我有许多 TextBoxes(确切地说是 95 个),每个都绑定到自己的数值数据,每个数据可以定义自己的分辨率。例如,如果数据为 99.123,分辨率为 2,则应显示为 99.12。同样,数据值为 99,分辨率为 3,则应显示为 99.000(而非 99)。有没有办法做到这一点?

编辑: 我应该澄清一下,在我正在工作的当前屏幕上有 95 个 TextBox,但我希望我的程序中各个屏幕上的每个 TextBox 都能显示正确的小数位数。现在我想起来了,其中一些是 TextBox(如我正在工作的屏幕),一些是 DataGrids 或 ListViews,但如果我能够弄清楚如何使 TextBox 工作,我相信我也能找出其他控件的解决方法。

在这种情况下,没有太多要分享的代码,但我会尽力让它更加清晰:

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

1
使用 IValueConverter 吗?将实际值和分辨率传递给转换器,让它在内部进行四舍五入。不知道这95个 TextBox 是如何生成的,很难建议一个合适的 StringFormat - Viv
请发布当前的代码和XAML。否则,所有的猜测都是没有帮助的揣测。 - Federico Berasategui
我在问题中添加了更多信息,希望这样可以更清晰明了。 - AXG1010
3个回答

237
你应该在绑定中使用StringFormat。 你可以使用标准字符串格式自定义字符串格式
<TextBox Text="{Binding Value, StringFormat=N2}" />
<TextBox Text="{Binding Value, StringFormat={}{0:#,#.00}}" />
注意,StringFormat 仅在目标属性的类型为字符串时才起作用。如果您要设置像Content属性(typeof(object))这样的内容,则需要使用自定义的StringFormatConverter如此),并将格式字符串作为ConverterParameter传递。
对于更新后的问题,如果您的ViewModel定义了精度,我建议您使用MultiBinding并创建自己的IMultiValueConverter来实现。实际上,从简单绑定到需要扩展为MultiBinding可能会很烦人,但如果编译时不知道精度,这几乎是您唯一能做的事情。您的IMultiValueConverter需要获取值和精度,并输出格式化的字符串。您可以使用String.Format完成此操作。
但是,对于像ContentControl这样的控件,您可以更轻松地使用Style实现此目的:
<Style TargetType="{x:Type ContentControl}">
    <Setter Property="ContentStringFormat" 
            Value="{Binding Resolution, StringFormat=N{0}}" />
</Style>

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


7
StringFormat设置为#,#.00的示例无法编译 - 逗号被解释为Binding标记扩展中属性的分隔符。 - Gigi
@Gigi,你说得对,但你可以轻松地这样使用它:StringFormat={}{0:#,#.00}。我会更新答案以使其正常工作。 - Abe Heidebrecht
“StringFormat=N{0}”非常好用。我想要显示两位小数,除了“10.00”之外,此时我只想显示“10”。当绑定精度时,有没有办法做到这一点?看起来我必须使用转换器。 - Gordon Slysz
我认为没有办法改变使用.NET字符串格式呈现的小数位数,所以最好编写一个转换器。 - Abe Heidebrecht
你能解释一下为什么要使用两个格式说明符0:#,#.00,一个不就足够了吗? - Lei Yang

12

被接受的答案在输入0.299等类似数字时,整数位没有显示0。在WPF UI中会显示.3。因此我的建议是使用以下字符串格式。

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

你好,你的解决方案还可以,但我更喜欢使用关键字N1(2,3...),因为它避免了打字错误,而且至少你知道它将如何显示。但确实第二个建议不会显示小于0的值的0,就像你提到的那样。 - 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;
        }
    }

11
解释会显著提高你的回答质量。 - mrun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接