Sam*_*Sam 16 wpf decimal data-entry
是否有任何体面的方法来获得绑定到十进制值的WPF控件?
当我将TextBox或DataGridTextColumn绑定到小数时,数据条目会耗费大量时间.
<TextBox Text="{Binding MyDecimal, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}"/>
Run Code Online (Sandbox Code Playgroud)
当我尝试在此TextBox中输入"0,5"时,我将得到"5".根本不可能输入"0,5"(除了输入1,5并用"0"替换"1").
当我使用StringFormat时,数据输入仍然很少:
<TextBox Text="{Binding MyDecimal, StringFormat=F1, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}"/>
Run Code Online (Sandbox Code Playgroud)
现在,当我尝试输入"0,5"时,我最终会得到"0,5,0",这仍然是错误的,但至少我可以删除尾随的"0"而没有太多问题.
尽管如此,使用WPF输入小数会吸引大量时间,因为此输入字段非常容易出现数据输入错误,这对于值来说尤其令人痛苦!
那么我应该用什么来为wpf中的十进制数据输入?或者微软不支持十进制数据?
bli*_*eis 20
我目前使用此行为进行数字和十进制输入:
public class TextBoxInputBehavior : Behavior<TextBox>
{
const NumberStyles validNumberStyles = NumberStyles.AllowDecimalPoint |
NumberStyles.AllowThousands |
NumberStyles.AllowLeadingSign;
public TextBoxInputBehavior()
{
this.InputMode = TextBoxInputMode.None;
this.JustPositivDecimalInput = false;
}
public TextBoxInputMode InputMode { get; set; }
public static readonly DependencyProperty JustPositivDecimalInputProperty =
DependencyProperty.Register("JustPositivDecimalInput", typeof(bool),
typeof(TextBoxInputBehavior), new FrameworkPropertyMetadata(false));
public bool JustPositivDecimalInput
{
get { return (bool)GetValue(JustPositivDecimalInputProperty); }
set { SetValue(JustPositivDecimalInputProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewTextInput += AssociatedObjectPreviewTextInput;
AssociatedObject.PreviewKeyDown += AssociatedObjectPreviewKeyDown;
DataObject.AddPastingHandler(AssociatedObject, Pasting);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewTextInput -= AssociatedObjectPreviewTextInput;
AssociatedObject.PreviewKeyDown -= AssociatedObjectPreviewKeyDown;
DataObject.RemovePastingHandler(AssociatedObject, Pasting);
}
private void Pasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(string)))
{
var pastedText = (string)e.DataObject.GetData(typeof(string));
if (!this.IsValidInput(this.GetText(pastedText)))
{
System.Media.SystemSounds.Beep.Play();
e.CancelCommand();
}
}
else
{
System.Media.SystemSounds.Beep.Play();
e.CancelCommand();
}
}
private void AssociatedObjectPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
{
if (!this.IsValidInput(this.GetText(" ")))
{
System.Media.SystemSounds.Beep.Play();
e.Handled = true;
}
}
}
private void AssociatedObjectPreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (!this.IsValidInput(this.GetText(e.Text)))
{
System.Media.SystemSounds.Beep.Play();
e.Handled = true;
}
}
private string GetText(string input)
{
var txt = this.AssociatedObject;
int selectionStart = txt.SelectionStart;
if (txt.Text.Length < selectionStart)
selectionStart = txt.Text.Length;
int selectionLength = txt.SelectionLength;
if (txt.Text.Length < selectionStart + selectionLength)
selectionLength = txt.Text.Length - selectionStart;
var realtext = txt.Text.Remove(selectionStart, selectionLength);
int caretIndex = txt.CaretIndex;
if (realtext.Length < caretIndex)
caretIndex = realtext.Length;
var newtext = realtext.Insert(caretIndex, input);
return newtext;
}
private bool IsValidInput(string input)
{
switch (InputMode)
{
case TextBoxInputMode.None:
return true;
case TextBoxInputMode.DigitInput:
return CheckIsDigit(input);
case TextBoxInputMode.DecimalInput:
decimal d;
//wen mehr als ein Komma
if (input.ToCharArray().Where(x => x == ',').Count() > 1)
return false;
if (input.Contains("-"))
{
if (this.JustPositivDecimalInput)
return false;
if (input.IndexOf("-",StringComparison.Ordinal) > 0)
return false;
if(input.ToCharArray().Count(x=>x=='-') > 1)
return false;
//minus einmal am anfang zulässig
if (input.Length == 1)
return true;
}
var result = decimal.TryParse(input, validNumberStyles, CultureInfo.CurrentCulture, out d);
return result;
default: throw new ArgumentException("Unknown TextBoxInputMode");
}
return true;
}
private bool CheckIsDigit(string wert)
{
return wert.ToCharArray().All(Char.IsDigit);
}
}
public enum TextBoxInputMode
{
None,
DecimalInput,
DigitInput
}
Run Code Online (Sandbox Code Playgroud)
XAML用法如下所示:
<TextBox Text="{Binding Sum}">
<i:Interaction.Behaviors>
<Behaviors:TextBoxInputBehavior InputMode="DecimalInput"/>
</i:Interaction.Behaviors>
</TextBox>
Run Code Online (Sandbox Code Playgroud)
private void DecimalTextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
bool approvedDecimalPoint = false;
if (e.Text == ".")
{
if (!((TextBox)sender).Text.Contains("."))
approvedDecimalPoint = true;
}
if (!(char.IsDigit(e.Text, e.Text.Length - 1) || approvedDecimalPoint))
e.Handled = true;
}
Run Code Online (Sandbox Code Playgroud)
我也遇到过这个问题;与UpdateSourceTrigger=PropertyChanged似乎结合尝试为您键入它更新文本。为了解决这个问题,我们改变了我们的输入字段UpdateSourceTrigger=LostFocus,例如:
<TextBox Text="{Binding MyDecimal, UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True, StringFormat=n1}" />
Run Code Online (Sandbox Code Playgroud)
您可以使用该IDataErrorInfo接口定义自己的验证错误。您只需要将以下内容添加到您的支持模型中:
public class MyModel : IDataErrorInfo
{
/* my properties */
public string Error { get { return null; } }
public string this[string name]
{
get
{
switch (name)
{
case "MyDecimal":
return NumberHelper.IsValidValue(MyDecimal) ? message : null;
default: return null;
}
}
}
private string message = "Invalid value";
}
Run Code Online (Sandbox Code Playgroud)
从 .NET 4.5 开始,有一个更简单的修复,在绑定中添加“延迟”
<TextBox Text="{Binding MyDouble, UpdateSourceTrigger=PropertyChanged, Delay=1000}" />
Run Code Online (Sandbox Code Playgroud)
现在,用户在绑定系统尝试替换句点(将“1.”更改为“1”)之前有 1 秒(1000 毫秒)的时间。这应该让他们有时间在“.”之后输入其他字符。这样它就不会被删除。
| 归档时间: |
|
| 查看次数: |
35485 次 |
| 最近记录: |