如何在不激怒用户的情况下格式化绑定到TextBox的小数?

epa*_*alm 52 wpf textbox decimal string-formatting

我正在尝试使用WPF中的数据绑定在TextBox中显示格式化的十进制数.

目标

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

目标2:当用户与TextBox交互(输入)时,不要惹恼他/她.

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

尝试

尝试1:没有格式化.

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

<TextBox Text="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" />
Run Code Online (Sandbox Code Playgroud)

违反目标1. SomeDecimal = 4.5将在TextBox中显示"4.50000".

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

<TextBox Text="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged, StringFormat=F2}" />
Run Code Online (Sandbox Code Playgroud)

违反目标2.说SomeDecimal是2.5,TextBox显示"2.50".如果我们选择all并输入"13.5",我们在TextBox中以"13.5.00"结束,因为格式化程序"有用"地插入小数和零.

尝试3:使用Extended WPF Toolkit的MaskedTextBox.

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

假设我正在正确阅读文档,掩码## 0.00表示"两个可选数字,后跟一个必需的数字,一个小数点,还有两个必需的数字.这迫使我说"可以进入的最大可能数字这个TextBox是999.99",但让我说我很好.

<xctk:MaskedTextBox Value="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" Mask="##0.00" />
Run Code Online (Sandbox Code Playgroud)

违反目标2. TextBox以___.__选择它并输入5.75产量开始575.__.获得5.75的唯一方法是选择TextBox并输入<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" />
Run Code Online (Sandbox Code Playgroud)

违反目标3.DecimalUpDown愉快地忽略UpdateSourceTrigger = PropertyChanged.Extended WPF Toolkit Codeplex页面上的协调员之一建议在http://wpftoolkit.codeplex.com/discussions/352551/上修改ControlTemplate .这符合目标3,但违反了目标2,表现出与尝试2中相同的行为.

尝试5:使用样式数据触发器,仅在用户未编辑时才使用格式.

使用TextBox上的StringFormat绑定到double

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

<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>
Run Code Online (Sandbox Code Playgroud)

抛开所有这些不舒服的事情......

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

尝试6:自己动手.

我将要通过继承TextBox或创建附加属性并自己编写格式化代码来开始痛苦和痛苦.它将充满字符串操作,并导致大量的hairloss.


有没有人有任何建议格式化绑定到满足上述所有目标的文本框的小数?

Vla*_*hov 5

尝试在ViewModel级别上解决此问题.它:

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));
        }
    }
Run Code Online (Sandbox Code Playgroud)

样品

XAML:

<TextBox x:Name="tb" Text="{Binding Path=SomeDecimalAsString, UpdateSourceTrigger=PropertyChanged}" />
Run Code Online (Sandbox Code Playgroud)

代码背后:

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;
}
Run Code Online (Sandbox Code Playgroud)


Xca*_*r37 1

尝试使用 WPF Extended Tookit Masked TextBox 来实现输入掩码: http://wpftoolkit.codeplex.com/wikipage? title=MaskedTextBox

例子:

<toolkit:MaskedTextBox Mask="(000) 000-0000" Value="(555) 123-4567" 
IncludeLiterals="True" />
Run Code Online (Sandbox Code Playgroud)