我试图使用显示在数据绑定WPF在TextBox格式化小数。
目标
目标1:在码设置一个十进制属性,在TextBox显示器2位小数。
目标2:当用户与文本框(在类型)相互作用,不要激怒他/她。
目标3:绑定必须的PropertyChanged更新源。
尝试
尝试1:无格式。
在这里,我们将从头开始接近。
<TextBox Text="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" />
违反目标1. SomeDecimal = 4.5
会显示“4.50000”在文本框中。
尝试2:使用的StringFormat的约束力。
<TextBox Text="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged, StringFormat=F2}" />
违反目标2.说SomeDecimal是2.5,和文本框被显示“2.50”。 如果我们选择所有,输入“13.5”我们在TextBox结束了“00年5月13日”,因为格式化“有益”插入小数和零。
尝试3:使用扩展WPF工具包的MaskedTextBox中。
http://wpftoolkit.codeplex.com/wikipage?title=MaskedTextBox
假设我在正确的阅读文档,面膜## 0.00手段“两个可选的数字,后面所需的数字,一个小数点和两个需要的数字。这迫使我说”尽可能多的能够进入这个文本框是999.99" ,但让我们说,我很确定这一点。
<xctk:MaskedTextBox Value="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" Mask="##0.00" />
违反目标2.文本框开头___.__
和选择它并键入5.75产生575.__
。 获得5.75的唯一途径是选择文本框和键入<space><space>5.75
。
尝试4:使用扩展WPF工具包的DecimalUpDown微调。
http://wpftoolkit.codeplex.com/wikipage?title=DecimalUpDown
<xctk:DecimalUpDown Value="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" FormatString="F2" />
违反目标3. DecimalUpDown愉快地忽略UpdateSourceTrigger =的PropertyChanged。 一个扩展的WPF工具包Codeplex上页面上的协调员建议在修改的ControlTemplate http://wpftoolkit.codeplex.com/discussions/352551/ 。 这满足目标3,但违反目标2,表现出相同的行为在尝试2。
尝试5:使用样式datatriggers,仅使用,如果用户不编辑格式。
绑定到双用的StringFormat上一个TextBox
即使这样一个满足所有三个进球,我不希望使用它。 (A)文本框成为每个代替1- 12行,以及我的应用程序包含许多,许多文本框。 (B)我所有的文本框已经有一个指向一个全球性的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>
所有这些不舒服的事情放在一边......
违反目标2.首先,点击其显示“13.50”突然使得它显示“13.5000”文本框,这是出乎意料的。 其次,如果从空白文本框,然后我输入“13.50”,文本框将包含“1350”。 我无法解释为什么,但按句号键如果光标在文本框字符串的右端不插入小数。 如果文本框包含长度> 0的字符串,并且我重新定位光标到任意位置,除了字符串的右端,然后我可以插入小数点。
尝试6:做我自己。
我即将踏上痛苦的jouney,方法是子类文本框,或创建一个附加属性,并编写格式代码自己。 这将是充满字符串操作,并造成大量脱发。
有没有人有格式化必将满足所有上述目标的文本框小数有什么建议?
尝试解决在视图模型的水平。 它:
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;
}
我创建了下面的自定义行为来移动用户在使用时光标到小数点后StringFormat={}{0:0.00}
这迫使一个小数位是存在,但是这可能会导致以下问题:
违反目标2.说SomeDecimal是2.5,和文本框被显示“2.50”。 如果我们选择所有,输入“13.5”我们在TextBox结束了“00年5月13日”,因为格式化“有益”插入小数和零。
我一直在使用,将当他们按小数点后的用户将光标移动到一个自定义的行为,解决这个砍死。 键:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace GUI.Helpers.Behaviors
{
public class DecimalPlaceHotkeyBehavior : Behavior<TextBox>
{
#region Methods
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewKeyDown += AssociatedObject_PreviewKeyDown;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewKeyDown -= AssociatedObject_PreviewKeyDown;
}
protected override Freezable CreateInstanceCore()
{
return new DecimalPlaceHotkeyBehavior();
}
#endregion
#region Event Methods
private void AssociatedObject_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.OemPeriod || e.Key == System.Windows.Input.Key.Decimal)
{
var periodIndex = AssociatedObject.Text.IndexOf('.');
if (periodIndex != -1)
{
AssociatedObject.CaretIndex = (periodIndex + 1);
e.Handled = true;
}
}
}
#endregion
#region Initialization
public DecimalPlaceHotkeyBehavior()
: base()
{
}
#endregion
}
}
我使用它,如下所示:
<TextBox xmlns:Behaviors="clr-namespace:GUI.Helpers.Behaviors"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, StringFormat={}{0:0.00}}">
<i:Interaction.Behaviors>
<Behaviors:DecimalPlaceHotkeyBehavior></Behaviors:DecimalPlaceHotkeyBehavior>
</i:Interaction.Behaviors>
</TextBox>
尝试扩展的无线工具包屏蔽文本框的WPF来实现输入掩码: http://wpftoolkit.codeplex.com/wikipage?title=MaskedTextBox
例:
<toolkit:MaskedTextBox Mask="(000) 000-0000" Value="(555) 123-4567"
IncludeLiterals="True" />