如何在不激怒用户的情况下格式化绑定到TextBox的小数? [英] How can I format a decimal bound to TextBox without angering my users?

查看:75
本文介绍了如何在不激怒用户的情况下格式化绑定到TextBox的小数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用WPF中的数据绑定在TextBox中显示带格式的小数。

I'm trying to display a formatted decimal in a TextBox using data binding in WPF.

目标1:设置a代码中的小数属性,在TextBox中显示2个小数位。

Goal 1: When setting a decimal property in code, display 2 decimal places in the TextBox.

目标2:当用户与TextBox(键入)交互时,不要生气。

Goal 2: When a user interacts with (types in) the TextBox, don't piss him/her off.

目标3:绑定必须更新PropertyChanged上的源。

Goal 3: Bindings must update source on PropertyChanged.

尝试1 :无格式。

在这里,我们几乎是从头开始。

Here we're starting nearly from scratch.

<TextBox Text="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" />

违反目标1。 SomeDecimal = 4.5 将显示 4.50000 "

Violates Goal 1. SomeDecimal = 4.5 will show "4.50000" in the TextBox.

尝试2:在绑定中使用StringFormat。

<TextBox Text="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged, StringFormat=F2}" />

违反目标2。假设SomeDecimal为2.5,并且文本框显示为 2.50。如果我们全选并输入 13.5,我们最终得到 13.5.00因为格式化程序有帮助插入小数点和零。

Violates Goal 2. Say SomeDecimal is 2.5, and the TextBox is displaying "2.50". If we select all and type "13.5" we end up with "13.5.00" in the TextBox because the formatter "helpfully" inserts decimals and zeros.

尝试3:使用扩展WPF工具包的MaskedTextBox。

http://wpftoolkit.codeplex.com/wikipage?title=MaskedTextBox

假设我正确地阅读了文档,掩码## 0.00表示两个可选数字,后跟一个必需的数字,一个小数点和另外两个必需的数字。这迫使我说该文本框可以输入的最大数字是999.99。

Assuming I'm reading the documentation correctly, the mask ##0.00 means "two optional digits, followed by a required digit, a decimal point, and two more required digits. This forces me to say "the largest possible number that can go into this TextBox is 999.99" but let's say I'm ok with that.

<xctk:MaskedTextBox Value="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" Mask="##0.00" />

违反目标2。文本框以 ___.__ 开头,选择它并输入5.75会产生 575 .__ 。获得5.75的唯一方法是选择文本框,然后键入< space>< space> 5.75

Violates Goal 2. The TextBox starts with ___.__ and selecting it and typing 5.75 yields 575.__. The only way to get 5.75 is to select the TextBox and type <space><space>5.75.

尝试4:使用Extended WPF Toolkit的DecimalUpDown微调器。

http://wpftoolkit.codeplex.com/wikipage?title=DecimalUpDown

<xctk:DecimalUpDown Value="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" FormatString="F2" />

违反目标3。DecimalUpDown愉快地忽略了UpdateSourceTrigger = PropertyChanged。 扩展的WPF工具包Codeplex页上的一位协调员建议在 http://wpftoolkit.codeplex上使用经过修改的ControlTemplate。 com / discussions / 352551 / 。满足目标3,但违反目标2,表现出与尝试2相同的行为。

Violates Goal 3. DecimalUpDown happily ignores UpdateSourceTrigger=PropertyChanged. One of the Coordinators on the Extended WPF Toolkit Codeplex page suggests a modified ControlTemplate at http://wpftoolkit.codeplex.com/discussions/352551/. This satisfies Goal 3, but violates Goal 2, exhibiting the same behavior as in Attempt 2.

尝试5:使用样式数据触发器,仅当用户不使用格式时才使用格式编辑。

在TextBox上使用StringFormat绑定到双精度 a>

即使这个目标满足了所有三个目标,我也不想使用它。 (A)文本框每个变成12行而不是1行,我的应用程序包含很多文本框。 (B)我所有的文本框都已经有一个Style属性,该属性指向设置了Margin,Height和其他内容的全局StaticResource。 (C)您可能已经注意到下面的代码两次设置了绑定路径,这违反了DRY主体。

