[英]WPF TextBox input filtering doesn't work as expected with a decimal
我試圖將輸入過濾到WPF TextBox
以防止用戶輸入非數字字符串。 我已經配置了PreviewKeyDown
並正在檢查使用此問題中的代碼輸入的字符,以將鍵控代碼轉換為字符。 一切正常,除非用戶輸入了句點。 該代碼確實檢測到輸入了一個句點,但是當我通過將KeyEventArgs
的Handled
設置為false從PreviewKeyDown
返回時,它不允許輸入句點。
XAML
<TextBox PreviewKeyDown="TextBox_PreviewKeyDown">
<TextBox.Text>
<Binding Source="{StaticResource SomeObject}" Path="SomePath" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:MyValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
C#
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
char character = GetCharFromKey(e.Key);
e.Handled = false;
if (character >= '0' && character <= '9')
return;
if (character == '.')
return;
switch(e.Key)
{
case Key.Delete:
case Key.Back:
return;
}
e.Handled = true;
}
您不能處理PreviewTextInput
事件嗎? 像這樣:
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
string str = ((TextBox)sender).Text + e.Text;
decimal i;
e.Handled = !decimal.TryParse(str, System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.CultureInfo.InvariantCulture, out i);
}
XAML:
<TextBox Text="{Binding SomePath}" PreviewTextInput="TextBox_PreviewTextInput" />
編輯:使用PropertyChanged
的UpdateSourceTrigger
的問題是字符串“ 5”。 被轉換為5M
,這就是您在TextBox
看到的值。 “ 5”。 字符串未存儲在某處。
您可以通過使用跟蹤最新已知字符串的轉換器來克服此問題:
public class DecimalToStringConverter : IValueConverter
{
private string _lastConvertedValue;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return _lastConvertedValue ?? value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string str = value?.ToString();
decimal d;
if (decimal.TryParse(str, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out d))
{
_lastConvertedValue = str;
return d;
}
_lastConvertedValue = null;
return Binding.DoNothing;
}
}
XAML:
<TextBox PreviewTextInput="TextBox_PreviewTextInput">
<TextBox.Text>
<Binding Path="SomePath" UpdateSourceTrigger="PropertyChanged">
<Binding.Converter>
<local:DecimalToStringConverter />
</Binding.Converter>
</Binding>
</TextBox.Text>
</TextBox>
我有一個用於此的行為,我認為它是基於我從網絡上獲得的東西。 您可以按原樣使用它,或者弄清楚為什么您的版本不起作用。 請注意,雖然代碼處理粘貼。
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace UILib
{
public class TextBoxDecimalRangeBehaviour : Behavior<TextBox>
{
public string EmptyValue { get; set; } = "0";
public double Minimum
{
get { return (double)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register("Minimum", typeof(double), typeof(TextBoxDecimalRangeBehaviour), new PropertyMetadata(0.0));
public double Maximum
{
get { return (double)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register("Maximum", typeof(double), typeof(TextBoxDecimalRangeBehaviour), new PropertyMetadata(10.0));
public int MaxInteger
{
get { return (int)GetValue(MaxIntegerProperty); }
set { SetValue(MaxIntegerProperty, value); }
}
public static readonly DependencyProperty MaxIntegerProperty =
DependencyProperty.Register("MaxInteger", typeof(int), typeof(TextBoxDecimalRangeBehaviour), new PropertyMetadata(1));
public int MaxDecimals
{
get { return (int)GetValue(MaxDecimalsProperty); }
set { SetValue(MaxDecimalsProperty, value); }
}
public static readonly DependencyProperty MaxDecimalsProperty =
DependencyProperty.Register("MaxDecimals", typeof(int), typeof(TextBoxDecimalRangeBehaviour), new PropertyMetadata(2));
/// <summary>
/// Attach our behaviour. Add event handlers
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewTextInput += PreviewTextInputHandler;
AssociatedObject.PreviewKeyDown += PreviewKeyDownHandler;
DataObject.AddPastingHandler(AssociatedObject, PastingHandler);
}
/// <summary>
/// Deattach our behaviour. remove event handlers
/// </summary>
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewTextInput -= PreviewTextInputHandler;
AssociatedObject.PreviewKeyDown -= PreviewKeyDownHandler;
DataObject.RemovePastingHandler(AssociatedObject, PastingHandler);
}
void PreviewTextInputHandler(object sender, TextCompositionEventArgs e)
{
string text;
if (this.AssociatedObject.Text.Length < this.AssociatedObject.CaretIndex)
text = this.AssociatedObject.Text;
else
{
// Remaining text after removing selected text.
string remainingTextAfterRemoveSelection;
text = TreatSelectedText(out remainingTextAfterRemoveSelection)
? remainingTextAfterRemoveSelection.Insert(AssociatedObject.SelectionStart, e.Text)
: AssociatedObject.Text.Insert(this.AssociatedObject.CaretIndex, e.Text);
}
e.Handled = !ValidateText(text);
}
/// <summary>
/// PreviewKeyDown event handler
/// </summary>
void PreviewKeyDownHandler(object sender, KeyEventArgs e)
{
if (string.IsNullOrEmpty(this.EmptyValue))
{
return;
}
string text = null;
// Handle the Backspace key
if (e.Key == Key.Back)
{
if (!this.TreatSelectedText(out text))
{
if (AssociatedObject.SelectionStart > 0)
text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart - 1, 1);
}
}
// Handle the Delete key
else if (e.Key == Key.Delete)
{
// If text was selected, delete it
if (!this.TreatSelectedText(out text) && this.AssociatedObject.Text.Length > AssociatedObject.SelectionStart)
{
// Otherwise delete next symbol
text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, 1);
}
}
if (text == string.Empty)
{
this.AssociatedObject.Text = this.EmptyValue;
if (e.Key == Key.Back)
AssociatedObject.SelectionStart++;
e.Handled = true;
}
}
private void PastingHandler(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(DataFormats.Text))
{
string text = Convert.ToString(e.DataObject.GetData(DataFormats.Text));
if (!ValidateText(text))
e.CancelCommand();
}
else
e.CancelCommand();
}
/// <summary>
/// Validate certain text by our regular expression and text length conditions
/// </summary>
/// <param name="text"> Text for validation </param>
/// <returns> True - valid, False - invalid </returns>
private bool ValidateText(string text)
{
double number;
if (!Double.TryParse(text, out number))
{
return false;
}
if(number < Minimum)
{
return false;
}
if (number > Maximum)
{
return false;
}
int dotPointer = text.IndexOf('.');
// No point entered so the decimals must be ok
if(dotPointer == -1)
{
return true;
}
if (dotPointer > MaxInteger)
{
return false;
}
if(text.Substring(dotPointer +1).Length > MaxDecimals)
{
return false;
}
return true;
}
/// <summary>
/// Handle text selection
/// </summary>
/// <returns>true if the character was successfully removed; otherwise, false. </returns>
private bool TreatSelectedText(out string text)
{
text = null;
if (AssociatedObject.SelectionLength <= 0)
return false;
var length = this.AssociatedObject.Text.Length;
if (AssociatedObject.SelectionStart >= length)
return true;
if (AssociatedObject.SelectionStart + AssociatedObject.SelectionLength >= length)
AssociatedObject.SelectionLength = length - AssociatedObject.SelectionStart;
text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, AssociatedObject.SelectionLength);
return true;
}
}
}
用法:
<TextBox Text="{Binding ......>
<i:Interaction.Behaviors>
<ui:TextBoxDecimalRangeBehaviour MaxDecimals="2"
MaxInteger="1"
Minimum="{StaticResource Zero}"
Maximum="{StaticResource Ten}" />
<ui:SelectAllTextBoxBehavior/>
</i:Interaction.Behaviors>
</TextBox>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.