WPF中的数值数据输入
我们如何在WPF应用程序中处理数值输入?
没有NumericUpDown控件,我一直在使用TextBox并使用下面的代码处理其PreviewKeyDown事件,但这非常丑陋。
有没有人找到一种更优雅的方式来从用户那里获取数字数据,而不依赖于第三方控件?
private void NumericEditPreviewKeyDown(object sender, KeyEventArgs e) { bool isNumPadNumeric = (e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9) || e.Key == Key.Decimal; bool isNumeric = (e.Key >= Key.D0 && e.Key <= Key.D9) || e.Key == Key.OemPeriod; if ((isNumeric || isNumPadNumeric) && Keyboard.Modifiers != ModifierKeys.None) { e.Handled = true; return; } bool isControl = ((Keyboard.Modifiers != ModifierKeys.None && Keyboard.Modifiers != ModifierKeys.Shift) || e.Key == Key.Back || e.Key == Key.Delete || e.Key == Key.Insert || e.Key == Key.Down || e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up || e.Key == Key.Tab || e.Key == Key.PageDown || e.Key == Key.PageUp || e.Key == Key.Enter || e.Key == Key.Return || e.Key == Key.Escape || e.Key == Key.Home || e.Key == Key.End); e.Handled = !isControl && !isNumeric && !isNumPadNumeric; }
解决方案
回答
叫我疯了,但为什么不在TextBox控件的两边放置加号和减号按钮,而只是阻止TextBox接收光标焦点,从而创建我们自己的廉价NumericUpDown控件?
回答
怎么样:
protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e) { e.Handled = !AreAllValidNumericChars(e.Text); base.OnPreviewTextInput(e); } private bool AreAllValidNumericChars(string str) { foreach(char c in str) { if(!Char.IsNumber(c)) return false; } return true; }
回答
如果用户在使用数据之前提交数据,也可以尝试使用数据验证。我发现这样做比摆弄键相当简单和干净。
否则,我们也可以始终禁用粘贴!
回答
我一直在使用添加属性,以允许用户使用向上和向下键更改文本框中的值。要使用它,我们只需使用
<TextBox local:TextBoxNumbers.SingleDelta="1">100</TextBox>
这实际上并没有解决此问题中提到的验证问题,但是它解决了我对没有数字上/下控件的要求。稍微使用一下,我想实际上可能比旧的数字上/下控件更喜欢它。
该代码不是完美的,但是可以处理我需要处理的情况:
- 向上箭头,向下箭头
- Shift +向上箭头,Shift +向下箭头
- "向上翻页","向下翻页"
- 在text属性上绑定
Converter
后面的代码
using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; namespace Helpers { public class TextBoxNumbers { public static Decimal GetSingleDelta(DependencyObject obj) { return (Decimal)obj.GetValue(SingleDeltaProperty); } public static void SetSingleDelta(DependencyObject obj, Decimal value) { obj.SetValue(SingleDeltaProperty, value); } // Using a DependencyProperty as the backing store for SingleValue. This enables animation, styling, binding, etc... public static readonly DependencyProperty SingleDeltaProperty = DependencyProperty.RegisterAttached("SingleDelta", typeof(Decimal), typeof(TextBoxNumbers), new UIPropertyMetadata(0.0m, new PropertyChangedCallback(f))); public static void f(DependencyObject o, DependencyPropertyChangedEventArgs e) { TextBox t = o as TextBox; if (t == null) return; t.PreviewKeyDown += new System.Windows.Input.KeyEventHandler(t_PreviewKeyDown); } private static Decimal GetSingleValue(DependencyObject obj) { return GetSingleDelta(obj); } private static Decimal GetDoubleValue(DependencyObject obj) { return GetSingleValue(obj) * 10; } private static Decimal GetTripleValue(DependencyObject obj) { return GetSingleValue(obj) * 100; } static void t_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e) { TextBox t = sender as TextBox; Decimal i; if (t == null) return; if (!Decimal.TryParse(t.Text, out i)) return; switch (e.Key) { case System.Windows.Input.Key.Up: if (Keyboard.Modifiers == ModifierKeys.Shift) i += GetDoubleValue(t); else i += GetSingleValue(t); break; case System.Windows.Input.Key.Down: if (Keyboard.Modifiers == ModifierKeys.Shift) i -= GetDoubleValue(t); else i -= GetSingleValue(t); break; case System.Windows.Input.Key.PageUp: i += GetTripleValue(t); break; case System.Windows.Input.Key.PageDown: i -= GetTripleValue(t); break; default: return; } if (BindingOperations.IsDataBound(t, TextBox.TextProperty)) { try { Binding binding = BindingOperations.GetBinding(t, TextBox.TextProperty); t.Text = (string)binding.Converter.Convert(i, null, binding.ConverterParameter, binding.ConverterCulture); } catch { t.Text = i.ToString(); } } else t.Text = i.ToString(); } } }
回答
我们不仅可以使用以下内容吗?
int numericValue = 0; if (false == int.TryParse(yourInput, out numericValue)) { // handle non-numeric input }
回答
private void txtNumericValue_PreviewKeyDown(object sender, KeyEventArgs e) { KeyConverter converter = new KeyConverter(); string key = converter.ConvertToString(e.Key); if (key != null && key.Length == 1) { e.Handled = Char.IsDigit(key[0]) == false; } }
这是我发现的最简单的技术。不利的一面是TextBox的上下文菜单仍然允许通过粘贴进行非数字操作。为了快速解决此问题,我仅向文本框添加了属性/属性:ContextMenu =" {x:Null}"从而将其禁用。不理想,但是对于我的情况就足够了。
显然,我们可以在测试中添加更多键/字符,以包含其他可接受的值(例如'。','$'等...)
回答
这就是我的方法。它使用正则表达式来检查框中要显示的文本是否为数字。
Regex NumEx = new Regex(@"^-?\d*\.?\d*$"); private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e) { if (sender is TextBox) { string text = (sender as TextBox).Text + e.Text; e.Handled = !NumEx.IsMatch(text); } else throw new NotImplementedException("TextBox_PreviewTextInput Can only Handle TextBoxes"); }
现在,在WPF和Silverlight中有更好的方法可以做到这一点。如果控件绑定到属性,则只需更改绑定语句即可。使用以下内容进行绑定:
<TextBox Text="{Binding Number, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>
请注意,我们也可以在自定义属性上使用此属性,如果框中的值无效,并且控件将以红色边框突出显示,那么我们所要做的就是抛出异常。如果单击红色边框的右上角,则会弹出异常消息。
回答
Private Sub Value1TextBox_PreviewTextInput(ByVal sender As Object, ByVal e As TextCompositionEventArgs) Handles Value1TextBox.PreviewTextInput Try If Not IsNumeric(e.Text) Then e.Handled = True End If Catch ex As Exception End Try End Sub
为我工作。
回答
void PreviewTextInputHandler(object sender, TextCompositionEventArgs e) { string sVal = e.Text; int val = 0; if (sVal != null && sVal.Length > 0) { if (int.TryParse(sVal, out val)) { e.Handled = false; } else { e.Handled = true; } } }
回答
public class NumericTextBox : TextBox { public NumericTextBox() : base() { DataObject.AddPastingHandler(this, new DataObjectPastingEventHandler(CheckPasteFormat)); } private Boolean CheckFormat(string text) { short val; return Int16.TryParse(text, out val); } private void CheckPasteFormat(object sender, DataObjectPastingEventArgs e) { var isText = e.SourceDataObject.GetDataPresent(System.Windows.DataFormats.Text, true); if (isText) { var text = e.SourceDataObject.GetData(DataFormats.Text) as string; if (CheckFormat(text)) { return; } } e.CancelCommand(); } protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e) { if (!CheckFormat(e.Text)) { e.Handled = true; } else { base.OnPreviewTextInput(e); } } }
另外,我们可以通过提供适当的依赖项属性来自定义解析行为。
回答
将此添加到主要解决方案中,以确保在清除文本框时将绑定更新为零。
protected override void OnPreviewKeyUp(System.Windows.Input.KeyEventArgs e) { base.OnPreviewKeyUp(e); if (BindingOperations.IsDataBound(this, TextBox.TextProperty)) { if (this.Text.Length == 0) { this.SetValue(TextBox.TextProperty, "0"); this.SelectAll(); } } }
回答
我决定使用LINQ表达式将此处标记为答案的答复简化为基本上两行。
e.Handled = !e.Text.All(Char.IsNumber); base.OnPreviewTextInput(e);