Even if this one satisfied all three goals, I wouldn't want to use it. (A) Textboxes become 12 lines each instead of 1, and my application contains many, many textboxes. (B) All my textboxes already have a Style attribute which points to a global StaticResource which sets Margin, Height, and other things. (C) You may have noticed the code below sets the binding Path twice, which violates the DRY principal.

<TextBox>
    <TextBox.Style>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Text" Value="{Binding Path=SomeDecimal, StringFormat=F2}" />
            <Style.Triggers>
                <Trigger Property="IsFocused" Value="True">
                    <Setter Property="Text" Value="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

所有这些令人不舒服的事情……

All these uncomfortable things aside...

违反了目标2。首先,单击显示 13.50的文本框。突然使它显示 13.5000,这是意外的。第二,如果以空白的文本框开头,并且键入 13.50,则文本框将包含 1350。我无法解释原因,但是如果光标位于TextBox中字符串的右端,则按句点键不会插入小数。如果TextBox包含长度为> 0,然后将光标重新定位到字符串右端以外的任何位置,然后可以插入小数点。

Violates Goal 2. First, clicking on a TextBox which displays "13.50" suddenly makes it display "13.5000", which is unexpected. Second, if starting with a blank TextBox, and I type "13.50", the TextBox will contain "1350". I can't explain why, but pressing the period key doesn't insert decimals if the cursor is at the right end of the string in the TextBox. If the TextBox contains a string with length > 0, and I reposition the cursor to anywhere except the right end of the string, I can then insert decimal points.

尝试6:自己动手。

Attempt 6: Do it myself.

我将通过继承TextBox或创建附加属性,然后自己编写格式代码来承受痛苦和磨难。

I'm about to embark on a jouney of pain and suffering, by either subclassing TextBox, or creating an attached property, and writing the formatting code myself. It will be full of string manipulation, and cause substantial hairloss.

有人对格式化满足上述所有目标的文本框的十进制格式有任何建议吗?

Does anyone have any suggestions for formatting decimals bound to textboxes that satisfies all the above goals?

推荐答案

尝试在ViewModel级别解决该问题。就是这样:

Try to resolve that on ViewModel level. That it:

public class FormattedDecimalViewModel : INotifyPropertyChanged
    {
        private readonly string _format;

        public FormattedDecimalViewModel()
            : this("F2")
        {

        }

        public FormattedDecimalViewModel(string format)
        {
            _format = format;
        }

        private string _someDecimalAsString;
        // String value that will be displayed on the view.
        // Bind this property to your control
        public string SomeDecimalAsString
        {
            get
            {
                return _someDecimalAsString;
            }
            set
            {
                _someDecimalAsString = value;
                RaisePropertyChanged("SomeDecimalAsString");
                RaisePropertyChanged("SomeDecimal");
            }
        }

        // Converts user input to decimal or initializes view model
        public decimal SomeDecimal
        {
            get
            {
                return decimal.Parse(_someDecimalAsString);
            }
            set
            {
                SomeDecimalAsString = value.ToString(_format);
            }
        }

        // Applies format forcibly
        public void ApplyFormat()
        {
            SomeDecimalAsString = SomeDecimal.ToString(_format);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

示例

Xaml:

<TextBox x:Name="tb" Text="{Binding Path=SomeDecimalAsString, UpdateSourceTrigger=PropertyChanged}" />

后面的代码:

public MainWindow()
{
    InitializeComponent();
    FormattedDecimalViewModel formattedDecimalViewModel = new FormattedDecimalViewModel { SomeDecimal = (decimal)2.50 };
    tb.LostFocus += (s, e) => formattedDecimalViewModel.ApplyFormat(); // when user finishes to type, will apply formatting
    DataContext = formattedDecimalViewModel;
}

这篇关于如何在不激怒用户的情况下格式化绑定到TextBox的小数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