我正在尝试使用WPF中的数据绑定在TextBox中显示格式化的十进制数。
目标1:在代码中设置小数属性时,在TextBox中显示2个小数位。
目标2:当用户与TextBox交互(输入)时,不要让他/她失望。
目标3:绑定必须更新PropertyChanged上的源。
尝试1:无格式化。
这里我们几乎从头开始。
<TextBox Text="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" />
违反目标1. SomeDecimal = 4.5
将在TextBox中显示“4.50000”。
尝试2:在绑定中使用StringFormat。
<TextBox Text="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged, StringFormat=F2}" />
违反目标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" />
违反目标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" />
违反目标3.DecimalUpDown很高兴忽略UpdateSourceTrigger = PropertyChanged。 Extended WPF Toolkit Codeplex页面上的协调员之一建议在http://wpftoolkit.codeplex.com/discussions/352551/处修改ControlTemplate。这符合目标3,但违反了目标2,表现出与尝试2中相同的行为。
尝试5:使用样式数据触发器,仅在用户未编辑时才使用格式化。
Binding to a double with StringFormat on a TextBox
即使这个目标满足所有三个目标,我也不想使用它。 (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>
所有这些令人不舒服的事情......
违反目标2.首先,单击显示“13.50”的TextBox突然显示“13.5000”,这是意外的。其次,如果以空白TextBox开头,并输入“13.50”,则TextBox将包含“1350”。我无法解释原因,但如果光标位于TextBox中字符串的右端,则按句点键不会插入小数。如果TextBox包含长度为&gt;的字符串。 0,我将光标重新定位到除了字符串右端之外的任何地方,然后我可以插入小数点。
尝试6:自己动手。
我即将通过继承TextBox或创建附加属性并自己编写格式化代码来开始痛苦和痛苦。它将充满字符串操作,并导致大量的hairloss。
是否有人建议格式化绑定到满足上述所有目标的文本框的小数?
答案 0 :(得分: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));
}
}
<强>样品强>
的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;
}
答案 1 :(得分:2)
我创建了以下自定义行为,以便在使用StringFormat={}{0:0.00}
时将用户光标移动到小数点后面,这会强制出现小数位,但这可能会导致以下问题:
违反目标2.说SomeDecimal是2.5,TextBox显示“2.50”。如果我们选择all并键入“13.5”,我们在TextBox中以“13.5.00”结束,因为格式化程序“有用”地插入小数和零。
我已经使用自定义行为攻击了这个行为,当用户按下时会将用户光标移动到小数位后面。键:
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>
答案 2 :(得分:1)
尝试使用WPF Extended Toolkit Maskedtextbox来实现输入掩码: http://wpftoolkit.codeplex.com/wikipage?title=MaskedTextBox
示例:
<toolkit:MaskedTextBox Mask="(000) 000-0000" Value="(555) 123-4567"
IncludeLiterals="True" />